home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / trialva / ibmcppw / sdk / mapi / win16 / dev / common / dt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  44.5 KB  |  1,441 lines

  1. /*
  2.  *  DT.C
  3.  *
  4.  *  Win32 date and time support for Win16.
  5.  *  Most borrowed from the Win32 library.
  6.  *  Some implemented specifically for MAPI.
  7.  *
  8.  *  Copyright 1993-1995 Microsoft Corporation. All Rights Reserved.
  9.  */
  10.  
  11. #pragma warning(disable:4001)   /* single line comments */
  12. #pragma warning(disable:4054)   /* cast function pointer to data pointer */
  13. #pragma warning(disable:4100)   /* unreferenced formal parameter */
  14. #pragma warning(disable:4127)   /* conditional expression is constant */
  15. #pragma warning(disable:4201)   /* nameless struct/union */
  16. #pragma warning(disable:4204)   /* non-constant aggregate initializer */
  17. #pragma warning(disable:4209)   /* benign typedef redefinition */
  18. #pragma warning(disable:4214)   /* bit field types other than int */
  19. #pragma warning(disable:4505)   /* unreferenced local function removed */
  20. #pragma warning(disable:4514)   /* unreferenced inline function removed */
  21. #pragma warning(disable:4702)   /* unreachable code */
  22. #pragma warning(disable:4704)   /* inline assembler turns off global optimizer */
  23. #pragma warning(disable:4705)   /* statement has no effect */
  24. #pragma warning(disable:4706)   /* assignment within conditional expression */
  25. #pragma warning(disable:4710)   /* function not expanded */
  26. #pragma warning(disable:4115)   /* named type def in parens */
  27.  
  28. #include <windows.h>
  29. #pragma warning(disable:4001)   /* single line comments */
  30. #include <windowsx.h>
  31. #include <mapiwin.h>
  32. #include <compobj.h>
  33. #include <mapidbg.h>
  34. #include <mapidefs.h>
  35. #include <mapiutil.h>
  36. #include <mapiperf.h>
  37.  
  38. #include <_mapiwin.h>
  39. #include <memory.h>
  40. #include <_memcpy.h>
  41.  
  42. #if defined(WIN16)
  43.  
  44. #pragma SEGMENT(MAPI_Conv)
  45.  
  46. #pragma warning (disable: 4704)
  47.  
  48. BOOL FValidBias(LONG lBias);
  49. BOOL FValidSysTime(SYSTEMTIME FAR *pst, BOOL fRelativeOK);
  50. BOOL FValidTimeZoneInformation(TIME_ZONE_INFORMATION FAR *ptz);
  51.  
  52. /*** From TIME.C */
  53.  
  54. /*  The following two tables map a day offset within a year to the month */
  55. /*  containing the day.  Both tables are zero based.  For example, day */
  56. /*  offset of 0 to 30 map to 0 (which is Jan). */
  57.  
  58. UCHAR LeapYearDayToMonth[366] = {
  59.      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* January */
  60.      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        /* February */
  61.      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  /* March */
  62.      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,     /* April */
  63.      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,  /* May */
  64.      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,     /* June */
  65.      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,  /* July */
  66.      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,  /* August */
  67.      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     /* September */
  68.      9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,  /* October */
  69.     10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,     /* November */
  70.     11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11}; /* December */
  71.  
  72. UCHAR NormalYearDayToMonth[365] = {
  73.      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* January */
  74.      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,           /* February */
  75.      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  /* March */
  76.      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,     /* April */
  77.      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,  /* May */
  78.      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,     /* June */
  79.      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,  /* July */
  80.      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,  /* August */
  81.      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     /* September */
  82.      9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,  /* October */
  83.     10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,     /* November */
  84.     11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11}; /* December */
  85.  
  86. /*  The following two tables map a month index to the number of days preceding */
  87. /*  the month in the year.  Both tables are zero based.  For example, 1 (Feb) */
  88. /*  has 31 days preceding it.  To help calculate the maximum number of days */
  89. /*  in a month each table has 13 entries, so the number of days in a month */
  90. /*  of index i is the table entry of i+1 minus the table entry of i. */
  91.  
  92. SHORT LeapYearDaysPrecedingMonth[13] = {
  93.     0,                                 /* January */
  94.     31,                                /* February */
  95.     31+29,                             /* March */
  96.     31+29+31,                          /* April */
  97.     31+29+31+30,                       /* May */
  98.     31+29+31+30+31,                    /* June */
  99.     31+29+31+30+31+30,                 /* July */
  100.     31+29+31+30+31+30+31,              /* August */
  101.     31+29+31+30+31+30+31+31,           /* September */
  102.     31+29+31+30+31+30+31+31+30,        /* October */
  103.     31+29+31+30+31+30+31+31+30+31,     /* November */
  104.     31+29+31+30+31+30+31+31+30+31+30,  /* December */
  105.     31+29+31+30+31+30+31+31+30+31+30+31};
  106.  
  107. SHORT NormalYearDaysPrecedingMonth[13] = {
  108.     0,                                 /* January */
  109.     31,                                /* February */
  110.     31+28,                             /* March */
  111.     31+28+31,                          /* April */
  112.     31+28+31+30,                       /* May */
  113.     31+28+31+30+31,                    /* June */
  114.     31+28+31+30+31+30,                 /* July */
  115.     31+28+31+30+31+30+31,              /* August */
  116.     31+28+31+30+31+30+31+31,           /* September */
  117.     31+28+31+30+31+30+31+31+30,        /* October */
  118.     31+28+31+30+31+30+31+31+30+31,     /* November */
  119.     31+28+31+30+31+30+31+31+30+31+30,  /* December */
  120.     31+28+31+30+31+30+31+31+30+31+30+31};
  121.  
  122. /*  The following definitions and declarations are some important constants */
  123. /*  used in the time conversion routines */
  124.  
  125. /*  This is the week day that January 1st, 1601 fell on (a Monday) */
  126.  
  127. #define WEEKDAY_OF_1601                  1
  128.  
  129. /*  These are the magic numbers needed to do our extended division.  The
  130.  *  only numbers we ever need to divide by are
  131.  *
  132.  *      10,000 = convert 100ns tics to millisecond tics
  133.  *
  134.  *      10,000,000 = convert 100ns tics to one second tics
  135.  *
  136.  *      86,400,000 = convert Millisecond tics to one day tics
  137.  */
  138.  
  139. FILETIME Magic10000    = {0xe219652c, 0xd1b71758};
  140. #define SHIFT10000                       13
  141.  
  142. FILETIME Magic10000000 = {0xe57a42bd, 0xd6bf94d5};
  143. #define SHIFT10000000                    23
  144.  
  145. FILETIME Magic86400000 = {0xfa67b90e, 0xc6d750eb};
  146. #define SHIFT86400000                    26
  147.  
  148. /*  To make the code more readable we'll also define some macros to */
  149. /*  do the actual division for us */
  150.  
  151. #define Convert100nsToMilliseconds(TIME)            \
  152.     (FtDivFtBogus ((TIME), Magic10000, SHIFT10000))
  153.  
  154. #define ConvertMillisecondsTo100ns(MILLISECONDS)    \
  155.     (FtMulDw (10000, (MILLISECONDS)))
  156.  
  157. #define Convert100nsToSeconds(TIME)                 \
  158.     (FtDivFtBogus ((TIME), Magic10000000, SHIFT10000000))
  159.  
  160. #define ConvertSecondsTo100ns(SECONDS)              \
  161.     (FtMulDw (10000000, (SECONDS)))
  162.  
  163. #define ConvertMillisecondsToDays(TIME)             \
  164.     (FtDivFtBogus ((TIME), Magic86400000, SHIFT86400000))
  165.  
  166. #define ConvertDaysToMilliseconds(DAYS)             \
  167.     (FtMulDwDw ((DAYS), 86400000))
  168.  
  169. /*
  170.  *  ULONG
  171.  *  ElapsedDaysToYears (ULONG ElapsedDays);
  172.  *
  173.  *  To be completely true to the Gregorian calendar the equation to
  174.  *  go from days to years is really
  175.  *
  176.  *      ElapsedDays / 365.2425
  177.  *
  178.  *  But because we are doing the computation in ulong integer arithmetic
  179.  *  and the TIME variable limits the number of expressible days to around
  180.  *  11,000,000 we use the following computation
  181.  *
  182.  *      (ElapsedDays * 128 + 127) / (365.2425 * 128)
  183.  *
  184.  *  which will be off from the Gregorian calendar in about 150,000 years
  185.  *  but that doesn't really matter because TIME can only express around
  186.  *  30,000 years
  187.  */
  188.  
  189. #define ElapsedDaysToYears(DAYS) (((DAYS) * 128 + 127) / 46751)
  190.  
  191. /*
  192.  *
  193.  *  ULONG
  194.  *  NumberOfLeapYears (ULONG ElapsedYears);
  195.  *
  196.  *  The number of leap years is simply the number of years divided by 4
  197.  *  minus years divided by 100 plus years divided by 400.  This says
  198.  *  that every four years is a leap year except centuries, and the
  199.  *  exception to the exception is the quadricenturies
  200.  */
  201.  
  202. #define NumberOfLeapYears(YEARS) \
  203.         (((YEARS) / 4) - ((YEARS) / 100) + ((YEARS) / 400))
  204.  
  205. /*
  206.  *  ULONG
  207.  *  ElapsedYearsToDays (ULONG ElapsedYears);
  208.  *
  209.  *  The number of days contained in elapsed years is simply the number
  210.  *  of years times 365 (because every year has at least 365 days) plus
  211.  *  the number of leap years there are (i.e., the number of 366 days years)
  212.  */
  213.  
  214. #define ElapsedYearsToDays(YEARS) (((YEARS) * 365) + NumberOfLeapYears(YEARS))
  215.  
  216. /*
  217.  *  BOOLEAN
  218.  *  IsLeapYear (ULONG ElapsedYears);
  219.  *
  220.  *  If it is an even 400 or a non century leapyear then the
  221.  *  answer is true otherwise it's false
  222.  */
  223.  
  224. #define IsLeapYear(YEARS)                           \
  225.     ((((YEARS) % 400 == 0) ||                       \
  226.      ((YEARS) % 100 != 0) && ((YEARS) % 4 == 0))    \
  227.     ? TRUE : FALSE)
  228. /*
  229.  *  ULONG
  230.  *  MaxDaysInMonth (ULONG Year, ULONG Month);
  231.  *
  232.  *  The maximum number of days in a month depend on the year and month.
  233.  *  It is the difference between the days to the month and the days
  234.  *  to the following month
  235.  */
  236.  
  237. #define MaxDaysInMonth(YEAR,MONTH)                                          \
  238.     (IsLeapYear(YEAR) ?                                                     \
  239.         LeapYearDaysPrecedingMonth[(MONTH) + 1] -                           \
  240.                                     LeapYearDaysPrecedingMonth[(MONTH)]     \
  241.     :                                                                       \
  242.         NormalYearDaysPrecedingMonth[(MONTH) + 1] -                         \
  243.                                     NormalYearDaysPrecedingMonth[(MONTH)])
  244.  
  245.  
  246. /*  Internal Support routine */
  247.  
  248. VOID
  249. TimeToDaysAndFraction (const FILETIME FAR * Time,
  250.     ULONG FAR * ElapsedDays,
  251.     ULONG FAR * Milliseconds)
  252.  
  253. /*++
  254.  
  255. Routine Description:
  256.  
  257.     This routine converts an input 64-bit time value to the number
  258.     of total elapsed days and the number of milliseconds in the
  259.     partial day.
  260.  
  261. Arguments:
  262.  
  263.     Time - Supplies the input time to convert from
  264.  
  265.     ElapsedDays - Receives the number of elapsed days
  266.  
  267.     Milliseconds - Receives the number of milliseconds in the partial day
  268.  
  269. Return Value:
  270.  
  271.     None
  272.  
  273. --*/
  274.  
  275. {
  276.     FILETIME TotalMilliseconds;
  277.     FILETIME Temp;
  278.  
  279.     /*  Convert the input time to total milliseconds */
  280.  
  281.     TotalMilliseconds = Convert100nsToMilliseconds( *Time );
  282.  
  283.     /*  Convert milliseconds to total days */
  284.  
  285.     Temp = ConvertMillisecondsToDays( TotalMilliseconds );
  286.  
  287.     /*  Set the elapsed days from temp, we've divided it enough so that */
  288.     /*  the high part must be zero. */
  289.  
  290.     *ElapsedDays = Temp.dwLowDateTime;
  291.  
  292.     /*  Calculate the exact number of milliseconds in the elapsed days */
  293.     /*  and subtract that from the total milliseconds to figure out */
  294.     /*  the number of milliseconds left in the partial day */
  295.  
  296.     Temp = ConvertDaysToMilliseconds( *ElapsedDays );
  297.  
  298.     Temp = FtSubFt( TotalMilliseconds, Temp );
  299.  
  300.     /*  Set the fraction part from temp, the total number of milliseconds in */
  301.     /*  a day guarantees that the high part must be zero. */
  302.  
  303.     *Milliseconds = Temp.dwLowDateTime;
  304.  
  305.     /*  And return to our caller */
  306.  
  307.     return;
  308. }
  309.  
  310. /*  Internal Support routine */
  311.  
  312. VOID
  313. DaysAndFractionToTime (ULONG ElapsedDays,
  314.     ULONG Milliseconds,
  315.     FILETIME FAR * Time)
  316.  
  317. /*++
  318.  
  319. Routine Description:
  320.  
  321.     This routine converts an input elapsed day count and partial time
  322.     in milliseconds to a 64-bit time value.
  323.  
  324. Arguments:
  325.  
  326.     ElapsedDays - Supplies the number of elapsed days
  327.  
  328.     Milliseconds - Supplies the number of milliseconds in the partial day
  329.  
  330.     Time - Receives the output time to value
  331.  
  332. Return Value:
  333.  
  334.     None
  335.  
  336. --*/
  337.  
  338. {
  339.     FILETIME Temp;
  340.     FILETIME Temp2;
  341.  
  342.     /*  Calculate the exact number of milliseconds in the elapsed days. */
  343.  
  344.     Temp = ConvertDaysToMilliseconds( ElapsedDays );
  345.  
  346.     /*  Convert milliseconds to a large integer */
  347.  
  348.     Temp2.dwLowDateTime = Milliseconds;
  349.     Temp2.dwHighDateTime = 0;
  350.  
  351.     /*  add milliseconds to the whole day milliseconds */
  352.  
  353.     Temp = FtAddFt( Temp, Temp2 );
  354.  
  355.     /*  Finally convert the milliseconds to 100ns resolution */
  356.  
  357.     *Time = ConvertMillisecondsTo100ns( Temp );
  358.  
  359.     /*  and return to our caller */
  360.  
  361.     return;
  362. }
  363.  
  364.  
  365.  
  366. BOOL __export WINAPI
  367. FileTimeToSystemTime (const FILETIME FAR * Time,
  368.     SYSTEMTIME FAR *TimeFields)
  369.  
  370. /*++
  371.  
  372. Routine Description:
  373.  
  374.     This routine converts an input 64-bit TIME variable to its corresponding
  375.     time field record.  It will tell the caller the year, month, day, hour,
  376.     minute, second, millisecond, and weekday corresponding to the input time
  377.     variable.
  378.  
  379. Arguments:
  380.  
  381.     Time - Supplies the time value to interpret
  382.  
  383.     TimeFields - Receives a value corresponding to Time
  384.  
  385. Return Value:
  386.  
  387.     None
  388.  
  389. --*/
  390.  
  391. {
  392.     ULONG Years;
  393.     ULONG Month;
  394.     ULONG Days;
  395.  
  396.     ULONG Hours;
  397.     ULONG Minutes;
  398.     ULONG Seconds;
  399.     ULONG Milliseconds;
  400.     
  401.     /* parameter validation */
  402.     
  403.     AssertSz( Time && !IsBadReadPtr( Time, sizeof( FILETIME ) ),
  404.             "Time fails address check" );
  405.             
  406.     AssertSz( TimeFields && !IsBadWritePtr( TimeFields, sizeof( SYSTEMTIME ) ),
  407.             "TimeFields fails address check" );
  408.  
  409.     /*  First divide the input time 64 bit time variable into */
  410.     /*  the number of whole days and part days (in milliseconds) */
  411.  
  412.     TimeToDaysAndFraction( Time, &Days, &Milliseconds );
  413.  
  414.     /*  Compute which weekday it is and save it away now in the output */
  415.     /*  variable.  We add the weekday of the base day to bias our computation */
  416.     /*  which means that if one day has elapsed then we the weekday we want */
  417.     /*  is the Jan 2nd, 1601. */
  418.  
  419.     TimeFields->wDayOfWeek= (SHORT)((Days + WEEKDAY_OF_1601) % 7);
  420.  
  421.     /*  Calculate the number of whole years contained in the elapsed days */
  422.     /*  For example if Days = 500 then Years = 1 */
  423.  
  424.     Years = ElapsedDaysToYears( Days );
  425.  
  426.     /*  And subtract the number of whole years from our elapsed days */
  427.     /*  For example if Days = 500, Years = 1, and the new days is equal */
  428.     /*  to 500 - 365 (normal year). */
  429.  
  430.     Days = Days - ElapsedYearsToDays( Years );
  431.  
  432.     /*  Now test whether the year we are working on (i.e., The year */
  433.     /*  after the total number of elapsed years) is a leap year */
  434.     /*  or not. */
  435.  
  436.     if (IsLeapYear( Years + 1 )) {
  437.  
  438.         /*  The current year is a leap year, so figure out what month */
  439.         /*  it is, and then subtract the number of days preceding the */
  440.         /*  month from the days to figure out what day of the month it is */
  441.  
  442.         Month = LeapYearDayToMonth[Days];
  443.         Days = Days - LeapYearDaysPrecedingMonth[Month];
  444.  
  445.     } else {
  446.  
  447.         /*  The current year is a normal year, so figure out the month */
  448.         /*  and days as described above for the leap year case */
  449.  
  450.         Month = NormalYearDayToMonth[Days];
  451.         Days = Days - NormalYearDaysPrecedingMonth[Month];
  452.  
  453.     }
  454.  
  455.     /*  Now we need to compute the elapsed hour, minute, second, milliseconds */
  456.     /*  from the millisecond variable.  This variable currently contains */
  457.     /*  the number of milliseconds in our input time variable that did not */
  458.     /*  fit into a whole day.  To compute the hour, minute, second part */
  459.     /*  we will actually do the arithmetic backwards computing milliseconds */
  460.     /*  seconds, minutes, and then hours.  We start by computing the */
  461.     /*  number of whole seconds left in the day, and then computing */
  462.     /*  the millisecond remainder. */
  463.  
  464.     Seconds = Milliseconds / 1000;
  465.     Milliseconds = Milliseconds % 1000;
  466.  
  467.     /*  Now we compute the number of whole minutes left in the day */
  468.     /*  and the number of remainder seconds */
  469.  
  470.     Minutes = Seconds / 60;
  471.     Seconds = Seconds % 60;
  472.  
  473.     /*  Now compute the number of whole hours left in the day */
  474.     /*  and the number of remainder minutes */
  475.  
  476.     Hours = Minutes / 60;
  477.     Minutes = Minutes % 60;
  478.  
  479.     /*  As our final step we put everything into the time fields */
  480.     /*  output variable */
  481.  
  482.     TimeFields->wYear         = (SHORT)(Years + 1601);
  483.     TimeFields->wMonth        = (SHORT)(Month + 1);
  484.     TimeFields->wDay          = (SHORT)(Days + 1);
  485.     TimeFields->wHour         = (SHORT)Hours;
  486.     TimeFields->wMinute       = (SHORT)Minutes;
  487.     TimeFields->wSecond       = (SHORT)Seconds;
  488.     TimeFields->wMilliseconds = (SHORT)Milliseconds;
  489.  
  490.     /*  and return to our caller */
  491.  
  492.     return TRUE;
  493. }
  494.  
  495. BOOL __export WINAPI
  496. SystemTimeToFileTime (const SYSTEMTIME FAR *TimeFields,
  497.     FILETIME FAR * Time)
  498.  
  499. /*++
  500.  
  501. Routine Description:
  502.  
  503.     This routine converts an input Time Field variable to a 64-bit NT time
  504.     value.  It ignores the WeekDay of the time field.
  505.  
  506. Arguments:
  507.  
  508.     TimeFields - Supplies the time field record to use
  509.  
  510.     Time - Receives the NT Time corresponding to TimeFields
  511.  
  512. Return Value:
  513.  
  514.     BOOLEAN - TRUE if the Time Fields is well formed and within the
  515.         range of time expressible by TIME and FALSE otherwise.
  516.  
  517. --*/
  518.  
  519. {
  520.     ULONG Year;
  521.     ULONG Month;
  522.     ULONG Day;
  523.     ULONG Hour;
  524.     ULONG Minute;
  525.     ULONG Second;
  526.     ULONG Milliseconds;
  527.  
  528.     ULONG ElapsedDays;
  529.     ULONG ElapsedMilliseconds;
  530.  
  531.     /* parameter validation */
  532.     
  533.     AssertSz( TimeFields && !IsBadReadPtr( TimeFields, sizeof( SYSTEMTIME ) ),
  534.             "TimeFields fails address check" );
  535.             
  536.     AssertSz( Time && !IsBadWritePtr( Time, sizeof( FILETIME ) ),
  537.             "Time fails address check" );
  538.             
  539.     /*  Load the time field elements into local variables.  This should */
  540.     /*  ensure that the compiler will only load the input elements */
  541.     /*  once, even if there are alias problems.  It will also make */
  542.     /*  everything (except the year) zero based.  We cannot zero base the */
  543.     /*  year because then we can't recognize cases where we're given a year */
  544.     /*  before 1601. */
  545.  
  546.     Year         = TimeFields->wYear;
  547.     Month        = TimeFields->wMonth - 1;
  548.     Day          = TimeFields->wDay - 1;
  549.     Hour         = TimeFields->wHour;
  550.     Minute       = TimeFields->wMinute;
  551.     Second       = TimeFields->wSecond;
  552.     Milliseconds = TimeFields->wMilliseconds;
  553.  
  554.     /*  Check that the time field input variable contains */
  555.     /*  proper values. */
  556.  
  557.     if ((Year < 1601)                        ||
  558.         (Month > 11)                         ||
  559.         ((SHORT)Day >= MaxDaysInMonth(Year, Month)) ||
  560.         (Hour > 23)                          ||
  561.         (Minute > 59)                        ||
  562.         (Second > 59)                        ||
  563.         (Milliseconds > 999)) {
  564.  
  565.         return FALSE;
  566.  
  567.     }
  568.  
  569.     /*  Compute the total number of elapsed days represented by the */
  570.     /*  input time field variable */
  571.  
  572.     ElapsedDays = ElapsedYearsToDays( Year - 1601 );
  573.  
  574.     if (IsLeapYear( Year - 1600 )) {
  575.  
  576.         ElapsedDays += LeapYearDaysPrecedingMonth[ Month ];
  577.  
  578.     } else {
  579.  
  580.         ElapsedDays += NormalYearDaysPrecedingMonth[ Month ];
  581.  
  582.     }
  583.  
  584.     ElapsedDays += Day;
  585.  
  586.     /*  Now compute the total number of milliseconds in the fractional */
  587.     /*  part of the day */
  588.  
  589.     ElapsedMilliseconds = (((Hour*60) + Minute)*60 + Second)*1000 + Milliseconds;
  590.  
  591.     /*  Given the elapsed days and milliseconds we can now build */
  592.     /*  the output time variable */
  593.  
  594.     DaysAndFractionToTime( ElapsedDays, ElapsedMilliseconds, Time );
  595.  
  596.     /*  And return to our caller */
  597.  
  598.     return TRUE;
  599. }
  600.  
  601.  
  602.  
  603. BOOL
  604. __export WINAPI
  605. FileTimeToDosDateTime(const FILETIME FAR * lpFileTime,
  606.     LPWORD lpFatDate,
  607.     LPWORD lpFatTime)
  608.  
  609. /*++
  610.  
  611. Routine Description:
  612.  
  613.     This function converts a 64-bit file time into DOS date and time value
  614.     which is represented as two 16-bit unsigned integers.
  615.  
  616.     Since the DOS date format can only represent dates between 1/1/80 and
  617.     12/31/2099, this conversion can fail if the input file time is outside
  618.     of this range.
  619.  
  620. Arguments:
  621.  
  622.     lpFileTime - Supplies the 64-bit file time to convert to DOS date and
  623.         time format.
  624.  
  625.     lpFatDate - Returns the 16-bit DOS representation of date.
  626.  
  627.     lpFatTime - Returns the 16-bit DOS representation of time.
  628.  
  629. Return Value:
  630.  
  631.     TRUE - The file time was successfully converted.
  632.  
  633.     FALSE - The operation failed. Extended error status is available
  634.         using GetLastError.
  635.  
  636. --*/
  637. {
  638.     SYSTEMTIME  TimeFields;
  639.     BOOL        fRet = TRUE;
  640.  
  641.     /* parameter validation */
  642.     
  643.     AssertSz( lpFileTime && !IsBadReadPtr( lpFileTime, sizeof( FILETIME ) ),
  644.             "lpFileTime fails address check" );
  645.             
  646.     AssertSz( lpFatDate && !IsBadWritePtr( lpFatDate, sizeof( LPWORD ) ),
  647.             "lpFatDate fails address check" );
  648.  
  649.     AssertSz( lpFatTime && !IsBadWritePtr( lpFatTime, sizeof( LPWORD ) ),
  650.             "lpFatTime fails address check" );
  651.             
  652.     FileTimeToSystemTime(lpFileTime, &TimeFields);
  653.  
  654.     if (TimeFields.wYear < 1980 || TimeFields.wYear > 2099) {
  655.     /* BaseSetLastNTError(STATUS_INVALID_PARAMETER); */
  656.     fRet = FALSE;
  657.     goto Exit;
  658.         }
  659.  
  660.     *lpFatDate = (WORD)( ((USHORT)(TimeFields.wYear-(SHORT)1980) << 9) |
  661.                          ((USHORT)TimeFields.wMonth << 5) |
  662.                          (USHORT)TimeFields.wDay
  663.                        );
  664.  
  665.     *lpFatTime = (WORD)( ((USHORT)TimeFields.wHour << 11) |
  666.                          ((USHORT)TimeFields.wMinute << 5) |
  667.                          ((USHORT)TimeFields.wSecond >> 1)
  668.                        );
  669.  
  670. Exit:
  671.     return fRet;
  672. }
  673.  
  674.  
  675. BOOL
  676. __export WINAPI
  677. DosDateTimeToFileTime(
  678.     WORD wFatDate,
  679.     WORD wFatTime,
  680.     FILETIME FAR * lpFileTime
  681.     )
  682.  
  683. /*++
  684.  
  685. Routine Description:
  686.  
  687.     This function converts a DOS date and time value, which is
  688.     represented as two 16-bit unsigned integers, into a 64-bit file
  689.     time.
  690.  
  691. Arguments:
  692.  
  693.     lpFatDate - Supplies the 16-bit DOS representation of date.
  694.  
  695.     lpFatTime - Supplies the 16-bit DOS representation of time.
  696.  
  697.     lpFileTime - Returns the 64-bit file time converted from the DOS
  698.         date and time format.
  699.  
  700. Return Value:
  701.  
  702.     TRUE - The Dos date and time were successfully converted.
  703.  
  704.     FALSE - The operation failed. Extended error status is available
  705.         using GetLastError.
  706.  
  707. --*/
  708. {
  709.     SYSTEMTIME  TimeFields;
  710.     BOOL        fRet;
  711.  
  712.     /* parameter validation */
  713.  
  714.     AssertSz( lpFileTime && !IsBadWritePtr( lpFileTime, sizeof( FILETIME ) ),
  715.             "lpFileTime fails address check" );
  716.             
  717.             
  718.     TimeFields.wYear        = (SHORT)(((wFatDate & 0xFE00) >> 9) + 1980);
  719.     TimeFields.wMonth        = (SHORT)((wFatDate & 0x01E0) >> 5);
  720.     TimeFields.wDay          = (SHORT)((wFatDate & 0x001F) >> 0);
  721.     TimeFields.wHour         = (SHORT)((wFatTime & 0xF800) >> 11);
  722.     TimeFields.wMinute       = (SHORT)((wFatTime & 0x07E0) >>  5);
  723.     TimeFields.wSecond      = (SHORT)((wFatTime & 0x001F) <<  1);
  724.     TimeFields.wMilliseconds = 0;
  725.  
  726.     fRet = SystemTimeToFileTime(&TimeFields, lpFileTime);
  727.     return (fRet);
  728. }
  729.  
  730. LONG
  731. __export WINAPI
  732. CompareFileTime(
  733.     const FILETIME FAR * t1,
  734.     const FILETIME FAR * t2
  735.     )
  736.  
  737. /*++
  738.  
  739. Routine Description:
  740.  
  741.     This function compares two 64-bit file times.
  742.  
  743. Arguments:
  744.  
  745.     lpFileTime1 - pointer to a 64-bit file time.
  746.  
  747.     lpFileTime2 - pointer to a 64-bit file time.
  748.  
  749. Return Value:
  750.  
  751.     -1 - *lpFileTime1 <  *lpFileTime2
  752.  
  753.      0 - *lpFileTime1 == *lpFileTime2
  754.  
  755.     +1 - *lpFileTime1 >  *lpFileTime2
  756.  
  757. --*/
  758.  
  759. {
  760.     LONG Ret;
  761.  
  762.     /* parameter validation */
  763.     
  764.     AssertSz( t1 && !IsBadReadPtr( t1, sizeof( FILETIME ) ),
  765.             "t1 fails address check" );
  766.             
  767.     AssertSz( t2 && !IsBadReadPtr( t2, sizeof( FILETIME ) ),
  768.             "t2 fails address check" );
  769.  
  770.     /* common case is high words equal, test for that first */
  771.     if (t1->dwHighDateTime == t2->dwHighDateTime) {
  772.  
  773.     if (t1->dwLowDateTime < t2->dwLowDateTime)
  774.         Ret = -1;
  775.     else if (t1->dwLowDateTime > t2->dwLowDateTime)
  776.         Ret = 1;
  777.     else
  778.         Ret = 0;
  779.     } else if (t1->dwHighDateTime < t2->dwHighDateTime)
  780.     Ret = -1;
  781.     else        /* since high words aren't equal, */
  782.     Ret = 1;    /* and t1 isn't less than t2, it must be greater */
  783.  
  784.     return (Ret);
  785. }
  786.  
  787. /*
  788.  *  Dynamic global variables involved in GMT <=> local time conversions.
  789.  *
  790.  *  fDaylight:
  791.  *      0 if we're in standard time
  792.  *      1 if were in daylight savings time
  793.     -1 if nothing has been initialized yet
  794.  *
  795.  *  ftStandardBias:
  796.  *      Delta between GMT and local standard time.
  797.  *
  798.  *  ftDaylightBias:
  799.  *      Delta between GMT and local daylight time.
  800.  *
  801.  *  __tz:
  802.  *
  803.  *  stUSST:
  804.  *      U.S. rule for beginning of standard time (last Sunday in
  805.  *      October)
  806.  *
  807.  *  stUSDT:
  808.  *      U.S. rule for beginning of daylight savings time (first
  809.  *      Sunday in April)
  810.  */
  811. int         fDaylight           = -1;
  812. FILETIME    ftStandardBias      = { 0xFFFFFFFF, 0xFFFFFFFF };
  813. FILETIME    ftDaylightBias      = { 0xFFFFFFFF, 0xFFFFFFFF };
  814. TIME_ZONE_INFORMATION __tz      = { 0, {0}, {0}, 0, {0}, {0}, 0 };
  815. SYSTEMTIME  stUSST              = { 0, 10, 0, 5, 2, 0, 0, 0 };
  816. SYSTEMTIME  stUSDT              = { 0, 4,  0, 1, 2, 0, 0, 0 };
  817.  
  818.  
  819. /*
  820.  *  Initializes the global variables related to the local timezone,
  821.  *  and detects ST <=> DST transition.
  822.  */
  823. void
  824. CheckTimezone(SYSTEMTIME FAR *pst)
  825. {
  826.     int         fDSTNow;
  827.     FILETIME    ft;
  828.     SYSTEMTIME  st;
  829.     DWORD       dw;
  830.     LONG        l;
  831.  
  832.     /* validate parameters */
  833.     
  834.     AssertSz( !pst || !IsBadReadPtr( pst, sizeof( SYSTEMTIME ) ),
  835.             "pst fails address check" );
  836.  
  837.     DOSTime(&st);
  838.  
  839.     if (fDaylight == -1)
  840.         (void) GetTimeZoneInformation(&__tz);
  841.  
  842.     fDSTNow = FDSTOfSystemTime(st, &__tz);
  843.  
  844.     /*  This will kick in, usually, only once (when initializing). */
  845.     /*  Rarely, when we're running while the transition to or from */
  846.     /*  DST happens, it will kick in a second time. */
  847.  
  848.     if (fDaylight != fDSTNow)
  849.     {
  850.         fDaylight = fDSTNow;
  851.  
  852.         /*  Get the local timezone from the environment. */
  853.         dw = GetTimeZoneInformation(&__tz);
  854.  
  855.         /*  Note: the Win32 TIME_ZONE_INFORMATION structure */
  856.         /*  specifies the bias from GMT as positive for the */
  857.         /*  Western hemisphere. Our code *adds* the bias to make */
  858.         /*  local time from GMT, so we have to negate what we get. */
  859.  
  860.         /*  Set up the standard time variables. */
  861.         l = __tz.Bias + __tz.StandardBias;
  862.         ft.dwLowDateTime = 60 * (l >= 0 ? l : -l);
  863.         ft.dwHighDateTime = 0L;
  864.         ft = ConvertSecondsTo100ns(ft);
  865.         ftStandardBias = (l >= 0 ? FtNegFt(ft) : ft);
  866.  
  867.         /*  Set up the daylight time variables. */
  868.         l = __tz.Bias + __tz.DaylightBias;
  869.         ft.dwLowDateTime = 60 * (l >= 0 ? l : -l);
  870.         ft.dwHighDateTime = 0L;
  871.         ft = ConvertSecondsTo100ns(ft);
  872.         ftDaylightBias = (l >= 0 ? FtNegFt(ft) : ft);
  873.     }
  874.  
  875.     if (pst)
  876.         *pst = st;
  877. }
  878.  
  879.  
  880. /*
  881.  *  Fills in a TIME_ZONE_INFORMATION structure based on the TZ
  882.  *  environment variable. Returns TRUE if the variable was found
  883.  *  and parsed OK, otherwise FALSE.
  884.  *
  885.  *  Daylight savings bias, start and end date follow U.S. rules
  886.  *  (hardcoded), i.e. DST runs from the first Sunday in April
  887.  *  through the last Sunday in October.
  888.  *
  889.  *  //$ BUG? reported not to work properly under the NT WoW subsystem.
  890.  *
  891.  */
  892. BOOL
  893. FParseTZ(LPTIME_ZONE_INFORMATION ptz)
  894. {
  895.     LPSTR   sz;
  896.     LPSTR   szStandardName;
  897.     int     h = 0;
  898.     int     m = 0;
  899.     BOOL    fEast = FALSE;
  900.  
  901.     Assert(!IsBadWritePtr(ptz, sizeof(TIME_ZONE_INFORMATION)));
  902.     ZeroMemory(ptz, sizeof(TIME_ZONE_INFORMATION));
  903.  
  904.     /*  Find the value of the TZ environment variable */
  905.     sz = GetDOSEnvironment();
  906.     while (sz && *sz)
  907.     {
  908.         if (lstrlen(sz) > 5 &&
  909.             (sz[0] == 't' || sz[0] == 'T') &&
  910.             (sz[1] == 'z' || sz[1] == 'Z') &&
  911.             sz[2] == '=')
  912.         {
  913.             /*  Skip over "TZ=" */
  914.             sz += 3;
  915.             break;
  916.         }
  917.         sz += lstrlen(sz) + 1;
  918.     }
  919.  
  920.     if (!sz || !*sz)
  921.         return FALSE;       /*  Not found. */
  922.  
  923.     /*  sz now points at the value of the variable. */
  924.     /*  Extract the numeric part. */
  925.     szStandardName = sz;
  926.     while (*sz && *sz != '+' && *sz != '-' && (*sz < '0' || *sz > '9'))
  927.         ++sz;
  928.  
  929.     /*  Parse out the standard timezone name */
  930.     if (sz > szStandardName)
  931.         MemCopy(ptz->StandardName, szStandardName, sz - szStandardName);
  932.  
  933.     /*  Parse out the sign */
  934.     if (*sz == '-')
  935.     {
  936.         fEast = TRUE;
  937.         ++sz;
  938.     }
  939.     else if (*sz == '+')
  940.         ++sz;
  941.  
  942.     /*  Parse out the hours */
  943.     while (*sz >= '0' && *sz <= '9')
  944.     {
  945.         h = h * 10 + (*sz - '0');
  946.         ++sz;
  947.     }
  948.  
  949.     /*  Parse out the minutes */
  950.     if (*sz == ':')
  951.     {
  952.         while (*sz >= '0' && *sz <= '9')
  953.         {
  954.             m = m * 10 + (*sz - '0');
  955.             ++sz;
  956.         }
  957.     }
  958.  
  959.     /*  Parse out the daylight timezone name */
  960.     if (*sz)
  961.         lstrcpy(ptz->DaylightName, sz);
  962.  
  963.     /*  Calculate the bias in minutes (still unsigned) */
  964.     m += h * 60;
  965.  
  966.     /*  Populate the structure. DST rules are U.S. rules. */
  967.     ptz->Bias = (fEast ? -m : m);
  968.     ptz->StandardBias = 0;
  969.     ptz->DaylightBias = -60;
  970.     MemCopy((LPBYTE)&ptz->StandardDate, (LPBYTE)&stUSST, sizeof(SYSTEMTIME));
  971.     MemCopy((LPBYTE)&ptz->DaylightDate, (LPBYTE)&stUSDT, sizeof(SYSTEMTIME));
  972.     return TRUE;
  973. }
  974.  
  975. void
  976. DOSTime(SYSTEMTIME FAR *pst)
  977. {
  978.     WORD segst = SELECTOROF(pst);
  979.     WORD offst = OFFSETOF(pst);
  980.  
  981.     /*  Get the current date from DOS. */
  982.     _asm {
  983.         push ds
  984.  
  985.         mov ax, segst               ; point ds:bx at the SYSTEMTIME
  986.         mov ds, ax
  987.         mov bx, offst
  988.  
  989.         mov ah, 2Ah                 ; get date
  990.         call DOS3Call               ;
  991.         mov ah, 0
  992.         mov [bx]SYSTEMTIME.wDayOfWeek,ax        ; 0 = Sunday
  993.         mov [bx]SYSTEMTIME.wYear, cx            ; 1980 forward
  994.         mov al, dh
  995.         mov [bx]SYSTEMTIME.wMonth, ax           ; 1 = January
  996.         mov al, dl
  997.         mov [bx]SYSTEMTIME.wDay, ax         ; 1 through 31
  998.  
  999.         mov ah, 2Ch                 ; get time
  1000.         call DOS3Call               ;
  1001.         mov ah, 0
  1002.         mov al, ch
  1003.         mov [bx]SYSTEMTIME.wHour, ax            ; 0 - 23
  1004.         mov al, cl
  1005.         mov [bx]SYSTEMTIME.wMinute, ax      ; 0 - 59
  1006.         mov al, dh
  1007.         mov [bx]SYSTEMTIME.wSecond, ax      ; 0 - 59
  1008.         mov al, dl                          ; 0 - 99
  1009.         mov [bx]SYSTEMTIME.wMilliseconds, ax    ; hundredths
  1010.  
  1011.         pop ds
  1012.     }
  1013.  
  1014.     pst->wMilliseconds *= 10;
  1015. }
  1016.  
  1017. /*
  1018.  *  Interprets a SYSTEMTIME structure that defines the begin date
  1019.  *  and time for DST or ST, according to the Win32 rules (see the
  1020.  *  entry for TIME_ZONE_INFORMATION in the Win32 docs).
  1021.  *
  1022.  *  Returns a FILETIME equal to the begin date/time for the year
  1023.  *  specified in stTarget.
  1024.  */
  1025. FILETIME
  1026. FtBegin(SYSTEMTIME stTarget, SYSTEMTIME stBegin)
  1027. {
  1028.     FILETIME    ft;
  1029.     WORD        wDayOfWeek;
  1030.     WORD        wDayOf1;
  1031.     WORD        iWeek;
  1032.     WORD        wDay;
  1033.     WORD        wLastDay;
  1034.     BOOL        fFive = FALSE;
  1035.  
  1036.     if (stBegin.wYear == 0)
  1037.     {
  1038.         /*  It's a relative time. */
  1039.         /*  Get the weekday for the first of the month, and use that */
  1040.         /*  to compute the date we need. */
  1041.  
  1042.         /*  Remember the magic values: the weekday and week ordinal. */
  1043.         /*  A week ordinal of 5 means the last such weekday in the month. */
  1044.         wDayOfWeek = stBegin.wDayOfWeek;
  1045.         iWeek = stBegin.wDay;
  1046.         if (iWeek == 5)
  1047.         {
  1048.             fFive = TRUE;
  1049.             iWeek = 4;      /*  every month has 4 of each day */
  1050.         }
  1051.  
  1052.         /*  Scope out the first day of the month. */
  1053.         /*  Tricky: converting to filetime and back gets us the day of week. */
  1054.         stBegin.wYear = stTarget.wYear;
  1055.         stBegin.wDay = 1;
  1056.         SideAssert(SystemTimeToFileTime(&stBegin, &ft));
  1057.         SideAssert(FileTimeToSystemTime(&ft, &stBegin));
  1058.         wDayOf1 = stBegin.wDayOfWeek;
  1059.  
  1060.         /*  Compute the day corresponding to our week and weekday. */
  1061.         wDay = 1 + (7 * iWeek) + (wDayOfWeek - wDayOf1);
  1062.         wLastDay = (WORD) MaxDaysInMonth(stBegin.wYear, stBegin.wMonth-1);
  1063.         if (wDay > wLastDay)
  1064.             wDay -= 7;
  1065.         Assert(wDay < 31);
  1066.         if (fFive && (wDay + 7 <= wLastDay))
  1067.             wDay += 7;
  1068.  
  1069.         /*  Re-convert to filetime */
  1070.         stBegin.wDay = wDay;
  1071.         SideAssert(SystemTimeToFileTime(&stBegin, &ft));
  1072.     }
  1073.     else
  1074.     {
  1075.         /*  It's an absolute date and time. */
  1076.         /*  Just substitute the current year and return it. */
  1077.         /* //$  SPEC Does the absolute year mark a cutoff of some sort? */
  1078.         /* //$  e.g. no DST prior to 1986 (when the current rules went */
  1079.         /* //$  into effect) ? */
  1080.         stBegin.wYear = stTarget.wYear;
  1081.         SideAssert(SystemTimeToFileTime(&stBegin, &ft));
  1082.     }
  1083.  
  1084.     return ft;
  1085. }
  1086.  
  1087. /*
  1088.  *  Determines whether 'st' falls in daylight savings or standard
  1089.  *  time, according to 'ptz'.
  1090.  */
  1091. BOOL
  1092. FDSTOfSystemTime(SYSTEMTIME st, LPTIME_ZONE_INFORMATION ptz)
  1093. {
  1094.     FILETIME    ft;
  1095.     FILETIME    ftDT;
  1096.     FILETIME    ftST;
  1097.     int         n0;
  1098.     int         n1;
  1099.     int         n2;
  1100.  
  1101.     /* if the month for standard time is 0, no DST */
  1102.     if (ptz->StandardDate.wMonth == 0)
  1103.         return FALSE;
  1104.  
  1105.     /*  If we don't know anything about DST, say we're not in DST. */
  1106.     if (ptz->StandardBias == ptz->DaylightBias)
  1107.         return FALSE;
  1108.  
  1109.     /*  Compute begin dates for DT and ST in the year we're interested in. */
  1110.     /* //$  BUG Adjust for bias? Target time is local. */
  1111.     ftST = FtBegin(st, ptz->StandardDate);
  1112.     ftDT = FtBegin(st, ptz->DaylightDate);
  1113.     n0 = (int) CompareFileTime(&ftDT, &ftST);
  1114.     if (n0 == 0)
  1115.         /*  pathological: start of DT == start of ST. Say no. */
  1116.         return FALSE;
  1117.     /*  n0 ==  1 if DT begins after ST */
  1118.     /*  n0 == -1 if ST begins after DT */
  1119.  
  1120.     SideAssert(SystemTimeToFileTime(&st, &ft));
  1121.     n1 = (int) CompareFileTime(&ft, &ftDT);
  1122.     n2 = (int) CompareFileTime(&ft, &ftST);
  1123.     if (n1 == n2)
  1124.         /*  The target time is not between DT and ST. */
  1125.         /*  It's DT if DT starts after ST. */
  1126.         return n0 > 0;
  1127.     else
  1128.         /*  The target time is between DT and ST. */
  1129.         /*  It's DT if ST starts after DT. */
  1130.         return n0 < 0;
  1131.  
  1132. }
  1133.  
  1134. BOOL
  1135. FDSTOfFileTime(FILETIME ft)
  1136. {
  1137.     SYSTEMTIME st;
  1138.  
  1139.     if (!FileTimeToSystemTime(&ft, &st))
  1140.         return FALSE;
  1141.     return FDSTOfSystemTime(st, &__tz);
  1142. }
  1143.  
  1144. BOOL
  1145. __export WINAPI
  1146. FileTimeToLocalFileTime (const FILETIME FAR * lpft, FILETIME FAR * lpftLocal)
  1147. {
  1148.     CheckTimezone(NULL);
  1149.  
  1150.     /*  This used to check FDSTOfFileTime(*lpft) instead of */
  1151.     /*  simply fDaylight. It was changed in order to be compatible */
  1152.     /*  with Win32. */
  1153.     *lpftLocal = FtAddFt(*lpft, fDaylight ? ftDaylightBias : ftStandardBias);
  1154.  
  1155.     return(TRUE) ;
  1156. }
  1157.  
  1158.  
  1159. BOOL
  1160. __export WINAPI
  1161. LocalFileTimeToFileTime (const FILETIME FAR * lpftLocal, FILETIME FAR * lpft)
  1162. {
  1163.  
  1164.     AssertSz( lpftLocal && !IsBadReadPtr( lpftLocal, sizeof( FILETIME ) ),
  1165.             "lpftLocal fails address check" );
  1166.     
  1167.     AssertSz( lpft && !IsBadWritePtr( lpft, sizeof( FILETIME ) ),
  1168.             "lpft fails address check" );
  1169.             
  1170.     CheckTimezone(NULL);
  1171.  
  1172.     /*  This used to check FDSTOfFileTime(*lpft) instead of */
  1173.     /*  simply fDaylight. It was changed in order to be compatible */
  1174.     /*  with Win32. */
  1175.     *lpft = FtSubFt(*lpftLocal,
  1176.         fDaylight ? ftDaylightBias : ftStandardBias);
  1177.  
  1178.     return(TRUE) ;
  1179. }
  1180.  
  1181. /*
  1182.  *  GetSystemTime
  1183.  *
  1184.  *  Returns the current time as GMT.
  1185.  */
  1186. void __export WINAPI
  1187. GetSystemTime(SYSTEMTIME FAR *pst)
  1188. {
  1189.     SYSTEMTIME  st;
  1190.     FILETIME    ft;
  1191.     
  1192.     AssertSz( pst && !IsBadWritePtr( pst, sizeof( SYSTEMTIME ) ),
  1193.             "pst fails address check" );
  1194.  
  1195.     CheckTimezone(&st);
  1196.     SystemTimeToFileTime(&st, &ft);
  1197.     ft = FtSubFt(ft, fDaylight ? ftDaylightBias : ftStandardBias);
  1198.     FileTimeToSystemTime(&ft, pst);
  1199. }
  1200.  
  1201. /*
  1202.  *  GetLocalTime
  1203.  *
  1204.  *  Returns the current time as local time.
  1205.  *  This is easy: DOS keeps local time.
  1206.  */
  1207. void __export WINAPI
  1208. GetLocalTime(SYSTEMTIME FAR *pst)
  1209. {
  1210.     /* validate parameters */
  1211.     
  1212.     AssertSz( pst && !IsBadWritePtr( pst, sizeof( SYSTEMTIME ) ),
  1213.             "pst fails address check" );
  1214.             
  1215.     DOSTime(pst);
  1216. }
  1217.  
  1218. /* //$  Read-only data */
  1219. /* //$  TCHAR szFileTZ              = "msmail.ini"; //$ maybe? */
  1220. TCHAR   szSectionTZ[]           = "MAPI 1.0 Time Zone";
  1221. TCHAR   szKeyBias[]             = "Bias";
  1222. TCHAR   szKeyStandardName[]     = "StandardName";
  1223. TCHAR   szKeyStandardStart[]    = "StandardStart";
  1224. TCHAR   szKeyStandardBias[]     = "StandardBias";
  1225. TCHAR   szKeyDaylightName[]     = "DaylightName";
  1226. TCHAR   szKeyDaylightStart[]    = "DaylightStart";
  1227. TCHAR   szKeyDaylightBias[]     = "DaylightBias";
  1228. TCHAR   __szEmpty[]             = "";
  1229.  
  1230. /*
  1231.  *  Reads time zone parameters from WIN.INI, if they are present
  1232.  *  there. If they are not, tries the TZ environment variable.
  1233.  */
  1234. DWORD __export WINAPI
  1235. GetTimeZoneInformation(LPTIME_ZONE_INFORMATION ptz)
  1236. {
  1237.     int         cb;
  1238.     DWORD       dw;
  1239.     CHAR        rgchBias[12];
  1240.     CHAR        rgchSD[50];
  1241.     CHAR        rgchSB[12];
  1242.     CHAR        rgchDD[50];
  1243.     CHAR        rgchDB[12];
  1244.     SYSTEMTIME  st;
  1245.  
  1246.     Assert(!IsBadWritePtr(ptz, sizeof(TIME_ZONE_INFORMATION)));
  1247.     ZeroMemory(ptz, sizeof(TIME_ZONE_INFORMATION));
  1248.  
  1249.     /*  First check for the base bias. */
  1250.     if (GetProfileString(szSectionTZ, szKeyBias, __szEmpty,
  1251.         rgchBias, sizeof(rgchBias)))
  1252.     {
  1253.         ptz->Bias = (LONG) UlFromSzHex(rgchBias);
  1254.     }
  1255.     else if (!FParseTZ(ptz))
  1256.     {
  1257.         DebugTrace("GetTimeZoneInformation: using TZ\n");
  1258.     }
  1259.     else
  1260.     {
  1261.         /*  If the base bias is missing, don't worry about the rest. */
  1262.         DebugTrace("GetTimeZoneInformation: nothing.\n");
  1263.         dw = TIME_ZONE_ID_INVALID;
  1264.         goto ret;
  1265.     }
  1266.  
  1267.     /*  Check for full timezone info. */
  1268.     /*  Missing names don't matter. */
  1269.     GetProfileString(szSectionTZ, szKeyStandardName,
  1270.         __szEmpty, ptz->StandardName, sizeof(ptz->StandardName));
  1271.     GetProfileString(szSectionTZ, szKeyDaylightName,
  1272.         __szEmpty, ptz->DaylightName, sizeof(ptz->DaylightName));
  1273.     if (!(cb = GetProfileString(szSectionTZ, szKeyStandardStart,
  1274.             __szEmpty, rgchSD, sizeof(rgchSD))) ||
  1275.         !(cb = GetProfileString(szSectionTZ, szKeyStandardBias,
  1276.             __szEmpty, rgchSB, sizeof(rgchSB))) ||
  1277.         !(cb = GetProfileString(szSectionTZ, szKeyDaylightStart,
  1278.             __szEmpty, rgchDD, sizeof(rgchDD))) ||
  1279.         !(cb = GetProfileString(szSectionTZ, szKeyDaylightBias,
  1280.             __szEmpty, rgchDB, sizeof(rgchDB))))
  1281.     {
  1282.         DebugTrace("GetTimeZoneInformation: incomplete timezone info\n");
  1283.         /*  just use the basic bias */
  1284.     }
  1285.     else
  1286.     {
  1287.         /*  We got full config information. */
  1288.         ptz->StandardBias = (LONG) UlFromSzHex(rgchSB);
  1289.         ptz->DaylightBias = (LONG) UlFromSzHex(rgchDB);
  1290.         SideAssert(FBinFromHex(rgchSD, (LPBYTE)&ptz->StandardDate));
  1291.         SideAssert(FBinFromHex(rgchDD, (LPBYTE)&ptz->DaylightDate));
  1292.     }
  1293.  
  1294.     /*  If there is no difference in the biases, we don't know about */
  1295.     /*  DST. Otherwise, check whether we're currently in DST. */
  1296.     if (ptz->StandardBias == ptz->DaylightBias)
  1297.     {
  1298.         dw = TIME_ZONE_ID_UNKNOWN;
  1299.     }
  1300.     else
  1301.     {
  1302.         DOSTime(&st);
  1303.         dw = FDSTOfSystemTime(st, ptz) ?
  1304.             TIME_ZONE_ID_DAYLIGHT :
  1305.             TIME_ZONE_ID_STANDARD;
  1306.     }
  1307.  
  1308. ret:
  1309.     return dw;
  1310. }
  1311.  
  1312. /*
  1313.  *  Writes time zone parameters to WIN.INI.
  1314.  */
  1315. BOOL __export WINAPI
  1316. SetTimeZoneInformation(const TIME_ZONE_INFORMATION FAR *ptz)
  1317. {
  1318.     CHAR        rgchBias[12];
  1319.     CHAR        rgchSD[50];
  1320.     CHAR        rgchSB[12];
  1321.     CHAR        rgchDD[50];
  1322.     CHAR        rgchDB[12];
  1323.  
  1324.     if (!FValidTimeZoneInformation((TIME_ZONE_INFORMATION FAR *)ptz))
  1325.     {
  1326.         DebugTraceArg(SetTimeZoneInformation, "bogus TZ info");
  1327.         return FALSE;
  1328.     }
  1329.  
  1330.     wsprintf(rgchBias, "%lx", ptz->Bias);
  1331.     wsprintf(rgchSB, "%lx", ptz->StandardBias);
  1332.     wsprintf(rgchDB, "%lx", ptz->DaylightBias);
  1333.     HexFromBin((LPBYTE)&ptz->StandardDate, sizeof(SYSTEMTIME), rgchSD);
  1334.     HexFromBin((LPBYTE)&ptz->DaylightDate, sizeof(SYSTEMTIME), rgchDD);
  1335.  
  1336.     if ((!WriteProfileString(szSectionTZ, szKeyBias, rgchBias)) ||
  1337.         (!WriteProfileString(szSectionTZ, szKeyStandardName, ptz->StandardName)) ||
  1338.         (!WriteProfileString(szSectionTZ, szKeyStandardStart, rgchSD)) ||
  1339.         (!WriteProfileString(szSectionTZ, szKeyStandardBias, rgchSB)) ||
  1340.         (!WriteProfileString(szSectionTZ, szKeyDaylightName, ptz->DaylightName)) ||
  1341.         (!WriteProfileString(szSectionTZ, szKeyDaylightStart, rgchDD)) ||
  1342.         (!WriteProfileString(szSectionTZ, szKeyDaylightBias, rgchDB)))
  1343.     {
  1344.         DebugTrace("SetTimeZoneInformation: WriteProfileString failed\n");
  1345.         return FALSE;
  1346.     }
  1347.  
  1348.     return TRUE;
  1349. }
  1350.  
  1351. BOOL
  1352. FValidTimeZoneInformation(TIME_ZONE_INFORMATION FAR *ptz)
  1353. {
  1354.     if (IsBadReadPtr(ptz, sizeof(TIME_ZONE_INFORMATION)))
  1355.     {
  1356.         DebugTraceArg(FValidTimeZoneInformation, "ptz fails address check");
  1357.         return FALSE;
  1358.     }
  1359.     if (IsBadStringPtr(ptz->StandardName, sizeof(ptz->StandardName)) ||
  1360.         IsBadStringPtr(ptz->StandardName, sizeof(ptz->DaylightName)))
  1361.     {
  1362.         DebugTraceArg(FValidTimeZoneInformation, "one of two names fails address check");
  1363.         return FALSE;
  1364.     }
  1365.     if (!FValidBias(ptz->Bias) ||
  1366.         !FValidBias(ptz->DaylightBias) ||
  1367.         !FValidBias(ptz->StandardBias))
  1368.     {
  1369.         DebugTraceArg(FValidTimeZoneInformation, "one of three biases is out of range");
  1370.         return FALSE;
  1371.     }
  1372.     if (!FValidSysTime(&ptz->StandardDate, TRUE) ||
  1373.         !FValidSysTime(&ptz->DaylightDate, TRUE))
  1374.     {
  1375.         DebugTraceArg(FValidTimeZoneInformation, "one of two start dates is out of range");
  1376.         return FALSE;
  1377.     }
  1378.  
  1379.     return TRUE;
  1380. }
  1381.  
  1382. BOOL
  1383. FValidBias(LONG lBias)
  1384. {
  1385.     if (lBias > 0 && lBias >= 24*60)
  1386.         return FALSE;
  1387.     if (lBias < 0 && lBias <= -24*60)
  1388.         return FALSE;
  1389.  
  1390.     return TRUE;
  1391. }
  1392.  
  1393. BOOL
  1394. FValidSysTime(SYSTEMTIME FAR *pst, BOOL fRelativeOK)
  1395. {
  1396.     if (IsBadReadPtr(pst, sizeof(SYSTEMTIME)))
  1397.         return FALSE;
  1398.  
  1399.     if (pst->wYear < 1600)
  1400.     {
  1401.         if (pst->wYear == 0 && fRelativeOK)
  1402.         {
  1403.             /*  This is a weird relative time for Get/SetTZInfo; */
  1404.             /*  validate as such */
  1405.  
  1406.             if (pst->wMonth == 0)       /* nothing here */
  1407.                 return TRUE;
  1408.  
  1409.             if (pst->wMonth > 12 ||
  1410.                 pst->wDayOfWeek > 6 ||
  1411.                 (pst->wDay == 0 || pst->wDay > 5) ||
  1412.                 pst->wHour > 24 ||
  1413.                 pst->wMinute >= 60 ||
  1414.                 pst->wSecond >= 60)
  1415.                 return FALSE;
  1416.  
  1417.             return TRUE;
  1418.         }
  1419.         return FALSE;
  1420.     }
  1421.     if (pst->wMonth == 0 || pst->wMonth > 12)
  1422.         return FALSE;
  1423.     /*  don't validate day of week */
  1424.     if (pst->wDay == 0 || pst->wDay > 31)
  1425.         return FALSE;
  1426.     if (pst->wHour > 24)
  1427.         return FALSE;
  1428.     if (pst->wMinute >= 60)
  1429.         return FALSE;
  1430.     if (pst->wSecond >= 60)
  1431.         return FALSE;
  1432.     if (pst->wMilliseconds >= 1000)
  1433.         return FALSE;
  1434.  
  1435.     return TRUE;
  1436. }
  1437.  
  1438. #pragma warning (default: 4704)
  1439.  
  1440. #endif  /* WIN16 */
  1441.