- Remove deprecated CHECKPOINT/CHECKPOINT1 macros which basically translated into...
[reactos.git] / reactos / lib / rtl / time.c
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 int YearLengths[2] =
41 {
42 DAYSPERNORMALYEAR, DAYSPERLEAPYEAR
43 };
44 static const int 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
50 static __inline int IsLeapYear(int Year)
51 {
52 return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
53 }
54
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 static __inline void NormalizeTimeFields(CSHORT *FieldToNormalize,
65 CSHORT *CarryField,
66 int Modulus)
67 {
68 *FieldToNormalize = (CSHORT) (*FieldToNormalize - Modulus);
69 *CarryField = (CSHORT) (*CarryField + 1);
70 }
71
72 /* FUNCTIONS *****************************************************************/
73
74 /*
75 * @implemented
76 */
77 BOOLEAN NTAPI
78 RtlCutoverTimeToSystemTime(IN PTIME_FIELDS CutoverTimeFields,
79 OUT PLARGE_INTEGER SystemTime,
80 IN PLARGE_INTEGER CurrentTime,
81 IN BOOLEAN ThisYearsCutoverOnly)
82 {
83 TIME_FIELDS AdjustedTimeFields;
84 TIME_FIELDS CurrentTimeFields;
85 TIME_FIELDS CutoverSystemTimeFields;
86 LARGE_INTEGER CutoverSystemTime;
87 CSHORT MonthLength;
88 CSHORT Days;
89 BOOLEAN NextYearsCutover = FALSE;
90
91 /* Check fixed cutover time */
92 if (CutoverTimeFields->Year != 0)
93 {
94 if (!RtlTimeFieldsToTime(CutoverTimeFields, SystemTime))
95 return FALSE;
96
97 if (SystemTime->QuadPart < CurrentTime->QuadPart)
98 return FALSE;
99
100 return TRUE;
101 }
102
103 /*
104 * Compute recurring cutover time
105 */
106
107 /* Day must be between 1(first) and 5(last) */
108 if (CutoverTimeFields->Day == 0 || CutoverTimeFields->Day > 5)
109 return FALSE;
110
111 RtlTimeToTimeFields(CurrentTime, &CurrentTimeFields);
112
113 while (TRUE)
114 {
115 /* Compute the cutover time of the first day of the current month */
116 AdjustedTimeFields.Year = CurrentTimeFields.Year;
117 if (NextYearsCutover == TRUE)
118 AdjustedTimeFields.Year++;
119
120 AdjustedTimeFields.Month = CutoverTimeFields->Month;
121 AdjustedTimeFields.Day = 1;
122 AdjustedTimeFields.Hour = CutoverTimeFields->Hour;
123 AdjustedTimeFields.Minute = CutoverTimeFields->Minute;
124 AdjustedTimeFields.Second = CutoverTimeFields->Second;
125 AdjustedTimeFields.Milliseconds = CutoverTimeFields->Milliseconds;
126
127 if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
128 return FALSE;
129
130 RtlTimeToTimeFields(&CutoverSystemTime, &CutoverSystemTimeFields);
131
132 /* Adjust day to first matching weekday */
133 if (CutoverSystemTimeFields.Weekday != CutoverTimeFields->Weekday)
134 {
135 if (CutoverSystemTimeFields.Weekday < CutoverTimeFields->Weekday)
136 Days = CutoverTimeFields->Weekday - CutoverSystemTimeFields.Weekday;
137 else
138 Days = DAYSPERWEEK - (CutoverSystemTimeFields.Weekday - CutoverTimeFields->Weekday);
139
140 AdjustedTimeFields.Day += Days;
141 }
142
143 /* Adjust the number of weeks */
144 if (CutoverTimeFields->Day > 1)
145 {
146 Days = DAYSPERWEEK * (CutoverTimeFields->Day - 1);
147 MonthLength = MonthLengths[IsLeapYear(AdjustedTimeFields.Year)][AdjustedTimeFields.Month - 1];
148 if ((AdjustedTimeFields.Day + Days) > MonthLength)
149 Days -= DAYSPERWEEK;
150
151 AdjustedTimeFields.Day += Days;
152 }
153
154 if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
155 return FALSE;
156
157 if (ThisYearsCutoverOnly == TRUE ||
158 NextYearsCutover == TRUE ||
159 CutoverSystemTime.QuadPart >= CurrentTime->QuadPart)
160 {
161 break;
162 }
163
164 NextYearsCutover = TRUE;
165 }
166
167 SystemTime->QuadPart = CutoverSystemTime.QuadPart;
168
169 return TRUE;
170 }
171
172
173 /*
174 * @implemented
175 */
176 BOOLEAN
177 NTAPI
178 RtlTimeFieldsToTime(
179 IN PTIME_FIELDS TimeFields,
180 OUT PLARGE_INTEGER Time)
181 {
182 int CurMonth;
183 TIME_FIELDS IntTimeFields;
184
185 memcpy(&IntTimeFields,
186 TimeFields,
187 sizeof(TIME_FIELDS));
188
189 /* Normalize the TIME_FIELDS structure here */
190 while (IntTimeFields.Second >= SECSPERMIN)
191 {
192 NormalizeTimeFields(&IntTimeFields.Second,
193 &IntTimeFields.Minute,
194 SECSPERMIN);
195 }
196 while (IntTimeFields.Minute >= MINSPERHOUR)
197 {
198 NormalizeTimeFields(&IntTimeFields.Minute,
199 &IntTimeFields.Hour,
200 MINSPERHOUR);
201 }
202 while (IntTimeFields.Hour >= HOURSPERDAY)
203 {
204 NormalizeTimeFields(&IntTimeFields.Hour,
205 &IntTimeFields.Day,
206 HOURSPERDAY);
207 }
208 while (IntTimeFields.Day >
209 MonthLengths[IsLeapYear(IntTimeFields.Year)][IntTimeFields.Month - 1])
210 {
211 NormalizeTimeFields(&IntTimeFields.Day,
212 &IntTimeFields.Month,
213 SECSPERMIN);
214 }
215 while (IntTimeFields.Month > MONSPERYEAR)
216 {
217 NormalizeTimeFields(&IntTimeFields.Month,
218 &IntTimeFields.Year,
219 MONSPERYEAR);
220 }
221
222 /* Compute the time */
223 Time->QuadPart = DaysSinceEpoch(IntTimeFields.Year);
224 for (CurMonth = 1; CurMonth < IntTimeFields.Month; CurMonth++)
225 {
226 Time->QuadPart += MonthLengths[IsLeapYear(IntTimeFields.Year)][CurMonth - 1];
227 }
228 Time->QuadPart += IntTimeFields.Day - 1;
229 Time->QuadPart *= SECSPERDAY;
230 Time->QuadPart += IntTimeFields.Hour * SECSPERHOUR + IntTimeFields.Minute * SECSPERMIN +
231 IntTimeFields.Second;
232 Time->QuadPart *= TICKSPERSEC;
233 Time->QuadPart += IntTimeFields.Milliseconds * TICKSPERMSEC;
234
235 return TRUE;
236 }
237
238
239 /*
240 * @implemented
241 */
242 VOID
243 NTAPI
244 RtlTimeToElapsedTimeFields(IN PLARGE_INTEGER Time,
245 OUT PTIME_FIELDS TimeFields)
246 {
247 ULONGLONG ElapsedSeconds;
248 ULONG SecondsInDay;
249 ULONG SecondsInMinute;
250
251 /* Extract millisecond from time */
252 TimeFields->Milliseconds = (CSHORT)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
253
254 /* Compute elapsed seconds */
255 ElapsedSeconds = (ULONGLONG)Time->QuadPart / TICKSPERSEC;
256
257 /* Compute seconds within the day */
258 SecondsInDay = ElapsedSeconds % SECSPERDAY;
259
260 /* Compute elapsed minutes within the day */
261 SecondsInMinute = SecondsInDay % SECSPERHOUR;
262
263 /* Compute elapsed time of day */
264 TimeFields->Hour = (CSHORT)(SecondsInDay / SECSPERHOUR);
265 TimeFields->Minute = (CSHORT)(SecondsInMinute / SECSPERMIN);
266 TimeFields->Second = (CSHORT)(SecondsInMinute % SECSPERMIN);
267
268 /* Compute elapsed days */
269 TimeFields->Day = (CSHORT)(ElapsedSeconds / SECSPERDAY);
270
271 /* The elapsed number of months and days cannot be calculated */
272 TimeFields->Month = 0;
273 TimeFields->Year = 0;
274 }
275
276
277 /*
278 * @implemented
279 */
280 VOID
281 NTAPI
282 RtlTimeToTimeFields(
283 IN PLARGE_INTEGER Time,
284 OUT PTIME_FIELDS TimeFields)
285 {
286 const int *Months;
287 int SecondsInDay, CurYear;
288 int LeapYear, CurMonth;
289 long int Days;
290 LONGLONG IntTime = (LONGLONG)Time->QuadPart;
291
292 /* Extract millisecond from time and convert time into seconds */
293 TimeFields->Milliseconds = (CSHORT) ((IntTime % TICKSPERSEC) / TICKSPERMSEC);
294 IntTime = IntTime / TICKSPERSEC;
295
296 /* Split the time into days and seconds within the day */
297 Days = IntTime / SECSPERDAY;
298 SecondsInDay = IntTime % SECSPERDAY;
299
300 /* Adjust the values for days and seconds in day */
301 while (SecondsInDay < 0)
302 {
303 SecondsInDay += SECSPERDAY;
304 Days--;
305 }
306 while (SecondsInDay >= SECSPERDAY)
307 {
308 SecondsInDay -= SECSPERDAY;
309 Days++;
310 }
311
312 /* compute time of day */
313 TimeFields->Hour = (CSHORT) (SecondsInDay / SECSPERHOUR);
314 SecondsInDay = SecondsInDay % SECSPERHOUR;
315 TimeFields->Minute = (CSHORT) (SecondsInDay / SECSPERMIN);
316 TimeFields->Second = (CSHORT) (SecondsInDay % SECSPERMIN);
317
318 /* compute day of week */
319 TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
320
321 /* compute year */
322 CurYear = EPOCHYEAR;
323 CurYear += Days / DAYSPERLEAPYEAR;
324 Days -= DaysSinceEpoch(CurYear);
325 while (1)
326 {
327 LeapYear = IsLeapYear(CurYear);
328 if (Days < (long) YearLengths[LeapYear])
329 {
330 break;
331 }
332 CurYear++;
333 Days = Days - (long) YearLengths[LeapYear];
334 }
335 TimeFields->Year = (CSHORT) CurYear;
336
337 /* Compute month of year */
338 LeapYear = IsLeapYear(CurYear);
339 Months = MonthLengths[LeapYear];
340 for (CurMonth = 0; Days >= (long) Months[CurMonth]; CurMonth++)
341 Days = Days - (long) Months[CurMonth];
342 TimeFields->Month = (CSHORT) (CurMonth + 1);
343 TimeFields->Day = (CSHORT) (Days + 1);
344 }
345
346
347 /*
348 * @implemented
349 */
350 BOOLEAN
351 NTAPI
352 RtlTimeToSecondsSince1970(
353 IN PLARGE_INTEGER Time,
354 OUT PULONG SecondsSince1970)
355 {
356 LARGE_INTEGER IntTime;
357
358 IntTime.QuadPart = Time->QuadPart - TICKSTO1970;
359 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
360
361 if (IntTime.u.HighPart != 0)
362 return FALSE;
363
364 *SecondsSince1970 = IntTime.u.LowPart;
365
366 return TRUE;
367 }
368
369
370 /*
371 * @implemented
372 */
373 BOOLEAN
374 NTAPI
375 RtlTimeToSecondsSince1980(
376 IN PLARGE_INTEGER Time,
377 OUT PULONG SecondsSince1980)
378 {
379 LARGE_INTEGER IntTime;
380
381 IntTime.QuadPart = Time->QuadPart - TICKSTO1980;
382 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
383
384 if (IntTime.u.HighPart != 0)
385 return FALSE;
386
387 *SecondsSince1980 = IntTime.u.LowPart;
388
389 return TRUE;
390 }
391
392
393 /*
394 * @implemented
395 */
396 NTSTATUS
397 NTAPI
398 RtlLocalTimeToSystemTime(IN PLARGE_INTEGER LocalTime,
399 OUT PLARGE_INTEGER SystemTime)
400 {
401 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
402 NTSTATUS Status;
403
404 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
405 &TimeInformation,
406 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
407 NULL);
408 if (!NT_SUCCESS(Status))
409 return Status;
410
411 SystemTime->QuadPart = LocalTime->QuadPart +
412 TimeInformation.TimeZoneBias.QuadPart;
413
414 return STATUS_SUCCESS;
415 }
416
417
418 /*
419 * @implemented
420 */
421 NTSTATUS
422 NTAPI
423 RtlSystemTimeToLocalTime(IN PLARGE_INTEGER SystemTime,
424 OUT PLARGE_INTEGER LocalTime)
425 {
426 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
427 NTSTATUS Status;
428
429 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
430 &TimeInformation,
431 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
432 NULL);
433 if (!NT_SUCCESS(Status))
434 return Status;
435
436 LocalTime->QuadPart = SystemTime->QuadPart -
437 TimeInformation.TimeZoneBias.QuadPart;
438
439 return STATUS_SUCCESS;
440 }
441
442
443 /*
444 * @implemented
445 */
446 VOID NTAPI
447 RtlSecondsSince1970ToTime(
448 IN ULONG SecondsSince1970,
449 OUT PLARGE_INTEGER Time)
450 {
451 Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970;
452 }
453
454
455 /*
456 * @implemented
457 */
458 VOID NTAPI
459 RtlSecondsSince1980ToTime(
460 IN ULONG SecondsSince1980,
461 OUT PLARGE_INTEGER Time)
462 {
463 Time->QuadPart = ((LONGLONG)SecondsSince1980 * TICKSPERSEC) + TICKSTO1980;
464 }
465
466 /* EOF */