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