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