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