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