Create the AHCI branch for Aman's work
[reactos.git] / sdk / 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)
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 ||
150 NextYearsCutover ||
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(IN PTIME_FIELDS TimeFields,
171 OUT PLARGE_INTEGER Time)
172 {
173 int CurMonth;
174 TIME_FIELDS IntTimeFields;
175
176 memcpy(&IntTimeFields,
177 TimeFields,
178 sizeof(TIME_FIELDS));
179
180 if (TimeFields->Milliseconds < 0 || TimeFields->Milliseconds > 999 ||
181 TimeFields->Second < 0 || TimeFields->Second > 59 ||
182 TimeFields->Minute < 0 || TimeFields->Minute > 59 ||
183 TimeFields->Hour < 0 || TimeFields->Hour > 23 ||
184 TimeFields->Month < 1 || TimeFields->Month > 12 ||
185 TimeFields->Day < 1 ||
186 TimeFields->Day >
187 MonthLengths[IsLeapYear(TimeFields->Year)][TimeFields->Month - 1] ||
188 TimeFields->Year < 1601)
189 {
190 return FALSE;
191 }
192
193 /* Compute the time */
194 Time->QuadPart = DaysSinceEpoch(IntTimeFields.Year);
195 for (CurMonth = 1; CurMonth < IntTimeFields.Month; CurMonth++)
196 {
197 Time->QuadPart += MonthLengths[IsLeapYear(IntTimeFields.Year)][CurMonth - 1];
198 }
199 Time->QuadPart += IntTimeFields.Day - 1;
200 Time->QuadPart *= SECSPERDAY;
201 Time->QuadPart += IntTimeFields.Hour * SECSPERHOUR + IntTimeFields.Minute * SECSPERMIN +
202 IntTimeFields.Second;
203 Time->QuadPart *= TICKSPERSEC;
204 Time->QuadPart += IntTimeFields.Milliseconds * TICKSPERMSEC;
205
206 return TRUE;
207 }
208
209
210 /*
211 * @implemented
212 */
213 VOID
214 NTAPI
215 RtlTimeToElapsedTimeFields(IN PLARGE_INTEGER Time,
216 OUT PTIME_FIELDS TimeFields)
217 {
218 ULONGLONG ElapsedSeconds;
219 ULONG SecondsInDay;
220 ULONG SecondsInMinute;
221
222 /* Extract millisecond from time */
223 TimeFields->Milliseconds = (CSHORT)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
224
225 /* Compute elapsed seconds */
226 ElapsedSeconds = (ULONGLONG)Time->QuadPart / TICKSPERSEC;
227
228 /* Compute seconds within the day */
229 SecondsInDay = ElapsedSeconds % SECSPERDAY;
230
231 /* Compute elapsed minutes within the day */
232 SecondsInMinute = SecondsInDay % SECSPERHOUR;
233
234 /* Compute elapsed time of day */
235 TimeFields->Hour = (CSHORT)(SecondsInDay / SECSPERHOUR);
236 TimeFields->Minute = (CSHORT)(SecondsInMinute / SECSPERMIN);
237 TimeFields->Second = (CSHORT)(SecondsInMinute % SECSPERMIN);
238
239 /* Compute elapsed days */
240 TimeFields->Day = (CSHORT)(ElapsedSeconds / SECSPERDAY);
241
242 /* The elapsed number of months and days cannot be calculated */
243 TimeFields->Month = 0;
244 TimeFields->Year = 0;
245 }
246
247
248 /*
249 * @implemented
250 */
251 VOID
252 NTAPI
253 RtlTimeToTimeFields(IN PLARGE_INTEGER Time,
254 OUT PTIME_FIELDS TimeFields)
255 {
256 const UCHAR *Months;
257 ULONG SecondsInDay, CurYear;
258 ULONG LeapYear, CurMonth;
259 ULONG Days;
260 ULONGLONG IntTime = Time->QuadPart;
261
262 /* Extract millisecond from time and convert time into seconds */
263 TimeFields->Milliseconds = (CSHORT) ((IntTime % TICKSPERSEC) / TICKSPERMSEC);
264 IntTime = IntTime / TICKSPERSEC;
265
266 /* Split the time into days and seconds within the day */
267 Days = (ULONG)(IntTime / SECSPERDAY);
268 SecondsInDay = IntTime % SECSPERDAY;
269
270 /* compute time of day */
271 TimeFields->Hour = (CSHORT) (SecondsInDay / SECSPERHOUR);
272 SecondsInDay = SecondsInDay % SECSPERHOUR;
273 TimeFields->Minute = (CSHORT) (SecondsInDay / SECSPERMIN);
274 TimeFields->Second = (CSHORT) (SecondsInDay % SECSPERMIN);
275
276 /* compute day of week */
277 TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
278
279 /* compute year */
280 CurYear = EPOCHYEAR;
281 CurYear += Days / DAYSPERLEAPYEAR;
282 Days -= DaysSinceEpoch(CurYear);
283 while (1)
284 {
285 LeapYear = IsLeapYear(CurYear);
286 if (Days < YearLengths[LeapYear])
287 {
288 break;
289 }
290 CurYear++;
291 Days = Days - YearLengths[LeapYear];
292 }
293 TimeFields->Year = (CSHORT) CurYear;
294
295 /* Compute month of year */
296 LeapYear = IsLeapYear(CurYear);
297 Months = MonthLengths[LeapYear];
298 for (CurMonth = 0; Days >= Months[CurMonth]; CurMonth++)
299 Days = Days - Months[CurMonth];
300 TimeFields->Month = (CSHORT) (CurMonth + 1);
301 TimeFields->Day = (CSHORT) (Days + 1);
302 }
303
304
305 /*
306 * @implemented
307 */
308 BOOLEAN
309 NTAPI
310 RtlTimeToSecondsSince1970(IN PLARGE_INTEGER Time,
311 OUT PULONG SecondsSince1970)
312 {
313 LARGE_INTEGER IntTime;
314
315 IntTime.QuadPart = Time->QuadPart - TICKSTO1970;
316 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
317
318 if (IntTime.u.HighPart != 0)
319 return FALSE;
320
321 *SecondsSince1970 = IntTime.u.LowPart;
322
323 return TRUE;
324 }
325
326
327 /*
328 * @implemented
329 */
330 BOOLEAN
331 NTAPI
332 RtlTimeToSecondsSince1980(IN PLARGE_INTEGER Time,
333 OUT PULONG SecondsSince1980)
334 {
335 LARGE_INTEGER IntTime;
336
337 IntTime.QuadPart = Time->QuadPart - TICKSTO1980;
338 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC;
339
340 if (IntTime.u.HighPart != 0)
341 return FALSE;
342
343 *SecondsSince1980 = IntTime.u.LowPart;
344
345 return TRUE;
346 }
347
348
349 /*
350 * @implemented
351 */
352 NTSTATUS
353 NTAPI
354 RtlLocalTimeToSystemTime(IN PLARGE_INTEGER LocalTime,
355 OUT PLARGE_INTEGER SystemTime)
356 {
357 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
358 NTSTATUS Status;
359
360 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
361 &TimeInformation,
362 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
363 NULL);
364 if (!NT_SUCCESS(Status))
365 return Status;
366
367 SystemTime->QuadPart = LocalTime->QuadPart +
368 TimeInformation.TimeZoneBias.QuadPart;
369
370 return STATUS_SUCCESS;
371 }
372
373
374 /*
375 * @implemented
376 */
377 NTSTATUS
378 NTAPI
379 RtlSystemTimeToLocalTime(IN PLARGE_INTEGER SystemTime,
380 OUT PLARGE_INTEGER LocalTime)
381 {
382 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
383 NTSTATUS Status;
384
385 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
386 &TimeInformation,
387 sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
388 NULL);
389 if (!NT_SUCCESS(Status))
390 return Status;
391
392 LocalTime->QuadPart = SystemTime->QuadPart -
393 TimeInformation.TimeZoneBias.QuadPart;
394
395 return STATUS_SUCCESS;
396 }
397
398
399 /*
400 * @implemented
401 */
402 VOID
403 NTAPI
404 RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970,
405 OUT PLARGE_INTEGER Time)
406 {
407 Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970;
408 }
409
410
411 /*
412 * @implemented
413 */
414 VOID NTAPI
415 RtlSecondsSince1980ToTime(IN ULONG SecondsSince1980,
416 OUT PLARGE_INTEGER Time)
417 {
418 Time->QuadPart = ((LONGLONG)SecondsSince1980 * TICKSPERSEC) + TICKSTO1980;
419 }
420
421 /* EOF */