xref: /reactos/sdk/lib/rtl/time.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS system libraries
4  * FILE:              lib/rtl/time.c
5  * PURPOSE:           Conversion between Time and TimeFields
6  * PROGRAMMER:        Rex Jolliff (rex@lvcablemodem.com)
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <rtl.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define TICKSPERMIN        600000000
17 #define TICKSPERSEC        10000000
18 #define TICKSPERMSEC       10000
19 #define SECSPERDAY         86400
20 #define SECSPERHOUR        3600
21 #define SECSPERMIN         60
22 #define MINSPERHOUR        60
23 #define HOURSPERDAY        24
24 #define EPOCHWEEKDAY       1
25 #define DAYSPERWEEK        7
26 #define EPOCHYEAR          1601
27 #define DAYSPERNORMALYEAR  365
28 #define DAYSPERLEAPYEAR    366
29 #define MONSPERYEAR        12
30 
31 #if defined(__GNUC__)
32 #define TICKSTO1970         0x019db1ded53e8000LL
33 #define TICKSTO1980         0x01a8e79fe1d58000LL
34 #else
35 #define TICKSTO1970         0x019db1ded53e8000i64
36 #define TICKSTO1980         0x01a8e79fe1d58000i64
37 #endif
38 
39 
40 static const unsigned int YearLengths[2] =
41 {
42     DAYSPERNORMALYEAR, DAYSPERLEAPYEAR
43 };
44 static const UCHAR MonthLengths[2][MONSPERYEAR] =
45 {
46     { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
47     { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
48 };
49 
IsLeapYear(int Year)50 static __inline int IsLeapYear(int Year)
51 {
52     return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
53 }
54 
DaysSinceEpoch(int Year)55 static int DaysSinceEpoch(int Year)
56 {
57     int Days;
58     Year--; /* Don't include a leap day from the current year */
59     Days = Year * DAYSPERNORMALYEAR + Year / 4 - Year / 100 + Year / 400;
60     Days -= (EPOCHYEAR - 1) * DAYSPERNORMALYEAR + (EPOCHYEAR - 1) / 4 - (EPOCHYEAR - 1) / 100 + (EPOCHYEAR - 1) / 400;
61     return Days;
62 }
63 
64 /* FUNCTIONS *****************************************************************/
65 
66 /*
67  * @implemented
68  */
69 BOOLEAN NTAPI
RtlCutoverTimeToSystemTime(IN PTIME_FIELDS CutoverTimeFields,OUT PLARGE_INTEGER SystemTime,IN PLARGE_INTEGER CurrentTime,IN BOOLEAN ThisYearsCutoverOnly)70 RtlCutoverTimeToSystemTime(IN PTIME_FIELDS CutoverTimeFields,
71                            OUT PLARGE_INTEGER SystemTime,
72                            IN PLARGE_INTEGER CurrentTime,
73                            IN BOOLEAN ThisYearsCutoverOnly)
74 {
75     TIME_FIELDS AdjustedTimeFields;
76     TIME_FIELDS CurrentTimeFields;
77     TIME_FIELDS CutoverSystemTimeFields;
78     LARGE_INTEGER CutoverSystemTime;
79     UCHAR MonthLength;
80     CSHORT Days;
81     BOOLEAN NextYearsCutover = FALSE;
82 
83     /* Check fixed cutover time */
84     if (CutoverTimeFields->Year != 0)
85     {
86         if (!RtlTimeFieldsToTime(CutoverTimeFields, SystemTime))
87             return FALSE;
88 
89         if (SystemTime->QuadPart < CurrentTime->QuadPart)
90             return FALSE;
91 
92         return TRUE;
93     }
94 
95     /*
96      * Compute recurring cutover time
97      */
98 
99     /* Day must be between 1(first) and 5(last) */
100     if (CutoverTimeFields->Day == 0 || CutoverTimeFields->Day > 5)
101         return FALSE;
102 
103     RtlTimeToTimeFields(CurrentTime, &CurrentTimeFields);
104 
105     while (TRUE)
106     {
107         /* Compute the cutover time of the first day of the current month */
108         AdjustedTimeFields.Year = CurrentTimeFields.Year;
109         if (NextYearsCutover)
110             AdjustedTimeFields.Year++;
111 
112         AdjustedTimeFields.Month = CutoverTimeFields->Month;
113         AdjustedTimeFields.Day = 1;
114         AdjustedTimeFields.Hour = CutoverTimeFields->Hour;
115         AdjustedTimeFields.Minute = CutoverTimeFields->Minute;
116         AdjustedTimeFields.Second = CutoverTimeFields->Second;
117         AdjustedTimeFields.Milliseconds = CutoverTimeFields->Milliseconds;
118 
119         if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
120             return FALSE;
121 
122         RtlTimeToTimeFields(&CutoverSystemTime, &CutoverSystemTimeFields);
123 
124         /* Adjust day to first matching weekday */
125         if (CutoverSystemTimeFields.Weekday != CutoverTimeFields->Weekday)
126         {
127             if (CutoverSystemTimeFields.Weekday < CutoverTimeFields->Weekday)
128                 Days = CutoverTimeFields->Weekday - CutoverSystemTimeFields.Weekday;
129             else
130                 Days = DAYSPERWEEK - (CutoverSystemTimeFields.Weekday - CutoverTimeFields->Weekday);
131 
132             AdjustedTimeFields.Day += Days;
133         }
134 
135         /* Adjust the number of weeks */
136         if (CutoverTimeFields->Day > 1)
137         {
138             Days = DAYSPERWEEK * (CutoverTimeFields->Day - 1);
139             MonthLength = MonthLengths[IsLeapYear(AdjustedTimeFields.Year)][AdjustedTimeFields.Month - 1];
140             if ((AdjustedTimeFields.Day + Days) > MonthLength)
141                 Days -= DAYSPERWEEK;
142 
143             AdjustedTimeFields.Day += Days;
144         }
145 
146         if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
147             return FALSE;
148 
149         if (ThisYearsCutoverOnly ||
150             NextYearsCutover ||
151             CutoverSystemTime.QuadPart >= CurrentTime->QuadPart)
152         {
153             break;
154         }
155 
156         NextYearsCutover = TRUE;
157     }
158 
159     SystemTime->QuadPart = CutoverSystemTime.QuadPart;
160 
161     return TRUE;
162 }
163 
164 
165 /*
166  * @implemented
167  */
168 BOOLEAN
169 NTAPI
RtlTimeFieldsToTime(IN PTIME_FIELDS TimeFields,OUT PLARGE_INTEGER Time)170 RtlTimeFieldsToTime(IN PTIME_FIELDS TimeFields,
171                     OUT PLARGE_INTEGER Time)
172 {
173     ULONG CurMonth;
174     TIME_FIELDS IntTimeFields;
175 
176     RtlCopyMemory(&IntTimeFields,
177                   TimeFields,
178                   sizeof(TIME_FIELDS));
179 
180     if (TimeFields->Milliseconds < 0 || TimeFields->Milliseconds > 999 ||
181         TimeFields->Second < 0 || TimeFields->Second > 59 ||
182         TimeFields->Minute < 0 || TimeFields->Minute > 59 ||
183         TimeFields->Hour < 0 || TimeFields->Hour > 23 ||
184         TimeFields->Month < 1 || TimeFields->Month > 12 ||
185         TimeFields->Day < 1 ||
186         TimeFields->Day >
187             MonthLengths[IsLeapYear(TimeFields->Year)][TimeFields->Month - 1] ||
188         TimeFields->Year < 1601)
189     {
190         return FALSE;
191     }
192 
193     /* Compute the time */
194     Time->QuadPart = DaysSinceEpoch(IntTimeFields.Year);
195     for (CurMonth = 1; CurMonth < IntTimeFields.Month; CurMonth++)
196     {
197         Time->QuadPart += MonthLengths[IsLeapYear(IntTimeFields.Year)][CurMonth - 1];
198     }
199     Time->QuadPart += IntTimeFields.Day - 1;
200     Time->QuadPart *= SECSPERDAY;
201     Time->QuadPart += IntTimeFields.Hour * SECSPERHOUR + IntTimeFields.Minute * SECSPERMIN +
202                       IntTimeFields.Second;
203     Time->QuadPart *= TICKSPERSEC;
204     Time->QuadPart += IntTimeFields.Milliseconds * TICKSPERMSEC;
205 
206     return TRUE;
207 }
208 
209 
210 /*
211  * @implemented
212  */
213 VOID
214 NTAPI
RtlTimeToElapsedTimeFields(IN PLARGE_INTEGER Time,OUT PTIME_FIELDS TimeFields)215 RtlTimeToElapsedTimeFields(IN PLARGE_INTEGER Time,
216                            OUT PTIME_FIELDS TimeFields)
217 {
218     ULONGLONG ElapsedSeconds;
219     ULONG SecondsInDay;
220     ULONG SecondsInMinute;
221 
222     /* Extract millisecond from time */
223     TimeFields->Milliseconds = (CSHORT)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
224 
225     /* Compute elapsed seconds */
226     ElapsedSeconds = (ULONGLONG)Time->QuadPart / TICKSPERSEC;
227 
228     /* Compute seconds within the day */
229     SecondsInDay = ElapsedSeconds % SECSPERDAY;
230 
231     /* Compute elapsed minutes within the day */
232     SecondsInMinute = SecondsInDay % SECSPERHOUR;
233 
234     /* Compute elapsed time of day */
235     TimeFields->Hour = (CSHORT)(SecondsInDay / SECSPERHOUR);
236     TimeFields->Minute = (CSHORT)(SecondsInMinute / SECSPERMIN);
237     TimeFields->Second = (CSHORT)(SecondsInMinute % SECSPERMIN);
238 
239     /* Compute elapsed days */
240     TimeFields->Day = (CSHORT)(ElapsedSeconds / SECSPERDAY);
241 
242     /* The elapsed number of months and days cannot be calculated */
243     TimeFields->Month = 0;
244     TimeFields->Year = 0;
245 }
246 
247 
248 /*
249  * @implemented
250  */
251 VOID
252 NTAPI
RtlTimeToTimeFields(IN PLARGE_INTEGER Time,OUT PTIME_FIELDS TimeFields)253 RtlTimeToTimeFields(IN PLARGE_INTEGER Time,
254                     OUT PTIME_FIELDS TimeFields)
255 {
256     const UCHAR *Months;
257     ULONG SecondsInDay, CurYear;
258     ULONG LeapYear, CurMonth;
259     ULONG Days;
260     ULONGLONG IntTime = Time->QuadPart;
261 
262     /* Extract millisecond from time and convert time into seconds */
263     TimeFields->Milliseconds = (CSHORT)((IntTime % TICKSPERSEC) / TICKSPERMSEC);
264     IntTime = IntTime / TICKSPERSEC;
265 
266     /* Split the time into days and seconds within the day */
267     Days = (ULONG)(IntTime / SECSPERDAY);
268     SecondsInDay = IntTime % SECSPERDAY;
269 
270     /* Compute time of day */
271     TimeFields->Hour = (CSHORT)(SecondsInDay / SECSPERHOUR);
272     SecondsInDay = SecondsInDay % SECSPERHOUR;
273     TimeFields->Minute = (CSHORT)(SecondsInDay / SECSPERMIN);
274     TimeFields->Second = (CSHORT)(SecondsInDay % SECSPERMIN);
275 
276     /* Compute day of week */
277     TimeFields->Weekday = (CSHORT)((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
278 
279     /* Compute year */
280     CurYear = EPOCHYEAR;
281     CurYear += Days / DAYSPERLEAPYEAR;
282     Days -= DaysSinceEpoch(CurYear);
283     while (TRUE)
284     {
285         LeapYear = IsLeapYear(CurYear);
286         if (Days < YearLengths[LeapYear])
287         {
288             break;
289         }
290         CurYear++;
291         Days = Days - YearLengths[LeapYear];
292     }
293     TimeFields->Year = (CSHORT)CurYear;
294 
295     /* Compute month of year */
296     LeapYear = IsLeapYear(CurYear);
297     Months = MonthLengths[LeapYear];
298     for (CurMonth = 0; Days >= Months[CurMonth]; CurMonth++)
299     {
300         Days = Days - Months[CurMonth];
301     }
302     TimeFields->Month = (CSHORT)(CurMonth + 1);
303     TimeFields->Day = (CSHORT)(Days + 1);
304 }
305 
306 
307 /*
308  * @implemented
309  */
310 BOOLEAN
311 NTAPI
RtlTimeToSecondsSince1970(IN PLARGE_INTEGER Time,OUT PULONG SecondsSince1970)312 RtlTimeToSecondsSince1970(IN PLARGE_INTEGER Time,
313                           OUT PULONG SecondsSince1970)
314 {
315     LARGE_INTEGER IntTime;
316 
317     IntTime.QuadPart = Time->QuadPart - TICKSTO1970;
318     IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
319 
320     if (IntTime.u.HighPart != 0)
321         return FALSE;
322 
323     *SecondsSince1970 = IntTime.u.LowPart;
324 
325     return TRUE;
326 }
327 
328 
329 /*
330  * @implemented
331  */
332 BOOLEAN
333 NTAPI
RtlTimeToSecondsSince1980(IN PLARGE_INTEGER Time,OUT PULONG SecondsSince1980)334 RtlTimeToSecondsSince1980(IN PLARGE_INTEGER Time,
335                           OUT PULONG SecondsSince1980)
336 {
337     LARGE_INTEGER IntTime;
338 
339     IntTime.QuadPart = Time->QuadPart - TICKSTO1980;
340     IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
341 
342     if (IntTime.u.HighPart != 0)
343         return FALSE;
344 
345     *SecondsSince1980 = IntTime.u.LowPart;
346 
347     return TRUE;
348 }
349 
350 
351 /*
352  * @implemented
353  */
354 NTSTATUS
355 NTAPI
RtlLocalTimeToSystemTime(IN PLARGE_INTEGER LocalTime,OUT PLARGE_INTEGER SystemTime)356 RtlLocalTimeToSystemTime(IN PLARGE_INTEGER LocalTime,
357                          OUT PLARGE_INTEGER SystemTime)
358 {
359     SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
360     NTSTATUS Status;
361 
362     Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
363                                       &TimeInformation,
364                                       sizeof(TimeInformation),
365                                       NULL);
366     if (!NT_SUCCESS(Status))
367         return Status;
368 
369     SystemTime->QuadPart = LocalTime->QuadPart +
370                            TimeInformation.TimeZoneBias.QuadPart;
371 
372     return STATUS_SUCCESS;
373 }
374 
375 
376 /*
377  * @implemented
378  */
379 NTSTATUS
380 NTAPI
RtlSystemTimeToLocalTime(IN PLARGE_INTEGER SystemTime,OUT PLARGE_INTEGER LocalTime)381 RtlSystemTimeToLocalTime(IN PLARGE_INTEGER SystemTime,
382                          OUT PLARGE_INTEGER LocalTime)
383 {
384     SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
385     NTSTATUS Status;
386 
387     Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
388                                       &TimeInformation,
389                                       sizeof(TimeInformation),
390                                       NULL);
391     if (!NT_SUCCESS(Status))
392         return Status;
393 
394     LocalTime->QuadPart = SystemTime->QuadPart -
395                           TimeInformation.TimeZoneBias.QuadPart;
396 
397     return STATUS_SUCCESS;
398 }
399 
400 
401 /*
402  * @implemented
403  */
404 VOID
405 NTAPI
RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970,OUT PLARGE_INTEGER Time)406 RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970,
407                           OUT PLARGE_INTEGER Time)
408 {
409     Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970;
410 }
411 
412 
413 /*
414  * @implemented
415  */
416 VOID NTAPI
RtlSecondsSince1980ToTime(IN ULONG SecondsSince1980,OUT PLARGE_INTEGER Time)417 RtlSecondsSince1980ToTime(IN ULONG SecondsSince1980,
418                           OUT PLARGE_INTEGER Time)
419 {
420     Time->QuadPart = ((LONGLONG)SecondsSince1980 * TICKSPERSEC) + TICKSTO1980;
421 }
422 
423 /* EOF */
424