fixed RtlCompareUnicodeString and RtlCompareString (said stuff were equal when it...
[reactos.git] / reactos / lib / rtl / time.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: lib/rtl/time.c
6 * PURPOSE: Conversion between Time and TimeFields
7 * PROGRAMMER: Rex Jolliff (rex@lvcablemodem.com)
8 * UPDATE HISTORY:
9 * Created 22/05/98
10 * 08/03/98 RJJ Implemented these functions
11 */
12
13 /* INCLUDES *****************************************************************/
14
15 #include <rtl.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 #define TICKSPERMIN 600000000
21 #define TICKSPERSEC 10000000
22 #define TICKSPERMSEC 10000
23 #define SECSPERDAY 86400
24 #define SECSPERHOUR 3600
25 #define SECSPERMIN 60
26 #define MINSPERHOUR 60
27 #define HOURSPERDAY 24
28 #define EPOCHWEEKDAY 1
29 #define DAYSPERWEEK 7
30 #define EPOCHYEAR 1601
31 #define DAYSPERNORMALYEAR 365
32 #define DAYSPERLEAPYEAR 366
33 #define MONSPERYEAR 12
34
35 #if defined(__GNUC__)
36 #define TICKSTO1970 0x019db1ded53e8000LL
37 #define TICKSTO1980 0x01a8e79fe1d58000LL
38 #else
39 #define TICKSTO1970 0x019db1ded53e8000i64
40 #define TICKSTO1980 0x01a8e79fe1d58000i64
41 #endif
42
43
44 static const int YearLengths[2] =
45 {
46 DAYSPERNORMALYEAR, DAYSPERLEAPYEAR
47 };
48 static const int MonthLengths[2][MONSPERYEAR] =
49 {
50 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
51 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
52 };
53
54 static __inline int IsLeapYear(int Year)
55 {
56 return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
57 }
58
59 static __inline void NormalizeTimeFields(CSHORT *FieldToNormalize,
60 CSHORT *CarryField,
61 int Modulus)
62 {
63 *FieldToNormalize = (CSHORT) (*FieldToNormalize - Modulus);
64 *CarryField = (CSHORT) (*CarryField + 1);
65 }
66
67 /* FUNCTIONS *****************************************************************/
68
69 /*
70 * @implemented
71 */
72 BOOLEAN STDCALL
73 RtlCutoverTimeToSystemTime(IN PTIME_FIELDS CutoverTimeFields,
74 OUT PLARGE_INTEGER SystemTime,
75 IN PLARGE_INTEGER CurrentTime,
76 IN BOOLEAN ThisYearsCutoverOnly)
77 {
78 TIME_FIELDS AdjustedTimeFields;
79 TIME_FIELDS CurrentTimeFields;
80 TIME_FIELDS CutoverSystemTimeFields;
81 LARGE_INTEGER CutoverSystemTime;
82 CSHORT MonthLength;
83 CSHORT Days;
84 BOOLEAN NextYearsCutover = FALSE;
85
86 /* Check fixed cutover time */
87 if (CutoverTimeFields->Year != 0)
88 {
89 if (!RtlTimeFieldsToTime(CutoverTimeFields, SystemTime))
90 return FALSE;
91
92 if (SystemTime->QuadPart < CurrentTime->QuadPart)
93 return FALSE;
94
95 return TRUE;
96 }
97
98 /*
99 * Compute recurring cutover time
100 */
101
102 /* Day must be between 1(first) and 5(last) */
103 if (CutoverTimeFields->Day == 0 || CutoverTimeFields->Day > 5)
104 return FALSE;
105
106 RtlTimeToTimeFields(CurrentTime, &CurrentTimeFields);
107
108 while (TRUE)
109 {
110 /* Compute the cutover time of the first day of the current month */
111 AdjustedTimeFields.Year = CurrentTimeFields.Year;
112 if (NextYearsCutover == TRUE)
113 AdjustedTimeFields.Year++;
114
115 AdjustedTimeFields.Month = CutoverTimeFields->Month;
116 AdjustedTimeFields.Day = 1;
117 AdjustedTimeFields.Hour = CutoverTimeFields->Hour;
118 AdjustedTimeFields.Minute = CutoverTimeFields->Minute;
119 AdjustedTimeFields.Second = CutoverTimeFields->Second;
120 AdjustedTimeFields.Milliseconds = CutoverTimeFields->Milliseconds;
121
122 if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
123 return FALSE;
124
125 RtlTimeToTimeFields(&CutoverSystemTime, &CutoverSystemTimeFields);
126
127 /* Adjust day to first matching weekday */
128 if (CutoverSystemTimeFields.Weekday != CutoverTimeFields->Weekday)
129 {
130 if (CutoverSystemTimeFields.Weekday < CutoverTimeFields->Weekday)
131 Days = CutoverTimeFields->Weekday - CutoverSystemTimeFields.Weekday;
132 else
133 Days = DAYSPERWEEK - (CutoverSystemTimeFields.Weekday - CutoverTimeFields->Weekday);
134
135 AdjustedTimeFields.Day += Days;
136 }
137
138 /* Adjust the number of weeks */
139 if (CutoverTimeFields->Day > 1)
140 {
141 Days = DAYSPERWEEK * (CutoverTimeFields->Day - 1);
142 MonthLength = MonthLengths[IsLeapYear(AdjustedTimeFields.Year)][AdjustedTimeFields.Month - 1];
143 if ((AdjustedTimeFields.Day + Days) > MonthLength)
144 Days -= DAYSPERWEEK;
145
146 AdjustedTimeFields.Day += Days;
147 }
148
149 if (!RtlTimeFieldsToTime(&AdjustedTimeFields, &CutoverSystemTime))
150 return FALSE;
151
152 if (ThisYearsCutoverOnly == TRUE ||
153 NextYearsCutover == TRUE ||
154 CutoverSystemTime.QuadPart >= CurrentTime->QuadPart)
155 {
156 break;
157 }
158
159 NextYearsCutover = TRUE;
160 }
161
162 SystemTime->QuadPart = CutoverSystemTime.QuadPart;
163
164 return TRUE;
165 }
166
167
168 /*
169 * @implemented
170 */
171 BOOLEAN
172 STDCALL
173 RtlTimeFieldsToTime(
174 IN PTIME_FIELDS TimeFields,
175 OUT PLARGE_INTEGER Time)
176 {
177 int CurYear;
178 int CurMonth;
179 TIME_FIELDS IntTimeFields;
180
181 Time->QuadPart = 0;
182 memcpy(&IntTimeFields,
183 TimeFields,
184 sizeof(TIME_FIELDS));
185
186 /* Normalize the TIME_FIELDS structure here */
187 while (IntTimeFields.Second >= SECSPERMIN)
188 {
189 NormalizeTimeFields(&IntTimeFields.Second,
190 &IntTimeFields.Minute,
191 SECSPERMIN);
192 }
193 while (IntTimeFields.Minute >= MINSPERHOUR)
194 {
195 NormalizeTimeFields(&IntTimeFields.Minute,
196 &IntTimeFields.Hour,
197 MINSPERHOUR);
198 }
199 while (IntTimeFields.Hour >= HOURSPERDAY)
200 {
201 NormalizeTimeFields(&IntTimeFields.Hour,
202 &IntTimeFields.Day,
203 HOURSPERDAY);
204 }
205 while (IntTimeFields.Day >
206 MonthLengths[IsLeapYear(IntTimeFields.Year)][IntTimeFields.Month - 1])
207 {
208 NormalizeTimeFields(&IntTimeFields.Day,
209 &IntTimeFields.Month,
210 SECSPERMIN);
211 }
212 while (IntTimeFields.Month > MONSPERYEAR)
213 {
214 NormalizeTimeFields(&IntTimeFields.Month,
215 &IntTimeFields.Year,
216 MONSPERYEAR);
217 }
218
219 /* Compute the time */
220 for (CurYear = EPOCHYEAR; CurYear < IntTimeFields.Year; CurYear++)
221 {
222 Time->QuadPart += YearLengths[IsLeapYear(CurYear)];
223 }
224 for (CurMonth = 1; CurMonth < IntTimeFields.Month; CurMonth++)
225 {
226 Time->QuadPart += MonthLengths[IsLeapYear(CurYear)][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 STDCALL
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 STDCALL
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 -= (CurYear - EPOCHYEAR) * DAYSPERLEAPYEAR;
325 CurYear--; /* The next calculation needs CurYear - 1 */
326 Days += CurYear - CurYear / 4 + CurYear / 100 - CurYear / 400;
327 CurYear++;
328 Days -= EPOCHYEAR - 1 - (EPOCHYEAR -1) / 4 + (EPOCHYEAR -1) / 100 - (EPOCHYEAR - 1) / 400;
329 while (1)
330 {
331 LeapYear = IsLeapYear(CurYear);
332 if (Days < (long) YearLengths[LeapYear])
333 {
334 break;
335 }
336 CurYear++;
337 Days = Days - (long) YearLengths[LeapYear];
338 }
339 TimeFields->Year = (CSHORT) CurYear;
340
341 /* Compute month of year */
342 LeapYear = IsLeapYear(CurYear);
343 Months = MonthLengths[LeapYear];
344 for (CurMonth = 0; Days >= (long) Months[CurMonth]; CurMonth++)
345 Days = Days - (long) Months[CurMonth];
346 TimeFields->Month = (CSHORT) (CurMonth + 1);
347 TimeFields->Day = (CSHORT) (Days + 1);
348 }
349
350
351 /*
352 * @implemented
353 */
354 BOOLEAN
355 STDCALL
356 RtlTimeToSecondsSince1970(
357 IN PLARGE_INTEGER Time,
358 OUT PULONG SecondsSince1970)
359 {
360 LARGE_INTEGER IntTime;
361
362 IntTime.QuadPart = Time->QuadPart - TICKSTO1970;
363 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
364
365 if (IntTime.u.HighPart != 0)
366 return FALSE;
367
368 *SecondsSince1970 = IntTime.u.LowPart;
369
370 return TRUE;
371 }
372
373
374 /*
375 * @implemented
376 */
377 BOOLEAN
378 STDCALL
379 RtlTimeToSecondsSince1980(
380 IN PLARGE_INTEGER Time,
381 OUT PULONG SecondsSince1980)
382 {
383 LARGE_INTEGER IntTime;
384
385 IntTime.QuadPart = Time->QuadPart - TICKSTO1980;
386 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
387
388 if (IntTime.u.HighPart != 0)
389 return FALSE;
390
391 *SecondsSince1980 = IntTime.u.LowPart;
392
393 return TRUE;
394 }
395
396
397 /*
398 * @implemented
399 */
400 NTSTATUS
401 STDCALL
402 RtlLocalTimeToSystemTime(IN PLARGE_INTEGER LocalTime,
403 OUT PLARGE_INTEGER SystemTime)
404 {
405 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
406 NTSTATUS Status;
407
408 Status = NtQuerySystemInformation(SystemTimeOfDayInformation,
409 &TimeInformation,
410 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
411 NULL);
412 if (!NT_SUCCESS(Status))
413 return Status;
414
415 SystemTime->QuadPart = LocalTime->QuadPart +
416 TimeInformation.TimeZoneBias.QuadPart;
417
418 return STATUS_SUCCESS;
419 }
420
421
422 /*
423 * @implemented
424 */
425 NTSTATUS
426 STDCALL
427 RtlSystemTimeToLocalTime(IN PLARGE_INTEGER SystemTime,
428 OUT PLARGE_INTEGER LocalTime)
429 {
430 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
431 NTSTATUS Status;
432
433 Status = NtQuerySystemInformation(SystemTimeOfDayInformation,
434 &TimeInformation,
435 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
436 NULL);
437 if (!NT_SUCCESS(Status))
438 return Status;
439
440 LocalTime->QuadPart = SystemTime->QuadPart -
441 TimeInformation.TimeZoneBias.QuadPart;
442
443 return STATUS_SUCCESS;
444 }
445
446
447 /*
448 * @implemented
449 */
450 VOID STDCALL
451 RtlSecondsSince1970ToTime(
452 IN ULONG SecondsSince1970,
453 OUT PLARGE_INTEGER Time)
454 {
455 Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970;
456 }
457
458
459 /*
460 * @implemented
461 */
462 VOID STDCALL
463 RtlSecondsSince1980ToTime(
464 IN ULONG SecondsSince1980,
465 OUT PLARGE_INTEGER Time)
466 {
467 Time->QuadPart = ((LONGLONG)SecondsSince1980 * TICKSPERSEC) + TICKSTO1980;
468 }
469
470 /* EOF */