- DBGKD_WAIT_STATE_CHANGE64 is used in KD protocol 5, not number 6 that we use. Proto...
[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 if (TimeFields->Milliseconds < 0 || TimeFields->Milliseconds > 999 ||
190 TimeFields->Second < 0 || TimeFields->Second > 59 ||
191 TimeFields->Minute < 0 || TimeFields->Minute > 59 ||
192 TimeFields->Hour < 0 || TimeFields->Hour > 23 ||
193 TimeFields->Month < 1 || TimeFields->Month > 12 ||
194 TimeFields->Day < 1 ||
195 TimeFields->Day >
196 MonthLengths[IsLeapYear(TimeFields->Year)][TimeFields->Month - 1] ||
197 TimeFields->Year < 1601)
198 {
199 return FALSE;
200 }
201
202 /* Normalize the TIME_FIELDS structure here */
203 while (IntTimeFields.Second >= SECSPERMIN)
204 {
205 NormalizeTimeFields(&IntTimeFields.Second,
206 &IntTimeFields.Minute,
207 SECSPERMIN);
208 }
209 while (IntTimeFields.Minute >= MINSPERHOUR)
210 {
211 NormalizeTimeFields(&IntTimeFields.Minute,
212 &IntTimeFields.Hour,
213 MINSPERHOUR);
214 }
215 while (IntTimeFields.Hour >= HOURSPERDAY)
216 {
217 NormalizeTimeFields(&IntTimeFields.Hour,
218 &IntTimeFields.Day,
219 HOURSPERDAY);
220 }
221 while (IntTimeFields.Day >
222 MonthLengths[IsLeapYear(IntTimeFields.Year)][IntTimeFields.Month - 1])
223 {
224 NormalizeTimeFields(&IntTimeFields.Day,
225 &IntTimeFields.Month,
226 SECSPERMIN);
227 }
228 while (IntTimeFields.Month > MONSPERYEAR)
229 {
230 NormalizeTimeFields(&IntTimeFields.Month,
231 &IntTimeFields.Year,
232 MONSPERYEAR);
233 }
234
235 /* Compute the time */
236 Time->QuadPart = DaysSinceEpoch(IntTimeFields.Year);
237 for (CurMonth = 1; CurMonth < IntTimeFields.Month; CurMonth++)
238 {
239 Time->QuadPart += MonthLengths[IsLeapYear(IntTimeFields.Year)][CurMonth - 1];
240 }
241 Time->QuadPart += IntTimeFields.Day - 1;
242 Time->QuadPart *= SECSPERDAY;
243 Time->QuadPart += IntTimeFields.Hour * SECSPERHOUR + IntTimeFields.Minute * SECSPERMIN +
244 IntTimeFields.Second;
245 Time->QuadPart *= TICKSPERSEC;
246 Time->QuadPart += IntTimeFields.Milliseconds * TICKSPERMSEC;
247
248 return TRUE;
249 }
250
251
252 /*
253 * @implemented
254 */
255 VOID
256 NTAPI
257 RtlTimeToElapsedTimeFields(IN PLARGE_INTEGER Time,
258 OUT PTIME_FIELDS TimeFields)
259 {
260 ULONGLONG ElapsedSeconds;
261 ULONG SecondsInDay;
262 ULONG SecondsInMinute;
263
264 /* Extract millisecond from time */
265 TimeFields->Milliseconds = (CSHORT)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
266
267 /* Compute elapsed seconds */
268 ElapsedSeconds = (ULONGLONG)Time->QuadPart / TICKSPERSEC;
269
270 /* Compute seconds within the day */
271 SecondsInDay = ElapsedSeconds % SECSPERDAY;
272
273 /* Compute elapsed minutes within the day */
274 SecondsInMinute = SecondsInDay % SECSPERHOUR;
275
276 /* Compute elapsed time of day */
277 TimeFields->Hour = (CSHORT)(SecondsInDay / SECSPERHOUR);
278 TimeFields->Minute = (CSHORT)(SecondsInMinute / SECSPERMIN);
279 TimeFields->Second = (CSHORT)(SecondsInMinute % SECSPERMIN);
280
281 /* Compute elapsed days */
282 TimeFields->Day = (CSHORT)(ElapsedSeconds / SECSPERDAY);
283
284 /* The elapsed number of months and days cannot be calculated */
285 TimeFields->Month = 0;
286 TimeFields->Year = 0;
287 }
288
289
290 /*
291 * @implemented
292 */
293 VOID
294 NTAPI
295 RtlTimeToTimeFields(
296 IN PLARGE_INTEGER Time,
297 OUT PTIME_FIELDS TimeFields)
298 {
299 const int *Months;
300 int SecondsInDay, CurYear;
301 int LeapYear, CurMonth;
302 long int Days;
303 LONGLONG IntTime = (LONGLONG)Time->QuadPart;
304
305 /* Extract millisecond from time and convert time into seconds */
306 TimeFields->Milliseconds = (CSHORT) ((IntTime % TICKSPERSEC) / TICKSPERMSEC);
307 IntTime = IntTime / TICKSPERSEC;
308
309 /* Split the time into days and seconds within the day */
310 Days = IntTime / SECSPERDAY;
311 SecondsInDay = IntTime % SECSPERDAY;
312
313 /* Adjust the values for days and seconds in day */
314 while (SecondsInDay < 0)
315 {
316 SecondsInDay += SECSPERDAY;
317 Days--;
318 }
319 while (SecondsInDay >= SECSPERDAY)
320 {
321 SecondsInDay -= SECSPERDAY;
322 Days++;
323 }
324
325 /* compute time of day */
326 TimeFields->Hour = (CSHORT) (SecondsInDay / SECSPERHOUR);
327 SecondsInDay = SecondsInDay % SECSPERHOUR;
328 TimeFields->Minute = (CSHORT) (SecondsInDay / SECSPERMIN);
329 TimeFields->Second = (CSHORT) (SecondsInDay % SECSPERMIN);
330
331 /* compute day of week */
332 TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
333
334 /* compute year */
335 CurYear = EPOCHYEAR;
336 CurYear += Days / DAYSPERLEAPYEAR;
337 Days -= DaysSinceEpoch(CurYear);
338 while (1)
339 {
340 LeapYear = IsLeapYear(CurYear);
341 if (Days < (long) YearLengths[LeapYear])
342 {
343 break;
344 }
345 CurYear++;
346 Days = Days - (long) YearLengths[LeapYear];
347 }
348 TimeFields->Year = (CSHORT) CurYear;
349
350 /* Compute month of year */
351 LeapYear = IsLeapYear(CurYear);
352 Months = MonthLengths[LeapYear];
353 for (CurMonth = 0; Days >= (long) Months[CurMonth]; CurMonth++)
354 Days = Days - (long) Months[CurMonth];
355 TimeFields->Month = (CSHORT) (CurMonth + 1);
356 TimeFields->Day = (CSHORT) (Days + 1);
357 }
358
359
360 /*
361 * @implemented
362 */
363 BOOLEAN
364 NTAPI
365 RtlTimeToSecondsSince1970(
366 IN PLARGE_INTEGER Time,
367 OUT PULONG SecondsSince1970)
368 {
369 LARGE_INTEGER IntTime;
370
371 IntTime.QuadPart = Time->QuadPart - TICKSTO1970;
372 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
373
374 if (IntTime.u.HighPart != 0)
375 return FALSE;
376
377 *SecondsSince1970 = IntTime.u.LowPart;
378
379 return TRUE;
380 }
381
382
383 /*
384 * @implemented
385 */
386 BOOLEAN
387 NTAPI
388 RtlTimeToSecondsSince1980(
389 IN PLARGE_INTEGER Time,
390 OUT PULONG SecondsSince1980)
391 {
392 LARGE_INTEGER IntTime;
393
394 IntTime.QuadPart = Time->QuadPart - TICKSTO1980;
395 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
396
397 if (IntTime.u.HighPart != 0)
398 return FALSE;
399
400 *SecondsSince1980 = IntTime.u.LowPart;
401
402 return TRUE;
403 }
404
405
406 /*
407 * @implemented
408 */
409 NTSTATUS
410 NTAPI
411 RtlLocalTimeToSystemTime(IN PLARGE_INTEGER LocalTime,
412 OUT PLARGE_INTEGER SystemTime)
413 {
414 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
415 NTSTATUS Status;
416
417 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
418 &TimeInformation,
419 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
420 NULL);
421 if (!NT_SUCCESS(Status))
422 return Status;
423
424 SystemTime->QuadPart = LocalTime->QuadPart +
425 TimeInformation.TimeZoneBias.QuadPart;
426
427 return STATUS_SUCCESS;
428 }
429
430
431 /*
432 * @implemented
433 */
434 NTSTATUS
435 NTAPI
436 RtlSystemTimeToLocalTime(IN PLARGE_INTEGER SystemTime,
437 OUT PLARGE_INTEGER LocalTime)
438 {
439 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
440 NTSTATUS Status;
441
442 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
443 &TimeInformation,
444 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
445 NULL);
446 if (!NT_SUCCESS(Status))
447 return Status;
448
449 LocalTime->QuadPart = SystemTime->QuadPart -
450 TimeInformation.TimeZoneBias.QuadPart;
451
452 return STATUS_SUCCESS;
453 }
454
455
456 /*
457 * @implemented
458 */
459 VOID NTAPI
460 RtlSecondsSince1970ToTime(
461 IN ULONG SecondsSince1970,
462 OUT PLARGE_INTEGER Time)
463 {
464 Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970;
465 }
466
467
468 /*
469 * @implemented
470 */
471 VOID NTAPI
472 RtlSecondsSince1980ToTime(
473 IN ULONG SecondsSince1980,
474 OUT PLARGE_INTEGER Time)
475 {
476 Time->QuadPart = ((LONGLONG)SecondsSince1980 * TICKSPERSEC) + TICKSTO1980;
477 }
478
479 /* EOF */