- Implement cases 2 & 4 of RtlSplayTree
[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 __inline void NormalizeTimeFields(CSHORT *FieldToNormalize,
56 CSHORT *CarryField,
57 int Modulus)
58 {
59 *FieldToNormalize = (CSHORT) (*FieldToNormalize - Modulus);
60 *CarryField = (CSHORT) (*CarryField + 1);
61 }
62
63 /* FUNCTIONS *****************************************************************/
64
65 /*
66 * @implemented
67 */
68 BOOLEAN NTAPI
69 RtlCutoverTimeToSystemTime(IN PTIME_FIELDS CutoverTimeFields,
70 OUT PLARGE_INTEGER SystemTime,
71 IN PLARGE_INTEGER CurrentTime,
72 IN BOOLEAN ThisYearsCutoverOnly)
73 {
74 TIME_FIELDS AdjustedTimeFields;
75 TIME_FIELDS CurrentTimeFields;
76 TIME_FIELDS CutoverSystemTimeFields;
77 LARGE_INTEGER CutoverSystemTime;
78 CSHORT MonthLength;
79 CSHORT Days;
80 BOOLEAN NextYearsCutover = FALSE;
81
82 /* Check fixed cutover time */
83 if (CutoverTimeFields->Year != 0)
84 {
85 if (!RtlTimeFieldsToTime(CutoverTimeFields, SystemTime))
86 return FALSE;
87
88 if (SystemTime->QuadPart < CurrentTime->QuadPart)
89 return FALSE;
90
91 return TRUE;
92 }
93
94 /*
95 * Compute recurring cutover time
96 */
97
98 /* Day must be between 1(first) and 5(last) */
99 if (CutoverTimeFields->Day == 0 || CutoverTimeFields->Day > 5)
100 return FALSE;
101
102 RtlTimeToTimeFields(CurrentTime, &CurrentTimeFields);
103
104 while (TRUE)
105 {
106 /* Compute the cutover time of the first day of the current month */
107 AdjustedTimeFields.Year = CurrentTimeFields.Year;
108 if (NextYearsCutover == TRUE)
109 AdjustedTimeFields.Year++;
110
111 AdjustedTimeFields.Month = CutoverTimeFields->Month;
112 AdjustedTimeFields.Day = 1;
113 AdjustedTimeFields.Hour = CutoverTimeFields->Hour;
114 AdjustedTimeFields.Minute = CutoverTimeFields->Minute;
115 AdjustedTimeFields.Second = CutoverTimeFields->Second;
116 AdjustedTimeFields.Milliseconds = CutoverTimeFields->Milliseconds;
117
118 if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
119 return FALSE;
120
121 RtlTimeToTimeFields(&CutoverSystemTime, &CutoverSystemTimeFields);
122
123 /* Adjust day to first matching weekday */
124 if (CutoverSystemTimeFields.Weekday != CutoverTimeFields->Weekday)
125 {
126 if (CutoverSystemTimeFields.Weekday < CutoverTimeFields->Weekday)
127 Days = CutoverTimeFields->Weekday - CutoverSystemTimeFields.Weekday;
128 else
129 Days = DAYSPERWEEK - (CutoverSystemTimeFields.Weekday - CutoverTimeFields->Weekday);
130
131 AdjustedTimeFields.Day += Days;
132 }
133
134 /* Adjust the number of weeks */
135 if (CutoverTimeFields->Day > 1)
136 {
137 Days = DAYSPERWEEK * (CutoverTimeFields->Day - 1);
138 MonthLength = MonthLengths[IsLeapYear(AdjustedTimeFields.Year)][AdjustedTimeFields.Month - 1];
139 if ((AdjustedTimeFields.Day + Days) > MonthLength)
140 Days -= DAYSPERWEEK;
141
142 AdjustedTimeFields.Day += Days;
143 }
144
145 if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
146 return FALSE;
147
148 if (ThisYearsCutoverOnly == TRUE ||
149 NextYearsCutover == TRUE ||
150 CutoverSystemTime.QuadPart >= CurrentTime->QuadPart)
151 {
152 break;
153 }
154
155 NextYearsCutover = TRUE;
156 }
157
158 SystemTime->QuadPart = CutoverSystemTime.QuadPart;
159
160 return TRUE;
161 }
162
163
164 /*
165 * @implemented
166 */
167 BOOLEAN
168 NTAPI
169 RtlTimeFieldsToTime(
170 IN PTIME_FIELDS TimeFields,
171 OUT PLARGE_INTEGER Time)
172 {
173 int CurYear;
174 int CurMonth;
175 TIME_FIELDS IntTimeFields;
176
177 Time->QuadPart = 0;
178 memcpy(&IntTimeFields,
179 TimeFields,
180 sizeof(TIME_FIELDS));
181
182 /* Normalize the TIME_FIELDS structure here */
183 while (IntTimeFields.Second >= SECSPERMIN)
184 {
185 NormalizeTimeFields(&IntTimeFields.Second,
186 &IntTimeFields.Minute,
187 SECSPERMIN);
188 }
189 while (IntTimeFields.Minute >= MINSPERHOUR)
190 {
191 NormalizeTimeFields(&IntTimeFields.Minute,
192 &IntTimeFields.Hour,
193 MINSPERHOUR);
194 }
195 while (IntTimeFields.Hour >= HOURSPERDAY)
196 {
197 NormalizeTimeFields(&IntTimeFields.Hour,
198 &IntTimeFields.Day,
199 HOURSPERDAY);
200 }
201 while (IntTimeFields.Day >
202 MonthLengths[IsLeapYear(IntTimeFields.Year)][IntTimeFields.Month - 1])
203 {
204 NormalizeTimeFields(&IntTimeFields.Day,
205 &IntTimeFields.Month,
206 SECSPERMIN);
207 }
208 while (IntTimeFields.Month > MONSPERYEAR)
209 {
210 NormalizeTimeFields(&IntTimeFields.Month,
211 &IntTimeFields.Year,
212 MONSPERYEAR);
213 }
214
215 /* Compute the time */
216 for (CurYear = EPOCHYEAR; CurYear < IntTimeFields.Year; CurYear++)
217 {
218 Time->QuadPart += YearLengths[IsLeapYear(CurYear)];
219 }
220 for (CurMonth = 1; CurMonth < IntTimeFields.Month; CurMonth++)
221 {
222 Time->QuadPart += MonthLengths[IsLeapYear(CurYear)][CurMonth - 1];
223 }
224 Time->QuadPart += IntTimeFields.Day - 1;
225 Time->QuadPart *= SECSPERDAY;
226 Time->QuadPart += IntTimeFields.Hour * SECSPERHOUR + IntTimeFields.Minute * SECSPERMIN +
227 IntTimeFields.Second;
228 Time->QuadPart *= TICKSPERSEC;
229 Time->QuadPart += IntTimeFields.Milliseconds * TICKSPERMSEC;
230
231 return TRUE;
232 }
233
234
235 /*
236 * @implemented
237 */
238 VOID
239 NTAPI
240 RtlTimeToElapsedTimeFields(IN PLARGE_INTEGER Time,
241 OUT PTIME_FIELDS TimeFields)
242 {
243 ULONGLONG ElapsedSeconds;
244 ULONG SecondsInDay;
245 ULONG SecondsInMinute;
246
247 /* Extract millisecond from time */
248 TimeFields->Milliseconds = (CSHORT)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
249
250 /* Compute elapsed seconds */
251 ElapsedSeconds = (ULONGLONG)Time->QuadPart / TICKSPERSEC;
252
253 /* Compute seconds within the day */
254 SecondsInDay = ElapsedSeconds % SECSPERDAY;
255
256 /* Compute elapsed minutes within the day */
257 SecondsInMinute = SecondsInDay % SECSPERHOUR;
258
259 /* Compute elapsed time of day */
260 TimeFields->Hour = (CSHORT)(SecondsInDay / SECSPERHOUR);
261 TimeFields->Minute = (CSHORT)(SecondsInMinute / SECSPERMIN);
262 TimeFields->Second = (CSHORT)(SecondsInMinute % SECSPERMIN);
263
264 /* Compute elapsed days */
265 TimeFields->Day = (CSHORT)(ElapsedSeconds / SECSPERDAY);
266
267 /* The elapsed number of months and days cannot be calculated */
268 TimeFields->Month = 0;
269 TimeFields->Year = 0;
270 }
271
272
273 /*
274 * @implemented
275 */
276 VOID
277 NTAPI
278 RtlTimeToTimeFields(
279 IN PLARGE_INTEGER Time,
280 OUT PTIME_FIELDS TimeFields)
281 {
282 const int *Months;
283 int SecondsInDay, CurYear;
284 int LeapYear, CurMonth;
285 long int Days;
286 LONGLONG IntTime = (LONGLONG)Time->QuadPart;
287
288 /* Extract millisecond from time and convert time into seconds */
289 TimeFields->Milliseconds = (CSHORT) ((IntTime % TICKSPERSEC) / TICKSPERMSEC);
290 IntTime = IntTime / TICKSPERSEC;
291
292 /* Split the time into days and seconds within the day */
293 Days = IntTime / SECSPERDAY;
294 SecondsInDay = IntTime % SECSPERDAY;
295
296 /* Adjust the values for days and seconds in day */
297 while (SecondsInDay < 0)
298 {
299 SecondsInDay += SECSPERDAY;
300 Days--;
301 }
302 while (SecondsInDay >= SECSPERDAY)
303 {
304 SecondsInDay -= SECSPERDAY;
305 Days++;
306 }
307
308 /* compute time of day */
309 TimeFields->Hour = (CSHORT) (SecondsInDay / SECSPERHOUR);
310 SecondsInDay = SecondsInDay % SECSPERHOUR;
311 TimeFields->Minute = (CSHORT) (SecondsInDay / SECSPERMIN);
312 TimeFields->Second = (CSHORT) (SecondsInDay % SECSPERMIN);
313
314 /* compute day of week */
315 TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
316
317 /* compute year */
318 CurYear = EPOCHYEAR;
319 CurYear += Days / DAYSPERLEAPYEAR;
320 Days -= (CurYear - EPOCHYEAR) * DAYSPERLEAPYEAR;
321 CurYear--; /* The next calculation needs CurYear - 1 */
322 Days += CurYear - CurYear / 4 + CurYear / 100 - CurYear / 400;
323 CurYear++;
324 Days -= EPOCHYEAR - 1 - (EPOCHYEAR -1) / 4 + (EPOCHYEAR -1) / 100 - (EPOCHYEAR - 1) / 400;
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 */