summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/rtl/time.c')
-rw-r--r--private/ntos/rtl/time.c1332
1 files changed, 1332 insertions, 0 deletions
diff --git a/private/ntos/rtl/time.c b/private/ntos/rtl/time.c
new file mode 100644
index 000000000..1b73ded35
--- /dev/null
+++ b/private/ntos/rtl/time.c
@@ -0,0 +1,1332 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Time.c
+
+Abstract:
+
+ This module implements the absolute time conversion routines for NT.
+
+ Absolute LARGE_INTEGER in NT is represented by a 64-bit large integer accurate
+ to 100ns resolution. The smallest time resolution used by this package
+ is One millisecond. The basis for NT time is the start of 1601 which
+ was chosen because it is the start of a new quadricentury. Some facts
+ to note are:
+
+ o At 100ns resolution 32 bits is good for about 429 seconds (or 7 minutes)
+
+ o At 100ns resolution a large integer (i.e., 63 bits) is good for
+ about 29,247 years, or around 10,682,247 days.
+
+ o At 1 second resolution 31 bits is good for about 68 years
+
+ o At 1 second resolution 32 bits is good for about 136 years
+
+ o 100ns Time (ignoring time less than a millisecond) can be expressed
+ as two values, Days and Milliseconds. Where Days is the number of
+ whole days and Milliseconds is the number of milliseconds for the
+ partial day. Both of these values are ULONG.
+
+ Given these facts most of the conversions are done by first splitting
+ LARGE_INTEGER into Days and Milliseconds.
+
+Author:
+
+ Gary Kimura [GaryKi] 26-Aug-1989
+
+Environment:
+
+ Pure utility routine
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE, RtlCutoverTimeToSystemTime)
+#pragma alloc_text(PAGE, RtlTimeToElapsedTimeFields)
+#endif
+
+
+//
+// The following two tables map a day offset within a year to the month
+// containing the day. Both tables are zero based. For example, day
+// offset of 0 to 30 map to 0 (which is Jan).
+//
+
+UCHAR LeapYearDayToMonth[366] = {
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+
+UCHAR NormalYearDayToMonth[365] = {
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+
+//
+// The following two tables map a month index to the number of days preceding
+// the month in the year. Both tables are zero based. For example, 1 (Feb)
+// has 31 days preceding it. To help calculate the maximum number of days
+// in a month each table has 13 entries, so the number of days in a month
+// of index i is the table entry of i+1 minus the table entry of i.
+//
+
+CSHORT LeapYearDaysPrecedingMonth[13] = {
+ 0, // January
+ 31, // February
+ 31+29, // March
+ 31+29+31, // April
+ 31+29+31+30, // May
+ 31+29+31+30+31, // June
+ 31+29+31+30+31+30, // July
+ 31+29+31+30+31+30+31, // August
+ 31+29+31+30+31+30+31+31, // September
+ 31+29+31+30+31+30+31+31+30, // October
+ 31+29+31+30+31+30+31+31+30+31, // November
+ 31+29+31+30+31+30+31+31+30+31+30, // December
+ 31+29+31+30+31+30+31+31+30+31+30+31};
+
+CSHORT NormalYearDaysPrecedingMonth[13] = {
+ 0, // January
+ 31, // February
+ 31+28, // March
+ 31+28+31, // April
+ 31+28+31+30, // May
+ 31+28+31+30+31, // June
+ 31+28+31+30+31+30, // July
+ 31+28+31+30+31+30+31, // August
+ 31+28+31+30+31+30+31+31, // September
+ 31+28+31+30+31+30+31+31+30, // October
+ 31+28+31+30+31+30+31+31+30+31, // November
+ 31+28+31+30+31+30+31+31+30+31+30, // December
+ 31+28+31+30+31+30+31+31+30+31+30+31};
+
+
+//
+// The following definitions and declarations are some important constants
+// used in the time conversion routines
+//
+
+//
+// This is the week day that January 1st, 1601 fell on (a Monday)
+//
+
+#define WEEKDAY_OF_1601 1
+
+//
+// These are known constants used to convert 1970 and 1980 times to 1601
+// times. They are the number of seconds from the 1601 base to the start
+// of 1970 and the start of 1980. The number of seconds from 1601 to
+// 1970 is 369 years worth, or (369 * 365) + 89 leap days = 134774 days, or
+// 134774 * 864000 seconds, which is equal to the large integer defined
+// below. The number of seconds from 1601 to 1980 is 379 years worth, or etc.
+//
+
+LARGE_INTEGER SecondsToStartOf1970 = {0xb6109100, 0x00000002};
+
+LARGE_INTEGER SecondsToStartOf1980 = {0xc8df3700, 0x00000002};
+
+//
+// These are the magic numbers needed to do our extended division. The
+// only numbers we ever need to divide by are
+//
+// 10,000 = convert 100ns tics to millisecond tics
+//
+// 10,000,000 = convert 100ns tics to one second tics
+//
+// 86,400,000 = convert Millisecond tics to one day tics
+//
+
+LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
+#define SHIFT10000 13
+
+LARGE_INTEGER Magic10000000 = {0xe57a42bd, 0xd6bf94d5};
+#define SHIFT10000000 23
+
+LARGE_INTEGER Magic86400000 = {0xfa67b90e, 0xc6d750eb};
+#define SHIFT86400000 26
+
+//
+// To make the code more readable we'll also define some macros to
+// do the actual division for use
+//
+
+#define Convert100nsToMilliseconds(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000, SHIFT10000 ) \
+ )
+
+#define ConvertMillisecondsTo100ns(MILLISECONDS) ( \
+ RtlExtendedIntegerMultiply( (MILLISECONDS), 10000 ) \
+ )
+
+#define Convert100nsToSeconds(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000000, SHIFT10000000 ) \
+ )
+
+#define ConvertSecondsTo100ns(SECONDS) ( \
+ RtlExtendedIntegerMultiply( (SECONDS), 10000000 ) \
+ )
+
+#define ConvertMillisecondsToDays(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic86400000, SHIFT86400000 ) \
+ )
+
+#define ConvertDaysToMilliseconds(DAYS) ( \
+ Int32x32To64( (DAYS), 86400000 ) \
+ )
+
+
+//
+// Local support routine
+//
+
+ULONG
+ElapsedDaysToYears (
+ IN ULONG ElapsedDays
+ )
+
+/*++
+
+Routine Description:
+
+ This routine computes the number of total years contained in the indicated
+ number of elapsed days. The computation is to first compute the number of
+ 400 years and subtract that it, then do the 100 years and subtract that out,
+ then do the number of 4 years and subtract that out. Then what we have left
+ is the number of days with in a normalized 4 year block. Normalized being that
+ the first three years are not leap years.
+
+Arguments:
+
+ ElapsedDays - Supplies the number of days to use
+
+Return Value:
+
+ ULONG - Returns the number of whole years contained within the input number
+ of days.
+
+--*/
+
+{
+ ULONG NumberOf400s;
+ ULONG NumberOf100s;
+ ULONG NumberOf4s;
+ ULONG Years;
+
+ //
+ // A 400 year time block is 365*400 + 400/4 - 400/100 + 400/400 = 146097 days
+ // long. So we simply compute the number of whole 400 year block and the
+ // the number days contained in those whole blocks, and subtract if from the
+ // elapsed day total
+ //
+
+ NumberOf400s = ElapsedDays / 146097;
+ ElapsedDays -= NumberOf400s * 146097;
+
+ //
+ // A 100 year time block is 365*100 + 100/4 - 100/100 = 36524 days long.
+ // The computation for the number of 100 year blocks is biased by 3/4 days per
+ // 100 years to account for the extra leap day thrown in on the last year
+ // of each 400 year block.
+ //
+
+ NumberOf100s = (ElapsedDays * 100 + 75) / 3652425;
+ ElapsedDays -= NumberOf100s * 36524;
+
+ //
+ // A 4 year time block is 365*4 + 4/4 = 1461 days long.
+ //
+
+ NumberOf4s = ElapsedDays / 1461;
+ ElapsedDays -= NumberOf4s * 1461;
+
+ //
+ // Now the number of whole years is the number of 400 year blocks times 400,
+ // 100 year blocks time 100, 4 year blocks times 4, and the number of elapsed
+ // whole years, taking into account the 3/4 day per year needed to handle the
+ // leap year.
+ //
+
+ Years = (NumberOf400s * 400) +
+ (NumberOf100s * 100) +
+ (NumberOf4s * 4) +
+ (ElapsedDays * 100 + 75) / 36525;
+
+ return Years;
+}
+
+
+//
+// ULONG
+// NumberOfLeapYears (
+// IN ULONG ElapsedYears
+// );
+//
+// The number of leap years is simply the number of years divided by 4
+// minus years divided by 100 plus years divided by 400. This says
+// that every four years is a leap year except centuries, and the
+// exception to the exception is the quadricenturies
+//
+
+#define NumberOfLeapYears(YEARS) ( \
+ ((YEARS) / 4) - ((YEARS) / 100) + ((YEARS) / 400) \
+ )
+
+//
+// ULONG
+// ElapsedYearsToDays (
+// IN ULONG ElapsedYears
+// );
+//
+// The number of days contained in elapsed years is simply the number
+// of years times 365 (because every year has at least 365 days) plus
+// the number of leap years there are (i.e., the number of 366 days years)
+//
+
+#define ElapsedYearsToDays(YEARS) ( \
+ ((YEARS) * 365) + NumberOfLeapYears(YEARS) \
+ )
+
+//
+// BOOLEAN
+// IsLeapYear (
+// IN ULONG ElapsedYears
+// );
+//
+// If it is an even 400 or a non century leapyear then the
+// answer is true otherwise it's false
+//
+
+#define IsLeapYear(YEARS) ( \
+ (((YEARS) % 400 == 0) || \
+ ((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
+ TRUE \
+ : \
+ FALSE \
+ )
+
+//
+// ULONG
+// MaxDaysInMonth (
+// IN ULONG Year,
+// IN ULONG Month
+// );
+//
+// The maximum number of days in a month depend on the year and month.
+// It is the difference between the days to the month and the days
+// to the following month
+//
+
+#define MaxDaysInMonth(YEAR,MONTH) ( \
+ IsLeapYear(YEAR) ? \
+ LeapYearDaysPrecedingMonth[(MONTH) + 1] - \
+ LeapYearDaysPrecedingMonth[(MONTH)] \
+ : \
+ NormalYearDaysPrecedingMonth[(MONTH) + 1] - \
+ NormalYearDaysPrecedingMonth[(MONTH)] \
+ )
+
+
+
+//
+// Internal Support routine
+//
+
+static
+VOID
+TimeToDaysAndFraction (
+ IN PLARGE_INTEGER Time,
+ OUT PULONG ElapsedDays,
+ OUT PULONG Milliseconds
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit time value to the number
+ of total elapsed days and the number of milliseconds in the
+ partial day.
+
+Arguments:
+
+ Time - Supplies the input time to convert from
+
+ ElapsedDays - Receives the number of elapsed days
+
+ Milliseconds - Receives the number of milliseconds in the partial day
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER TotalMilliseconds;
+ LARGE_INTEGER Temp;
+
+ //
+ // Convert the input time to total milliseconds
+ //
+
+ TotalMilliseconds = Convert100nsToMilliseconds( *(PLARGE_INTEGER)Time );
+
+ //
+ // Convert milliseconds to total days
+ //
+
+ Temp = ConvertMillisecondsToDays( TotalMilliseconds );
+
+ //
+ // Set the elapsed days from temp, we've divided it enough so that
+ // the high part must be zero.
+ //
+
+ *ElapsedDays = Temp.LowPart;
+
+ //
+ // Calculate the exact number of milliseconds in the elapsed days
+ // and subtract that from the total milliseconds to figure out
+ // the number of milliseconds left in the partial day
+ //
+
+ Temp.QuadPart = ConvertDaysToMilliseconds( *ElapsedDays );
+
+ Temp.QuadPart = TotalMilliseconds.QuadPart - Temp.QuadPart;
+
+ //
+ // Set the fraction part from temp, the total number of milliseconds in
+ // a day guarantees that the high part must be zero.
+ //
+
+ *Milliseconds = Temp.LowPart;
+
+ //
+ // And return to our caller
+ //
+
+ return;
+}
+
+
+//
+// Internal Support routine
+//
+
+//static
+VOID
+DaysAndFractionToTime (
+ IN ULONG ElapsedDays,
+ IN ULONG Milliseconds,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input elapsed day count and partial time
+ in milliseconds to a 64-bit time value.
+
+Arguments:
+
+ ElapsedDays - Supplies the number of elapsed days
+
+ Milliseconds - Supplies the number of milliseconds in the partial day
+
+ Time - Receives the output time to value
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER Temp;
+ LARGE_INTEGER Temp2;
+
+ //
+ // Calculate the exact number of milliseconds in the elapsed days.
+ //
+
+ Temp.QuadPart = ConvertDaysToMilliseconds( ElapsedDays );
+
+ //
+ // Convert milliseconds to a large integer
+ //
+
+ Temp2.LowPart = Milliseconds;
+ Temp2.HighPart = 0;
+
+ //
+ // add milliseconds to the whole day milliseconds
+ //
+
+ Temp.QuadPart = Temp.QuadPart + Temp2.QuadPart;
+
+ //
+ // Finally convert the milliseconds to 100ns resolution
+ //
+
+ *(PLARGE_INTEGER)Time = ConvertMillisecondsTo100ns( Temp );
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+
+VOID
+RtlTimeToTimeFields (
+ IN PLARGE_INTEGER Time,
+ OUT PTIME_FIELDS TimeFields
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit LARGE_INTEGER variable to its corresponding
+ time field record. It will tell the caller the year, month, day, hour,
+ minute, second, millisecond, and weekday corresponding to the input time
+ variable.
+
+Arguments:
+
+ Time - Supplies the time value to interpret
+
+ TimeFields - Receives a value corresponding to Time
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG Years;
+ ULONG Month;
+ ULONG Days;
+
+ ULONG Hours;
+ ULONG Minutes;
+ ULONG Seconds;
+ ULONG Milliseconds;
+
+ //
+ // First divide the input time 64 bit time variable into
+ // the number of whole days and part days (in milliseconds)
+ //
+
+ TimeToDaysAndFraction( Time, &Days, &Milliseconds );
+
+ //
+ // Compute which weekday it is and save it away now in the output
+ // variable. We add the weekday of the base day to bias our computation
+ // which means that if one day has elapsed then we the weekday we want
+ // is the Jan 2nd, 1601.
+ //
+
+ TimeFields->Weekday = (CSHORT)((Days + WEEKDAY_OF_1601) % 7);
+
+ //
+ // Calculate the number of whole years contained in the elapsed days
+ // For example if Days = 500 then Years = 1
+ //
+
+ Years = ElapsedDaysToYears( Days );
+
+ //
+ // And subtract the number of whole years from our elapsed days
+ // For example if Days = 500, Years = 1, and the new days is equal
+ // to 500 - 365 (normal year).
+ //
+
+ Days = Days - ElapsedYearsToDays( Years );
+
+ //
+ // Now test whether the year we are working on (i.e., The year
+ // after the total number of elapsed years) is a leap year
+ // or not.
+ //
+
+ if (IsLeapYear( Years + 1 )) {
+
+ //
+ // The current year is a leap year, so figure out what month
+ // it is, and then subtract the number of days preceding the
+ // month from the days to figure out what day of the month it is
+ //
+
+ Month = LeapYearDayToMonth[Days];
+ Days = Days - LeapYearDaysPrecedingMonth[Month];
+
+ } else {
+
+ //
+ // The current year is a normal year, so figure out the month
+ // and days as described above for the leap year case
+ //
+
+ Month = NormalYearDayToMonth[Days];
+ Days = Days - NormalYearDaysPrecedingMonth[Month];
+
+ }
+
+ //
+ // Now we need to compute the elapsed hour, minute, second, milliseconds
+ // from the millisecond variable. This variable currently contains
+ // the number of milliseconds in our input time variable that did not
+ // fit into a whole day. To compute the hour, minute, second part
+ // we will actually do the arithmetic backwards computing milliseconds
+ // seconds, minutes, and then hours. We start by computing the
+ // number of whole seconds left in the day, and then computing
+ // the millisecond remainder.
+ //
+
+ Seconds = Milliseconds / 1000;
+ Milliseconds = Milliseconds % 1000;
+
+ //
+ // Now we compute the number of whole minutes left in the day
+ // and the number of remainder seconds
+ //
+
+ Minutes = Seconds / 60;
+ Seconds = Seconds % 60;
+
+ //
+ // Now compute the number of whole hours left in the day
+ // and the number of remainder minutes
+ //
+
+ Hours = Minutes / 60;
+ Minutes = Minutes % 60;
+
+ //
+ // As our final step we put everything into the time fields
+ // output variable
+ //
+
+ TimeFields->Year = (CSHORT)(Years + 1601);
+ TimeFields->Month = (CSHORT)(Month + 1);
+ TimeFields->Day = (CSHORT)(Days + 1);
+ TimeFields->Hour = (CSHORT)Hours;
+ TimeFields->Minute = (CSHORT)Minutes;
+ TimeFields->Second = (CSHORT)Seconds;
+ TimeFields->Milliseconds = (CSHORT)Milliseconds;
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+BOOLEAN
+RtlCutoverTimeToSystemTime(
+ PTIME_FIELDS CutoverTime,
+ PLARGE_INTEGER SystemTime,
+ PLARGE_INTEGER CurrentSystemTime,
+ BOOLEAN ThisYear
+ )
+{
+ TIME_FIELDS CurrentTimeFields;
+
+ //
+ // Get the current system time
+ //
+
+ RtlTimeToTimeFields(CurrentSystemTime,&CurrentTimeFields);
+
+ //
+ // check for absolute time field. If the year is specified,
+ // the the time is an abosulte time
+ //
+
+ if ( CutoverTime->Year ) {
+
+ //
+ // Convert this to a time value and make sure it
+ // is greater than the current system time
+ //
+
+ if ( !RtlTimeFieldsToTime(CutoverTime,SystemTime) ) {
+ return FALSE;
+ }
+
+ if (SystemTime->QuadPart < CurrentSystemTime->QuadPart) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+ else {
+
+ TIME_FIELDS WorkingTimeField;
+ TIME_FIELDS ScratchTimeField;
+ LARGE_INTEGER ScratchTime;
+ CSHORT BestWeekdayDate;
+ CSHORT WorkingWeekdayNumber;
+ CSHORT TargetWeekdayNumber;
+ CSHORT TargetYear;
+ CSHORT TargetMonth;
+ CSHORT TargetWeekday; // range [0..6] == [Sunday..Saturday]
+ BOOLEAN MonthMatches;
+ //
+ // The time is an day in the month style time
+ //
+ // the convention is the Day is 1-5 specifying 1st, 2nd... Last
+ // day within the month. The day is WeekDay.
+ //
+
+ //
+ // Compute the target month and year
+ //
+
+ TargetWeekdayNumber = CutoverTime->Day;
+ if ( TargetWeekdayNumber > 5 || TargetWeekdayNumber == 0 ) {
+ return FALSE;
+ }
+ TargetWeekday = CutoverTime->Weekday;
+ TargetMonth = CutoverTime->Month;
+ MonthMatches = FALSE;
+ if ( !ThisYear ) {
+ if ( TargetMonth < CurrentTimeFields.Month ) {
+ TargetYear = CurrentTimeFields.Year + 1;
+ }
+ else if ( TargetMonth > CurrentTimeFields.Month ) {
+ TargetYear = CurrentTimeFields.Year;
+ }
+ else {
+ TargetYear = CurrentTimeFields.Year;
+ MonthMatches = TRUE;
+ }
+ }
+ else {
+ TargetYear = CurrentTimeFields.Year;
+ }
+try_next_year:
+ BestWeekdayDate = 0;
+
+ WorkingTimeField.Year = TargetYear;
+ WorkingTimeField.Month = TargetMonth;
+ WorkingTimeField.Day = 1;
+ WorkingTimeField.Hour = CutoverTime->Hour;
+ WorkingTimeField.Minute = CutoverTime->Minute;
+ WorkingTimeField.Second = CutoverTime->Second;
+ WorkingTimeField.Milliseconds = CutoverTime->Milliseconds;
+ WorkingTimeField.Weekday = 0;
+
+ //
+ // Convert to time and then back to time fields so we can determine
+ // the weekday of day 1 on the month
+ //
+
+ if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
+ return FALSE;
+ }
+ RtlTimeToTimeFields(&ScratchTime,&ScratchTimeField);
+
+ //
+ // Compute bias to target weekday
+ //
+ if ( ScratchTimeField.Weekday > TargetWeekday ) {
+ WorkingTimeField.Day += (7-(ScratchTimeField.Weekday - TargetWeekday));
+ }
+ else if ( ScratchTimeField.Weekday < TargetWeekday ) {
+ WorkingTimeField.Day += (TargetWeekday - ScratchTimeField.Weekday);
+ }
+
+ //
+ // We are now at the first weekday that matches our target weekday
+ //
+
+ BestWeekdayDate = WorkingTimeField.Day;
+ WorkingWeekdayNumber = 1;
+
+ //
+ // Keep going one week at a time until we either pass the
+ // target weekday, or we match exactly
+ //
+
+ while ( WorkingWeekdayNumber < TargetWeekdayNumber ) {
+ WorkingTimeField.Day += 7;
+ if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
+ break;
+ }
+ RtlTimeToTimeFields(&ScratchTime,&ScratchTimeField);
+ WorkingWeekdayNumber++;
+ BestWeekdayDate = ScratchTimeField.Day;
+ }
+ WorkingTimeField.Day = BestWeekdayDate;
+
+ //
+ // If the months match, and the date is less than the current
+ // date, then be have to go to next year.
+ //
+
+ if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
+ return FALSE;
+ }
+ if ( MonthMatches ) {
+ if ( WorkingTimeField.Day < CurrentTimeFields.Day ) {
+ MonthMatches = FALSE;
+ TargetYear++;
+ goto try_next_year;
+ }
+ if ( WorkingTimeField.Day == CurrentTimeFields.Day ) {
+
+ if (ScratchTime.QuadPart < CurrentSystemTime->QuadPart) {
+ MonthMatches = FALSE;
+ TargetYear++;
+ goto try_next_year;
+ }
+ }
+ }
+ *SystemTime = ScratchTime;
+
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+RtlTimeFieldsToTime (
+ IN PTIME_FIELDS TimeFields,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input Time Field variable to a 64-bit NT time
+ value. It ignores the WeekDay of the time field.
+
+Arguments:
+
+ TimeFields - Supplies the time field record to use
+
+ Time - Receives the NT Time corresponding to TimeFields
+
+Return Value:
+
+ BOOLEAN - TRUE if the Time Fields is well formed and within the
+ range of time expressible by LARGE_INTEGER and FALSE otherwise.
+
+--*/
+
+{
+ ULONG Year;
+ ULONG Month;
+ ULONG Day;
+ ULONG Hour;
+ ULONG Minute;
+ ULONG Second;
+ ULONG Milliseconds;
+
+ ULONG ElapsedDays;
+ ULONG ElapsedMilliseconds;
+
+ //
+ // Load the time field elements into local variables. This should
+ // ensure that the compiler will only load the input elements
+ // once, even if there are alias problems. It will also make
+ // everything (except the year) zero based. We cannot zero base the
+ // year because then we can't recognize cases where we're given a year
+ // before 1601.
+ //
+
+ Year = TimeFields->Year;
+ Month = TimeFields->Month - 1;
+ Day = TimeFields->Day - 1;
+ Hour = TimeFields->Hour;
+ Minute = TimeFields->Minute;
+ Second = TimeFields->Second;
+ Milliseconds = TimeFields->Milliseconds;
+
+ //
+ // Check that the time field input variable contains
+ // proper values.
+ //
+
+ if ((TimeFields->Month < 1) ||
+ (TimeFields->Day < 1) ||
+ (Year < 1601) ||
+ (Month > 11) ||
+ ((CSHORT)Day >= MaxDaysInMonth(Year, Month)) ||
+ (Hour > 23) ||
+ (Minute > 59) ||
+ (Second > 59) ||
+ (Milliseconds > 999)) {
+
+ return FALSE;
+
+ }
+
+ //
+ // Compute the total number of elapsed days represented by the
+ // input time field variable
+ //
+
+ ElapsedDays = ElapsedYearsToDays( Year - 1601 );
+
+ if (IsLeapYear( Year - 1600 )) {
+
+ ElapsedDays += LeapYearDaysPrecedingMonth[ Month ];
+
+ } else {
+
+ ElapsedDays += NormalYearDaysPrecedingMonth[ Month ];
+
+ }
+
+ ElapsedDays += Day;
+
+ //
+ // Now compute the total number of milliseconds in the fractional
+ // part of the day
+ //
+
+ ElapsedMilliseconds = (((Hour*60) + Minute)*60 + Second)*1000 + Milliseconds;
+
+ //
+ // Given the elapsed days and milliseconds we can now build
+ // the output time variable
+ //
+
+ DaysAndFractionToTime( ElapsedDays, ElapsedMilliseconds, Time );
+
+ //
+ // And return to our caller
+ //
+
+ return TRUE;
+}
+
+
+VOID
+RtlTimeToElapsedTimeFields (
+ IN PLARGE_INTEGER Time,
+ OUT PTIME_FIELDS TimeFields
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit LARGE_INTEGER variable to its corresponding
+ time field record. The input time is the elapsed time (difference
+ between to times). It will tell the caller the number of days, hour,
+ minute, second, and milliseconds that the elapsed time represents.
+
+Arguments:
+
+ Time - Supplies the time value to interpret
+
+ TimeFields - Receives a value corresponding to Time
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG Days;
+ ULONG Hours;
+ ULONG Minutes;
+ ULONG Seconds;
+ ULONG Milliseconds;
+
+ //
+ // First divide the input time 64 bit time variable into
+ // the number of whole days and part days (in milliseconds)
+ //
+
+ TimeToDaysAndFraction( Time, &Days, &Milliseconds );
+
+ //
+ // Now we need to compute the elapsed hour, minute, second, milliseconds
+ // from the millisecond variable. This variable currently contains
+ // the number of milliseconds in our input time variable that did not
+ // fit into a whole day. To compute the hour, minute, second part
+ // we will actually do the arithmetic backwards computing milliseconds
+ // seconds, minutes, and then hours. We start by computing the
+ // number of whole seconds left in the day, and then computing
+ // the millisecond remainder.
+ //
+
+ Seconds = Milliseconds / 1000;
+ Milliseconds = Milliseconds % 1000;
+
+ //
+ // Now we compute the number of whole minutes left in the day
+ // and the number of remainder seconds
+ //
+
+ Minutes = Seconds / 60;
+ Seconds = Seconds % 60;
+
+ //
+ // Now compute the number of whole hours left in the day
+ // and the number of remainder minutes
+ //
+
+ Hours = Minutes / 60;
+ Minutes = Minutes % 60;
+
+ //
+ // As our final step we put everything into the time fields
+ // output variable
+ //
+
+ TimeFields->Year = 0;
+ TimeFields->Month = 0;
+ TimeFields->Day = (CSHORT)Days;
+ TimeFields->Hour = (CSHORT)Hours;
+ TimeFields->Minute = (CSHORT)Minutes;
+ TimeFields->Second = (CSHORT)Seconds;
+ TimeFields->Milliseconds = (CSHORT)Milliseconds;
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+
+BOOLEAN
+RtlTimeToSecondsSince1980 (
+ IN PLARGE_INTEGER Time,
+ OUT PULONG ElapsedSeconds
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit NT Time variable to the
+ number of seconds since the start of 1980. The NT time must be
+ within the range 1980 to around 2115.
+
+Arguments:
+
+ Time - Supplies the Time to convert from
+
+ ElapsedSeconds - Receives the number of seconds since the start of 1980
+ denoted by Time
+
+Return Value:
+
+ BOOLEAN - TRUE if the input Time is within a range expressible by
+ ElapsedSeconds and FALSE otherwise
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // First convert time to seconds since 1601
+ //
+
+ Seconds = Convert100nsToSeconds( *(PLARGE_INTEGER)Time );
+
+ //
+ // Then subtract the number of seconds from 1601 to 1980.
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart - SecondsToStartOf1980.QuadPart;
+
+ //
+ // If the results is negative then the date was before 1980 or if
+ // the results is greater than a ulong then its too far in the
+ // future so we return FALSE
+ //
+
+ if (Seconds.HighPart != 0) {
+
+ return FALSE;
+
+ }
+
+ //
+ // Otherwise we have the answer
+ //
+
+ *ElapsedSeconds = Seconds.LowPart;
+
+ //
+ // And return to our caller
+ //
+
+ return TRUE;
+}
+
+
+VOID
+RtlSecondsSince1980ToTime (
+ IN ULONG ElapsedSeconds,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts the seconds since the start of 1980 to an
+ NT Time value.
+
+Arguments:
+
+ ElapsedSeconds - Supplies the number of seconds from the start of 1980
+ to convert from
+
+ Time - Receives the converted Time value
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // Move elapsed seconds to a large integer
+ //
+
+ Seconds.LowPart = ElapsedSeconds;
+ Seconds.HighPart = 0;
+
+ //
+ // convert number of seconds from 1980 to number of seconds from 1601
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart + SecondsToStartOf1980.QuadPart;
+
+ //
+ // Convert seconds to 100ns resolution
+ //
+
+ *(PLARGE_INTEGER)Time = ConvertSecondsTo100ns( Seconds );
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+
+BOOLEAN
+RtlTimeToSecondsSince1970 (
+ IN PLARGE_INTEGER Time,
+ OUT PULONG ElapsedSeconds
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit NT Time variable to the
+ number of seconds since the start of 1970. The NT time must be
+ within the range 1970 to around 2105.
+
+Arguments:
+
+ Time - Supplies the Time to convert from
+
+ ElapsedSeconds - Receives the number of seconds since the start of 1970
+ denoted by Time
+
+Return Value:
+
+ BOOLEAN - TRUE if the input time is within the range expressible by
+ ElapsedSeconds and FALSE otherwise
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // First convert time to seconds since 1601
+ //
+
+ Seconds = Convert100nsToSeconds( *(PLARGE_INTEGER)Time );
+
+ //
+ // Then subtract the number of seconds from 1601 to 1970.
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart - SecondsToStartOf1970.QuadPart;
+
+ //
+ // If the results is negative then the date was before 1970 or if
+ // the results is greater than a ulong then its too far in the
+ // future so we return FALSE
+ //
+
+ if (Seconds.HighPart != 0) {
+
+ return FALSE;
+
+ }
+
+ //
+ // Otherwise we have the answer
+ //
+
+ *ElapsedSeconds = Seconds.LowPart;
+
+ //
+ // And return to our caller
+ //
+
+ return TRUE;
+}
+
+
+VOID
+RtlSecondsSince1970ToTime (
+ IN ULONG ElapsedSeconds,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts the seconds since the start of 1970 to an
+ NT Time value
+
+Arguments:
+
+ ElapsedSeconds - Supplies the number of seconds from the start of 1970
+ to convert from
+
+ Time - Receives the converted Time value
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // Move elapsed seconds to a large integer
+ //
+
+ Seconds.LowPart = ElapsedSeconds;
+ Seconds.HighPart = 0;
+
+ //
+ // Convert number of seconds from 1970 to number of seconds from 1601
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart + SecondsToStartOf1970.QuadPart;
+
+ //
+ // Convert seconds to 100ns resolution
+ //
+
+ *(PLARGE_INTEGER)Time = ConvertSecondsTo100ns( Seconds );
+
+ //
+ // return to our caller
+ //
+
+ return;
+}
+
+NTSTATUS
+RtlSystemTimeToLocalTime (
+ IN PLARGE_INTEGER SystemTime,
+ OUT PLARGE_INTEGER LocalTime
+ )
+{
+ NTSTATUS Status;
+ SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
+
+ Status = ZwQuerySystemInformation(
+ SystemTimeOfDayInformation,
+ &TimeOfDay,
+ sizeof(TimeOfDay),
+ NULL
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // LocalTime = SystemTime - TimeZoneBias
+ //
+
+ LocalTime->QuadPart = SystemTime->QuadPart - TimeOfDay.TimeZoneBias.QuadPart;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+RtlLocalTimeToSystemTime (
+ IN PLARGE_INTEGER LocalTime,
+ OUT PLARGE_INTEGER SystemTime
+ )
+{
+
+ NTSTATUS Status;
+ SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
+
+ Status = ZwQuerySystemInformation(
+ SystemTimeOfDayInformation,
+ &TimeOfDay,
+ sizeof(TimeOfDay),
+ NULL
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // SystemTime = LocalTime + TimeZoneBias
+ //
+
+ SystemTime->QuadPart = LocalTime->QuadPart + TimeOfDay.TimeZoneBias.QuadPart;
+
+ return STATUS_SUCCESS;
+}