d0d8a7edd1181cf472911abe2e6cd83bfe259052
[reactos.git] / lib / ntdll / rtl / time.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            lib/ntdll/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
19 #define TICKSPERSEC        10000000
20 #define TICKSPERMSEC       10000
21 #define SECSPERDAY         86400
22 #define SECSPERHOUR        3600
23 #define SECSPERMIN         60
24 #define MINSPERHOUR        60
25 #define HOURSPERDAY        24
26 #define EPOCHWEEKDAY       1
27 #define DAYSPERWEEK        7
28 #define EPOCHYEAR          1601
29 #define DAYSPERNORMALYEAR  365
30 #define DAYSPERLEAPYEAR    366
31 #define MONSPERYEAR        12
32
33 #define TICKSTO1970         0x019db1ded53e8000
34 #define TICKSTO1980         0x01a8e79fe1d58000
35
36 static const int YearLengths[2] = {DAYSPERNORMALYEAR, DAYSPERLEAPYEAR};
37 static const int MonthLengths[2][MONSPERYEAR] =
38 {
39         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
40         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
41 };
42
43 static __inline int IsLeapYear(int Year)
44 {
45   return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
46 }
47
48 static __inline void NormalizeTimeFields(CSHORT *FieldToNormalize,
49                                          CSHORT *CarryField,
50                                          int Modulus)
51 {
52   *FieldToNormalize = (CSHORT) (*FieldToNormalize - Modulus);
53   *CarryField = (CSHORT) (*CarryField + 1);
54 }
55
56 /* FUNCTIONS *****************************************************************/
57
58 VOID
59 STDCALL
60 RtlTimeToTimeFields(
61         PLARGE_INTEGER liTime,
62         PTIME_FIELDS TimeFields)
63 {
64   const int *Months;
65   int LeapSecondCorrections, SecondsInDay, CurYear;
66   int LeapYear, CurMonth, GMTOffset;
67   long int Days;
68   long long int Time = (long long int)liTime->QuadPart;
69
70     /* Extract millisecond from time and convert time into seconds */
71   TimeFields->Milliseconds = (CSHORT) ((Time % TICKSPERSEC) / TICKSPERMSEC);
72   Time = Time / TICKSPERSEC;
73
74     /* FIXME: Compute the number of leap second corrections here */
75   LeapSecondCorrections = 0;
76
77     /* FIXME: get the GMT offset here */
78   GMTOffset = 0;
79
80     /* Split the time into days and seconds within the day */
81   Days = Time / SECSPERDAY;
82   SecondsInDay = Time % SECSPERDAY;
83
84     /* Adjust the values for GMT and leap seconds */
85   SecondsInDay += (GMTOffset - LeapSecondCorrections);
86   while (SecondsInDay < 0) 
87     {
88       SecondsInDay += SECSPERDAY;
89       Days--;
90     }
91   while (SecondsInDay >= SECSPERDAY) 
92     {
93       SecondsInDay -= SECSPERDAY;
94       Days++;
95     }
96
97     /* compute time of day */
98   TimeFields->Hour = (CSHORT) (SecondsInDay / SECSPERHOUR);
99   SecondsInDay = SecondsInDay % SECSPERHOUR;
100   TimeFields->Minute = (CSHORT) (SecondsInDay / SECSPERMIN);
101   TimeFields->Second = (CSHORT) (SecondsInDay % SECSPERMIN);
102
103     /* FIXME: handle the possibility that we are on a leap second (i.e. Second = 60) */
104
105     /* compute day of week */
106   TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
107
108     /* compute year */
109   CurYear = EPOCHYEAR;
110   CurYear += Days / DAYSPERLEAPYEAR;
111   Days -= (CurYear - EPOCHYEAR) * DAYSPERLEAPYEAR;
112   CurYear--; /* The next calculation needs CurYear - 1 */
113   Days += CurYear - CurYear / 4 + CurYear / 100 - CurYear / 400;
114   CurYear++;
115   Days -= EPOCHYEAR - 1 - (EPOCHYEAR -1) / 4 + (EPOCHYEAR -1) / 100 - (EPOCHYEAR - 1) / 400;
116
117     /* FIXME: handle calendar modifications */
118   while (1)
119     {
120       LeapYear = IsLeapYear(CurYear);
121       if (Days < (long) YearLengths[LeapYear])
122         {
123           break;
124         }
125       CurYear++;
126       Days = Days - (long) YearLengths[LeapYear];
127     }
128   TimeFields->Year = (CSHORT) CurYear;
129
130     /* Compute month of year */
131   LeapYear = IsLeapYear(CurYear);
132   Months = MonthLengths[LeapYear];
133   for (CurMonth = 0; Days >= (long) Months[CurMonth]; CurMonth++)
134     Days = Days - (long) Months[CurMonth];
135   TimeFields->Month = (CSHORT) (CurMonth + 1);
136   TimeFields->Day = (CSHORT) (Days + 1);
137 }
138
139
140 BOOLEAN
141 STDCALL
142 RtlTimeFieldsToTime(
143         PTIME_FIELDS tfTimeFields,
144         PLARGE_INTEGER Time)
145 {
146   int CurYear;
147   int CurMonth;
148   long long int rcTime;
149   TIME_FIELDS TimeFields = *tfTimeFields;
150
151   rcTime = 0;
152   
153     /* FIXME: normalize the TIME_FIELDS structure here */
154   while (TimeFields.Second >= SECSPERMIN)
155     {
156       NormalizeTimeFields(&TimeFields.Second, 
157                           &TimeFields.Minute, 
158                           SECSPERMIN);
159     }
160   while (TimeFields.Minute >= MINSPERHOUR)
161     {
162       NormalizeTimeFields(&TimeFields.Minute, 
163                           &TimeFields.Hour, 
164                           MINSPERHOUR);
165     }
166   while (TimeFields.Hour >= HOURSPERDAY)
167     {
168       NormalizeTimeFields(&TimeFields.Hour, 
169                           &TimeFields.Day, 
170                           HOURSPERDAY);
171     }
172   while (TimeFields.Day >
173          MonthLengths[IsLeapYear(TimeFields.Year)][TimeFields.Month - 1])
174     {
175       NormalizeTimeFields(&TimeFields.Day, 
176                           &TimeFields.Month, 
177                           SECSPERMIN);
178     }
179   while (TimeFields.Month > MONSPERYEAR)
180     {
181       NormalizeTimeFields(&TimeFields.Month, 
182                           &TimeFields.Year, 
183                           MONSPERYEAR);
184     }
185
186     /* FIXME: handle calendar corrections here */
187   for (CurYear = EPOCHYEAR; CurYear < TimeFields.Year; CurYear++)
188     {
189       rcTime += YearLengths[IsLeapYear(CurYear)];
190     }
191   for (CurMonth = 1; CurMonth < TimeFields.Month; CurMonth++)
192     {
193       rcTime += MonthLengths[IsLeapYear(CurYear)][CurMonth - 1];
194     }
195   rcTime += TimeFields.Day - 1;
196   rcTime *= SECSPERDAY;
197   rcTime += TimeFields.Hour * SECSPERHOUR + TimeFields.Minute * SECSPERMIN +
198             TimeFields.Second;
199   rcTime *= TICKSPERSEC;
200   rcTime += TimeFields.Milliseconds * TICKSPERMSEC;
201   *Time = *(LARGE_INTEGER *)&rcTime;
202
203   return TRUE;
204 }
205
206
207 VOID
208 STDCALL
209 RtlSecondsSince1970ToTime(
210         ULONG SecondsSince1970,
211         PLARGE_INTEGER Time)
212 {
213   LONGLONG llTime;
214
215   llTime = (SecondsSince1970 * TICKSPERSEC) + TICKSTO1970;
216
217   *Time = *(LARGE_INTEGER *)&llTime;
218 }
219
220
221 VOID
222 STDCALL
223 RtlSecondsSince1980ToTime(
224         ULONG SecondsSince1980,
225         PLARGE_INTEGER Time)
226 {
227   LONGLONG llTime;
228
229   llTime = (SecondsSince1980 * TICKSPERSEC) + TICKSTO1980;
230
231   *Time = *(LARGE_INTEGER *)&llTime;
232 }
233
234
235 BOOLEAN
236 STDCALL
237 RtlTimeToSecondsSince1970(
238         PLARGE_INTEGER Time,
239         PULONG SecondsSince1970)
240 {
241   LARGE_INTEGER liTime;
242
243   liTime.QuadPart = Time->QuadPart - TICKSTO1970;
244   liTime.QuadPart = liTime.QuadPart / TICKSPERSEC;
245
246   if (liTime.u.HighPart != 0)
247     return FALSE;
248
249   *SecondsSince1970 = liTime.u.LowPart;
250
251   return TRUE;
252 }
253
254
255 BOOLEAN
256 STDCALL
257 RtlTimeToSecondsSince1980(
258         PLARGE_INTEGER Time,
259         PULONG SecondsSince1980)
260 {
261   LARGE_INTEGER liTime;
262
263   liTime.QuadPart = Time->QuadPart - TICKSTO1980;
264   liTime.QuadPart = liTime.QuadPart / TICKSPERSEC;
265
266   if (liTime.u.HighPart != 0)
267     return FALSE;
268
269   *SecondsSince1980 = liTime.u.LowPart;
270
271   return TRUE;
272 }
273
274
275 NTSTATUS
276 STDCALL
277 RtlLocalTimeToSystemTime(PLARGE_INTEGER LocalTime,
278                          PLARGE_INTEGER SystemTime)
279 {
280   SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
281   NTSTATUS Status;
282
283   Status = NtQuerySystemInformation(SystemTimeOfDayInformation,
284                                     &TimeInformation,
285                                     sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
286                                     NULL);
287   if (!NT_SUCCESS(Status))
288     return(Status);
289
290   SystemTime->QuadPart = LocalTime->QuadPart +
291                          TimeInformation.TimeZoneBias.QuadPart;
292
293   return(STATUS_SUCCESS);
294 }
295
296
297 NTSTATUS
298 STDCALL
299 RtlSystemTimeToLocalTime(PLARGE_INTEGER SystemTime,
300                          PLARGE_INTEGER LocalTime)
301 {
302   SYSTEM_TIMEOFDAY_INFORMATION TimeInformation;
303   NTSTATUS Status;
304
305   Status = NtQuerySystemInformation(SystemTimeOfDayInformation,
306                                     &TimeInformation,
307                                     sizeof(SYSTEM_TIMEOFDAY_INFORMATION),
308                                     NULL);
309   if (!NT_SUCCESS(Status))
310     return(Status);
311
312   LocalTime->QuadPart = SystemTime->QuadPart -
313                         TimeInformation.TimeZoneBias.QuadPart;
314
315   return(STATUS_SUCCESS);
316 }
317
318
319 VOID
320 STDCALL
321 RtlTimeToElapsedTimeFields(IN PLARGE_INTEGER Time,
322                            OUT PTIME_FIELDS TimeFields)
323 {
324   ULONGLONG ElapsedSeconds;
325   ULONG SecondsInDay;
326   ULONG SecondsInMinute;
327
328   /* Extract millisecond from time */
329   TimeFields->Milliseconds = (CSHORT)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
330
331   /* Compute elapsed seconds */
332   ElapsedSeconds = (ULONGLONG)Time->QuadPart / TICKSPERSEC;
333
334   /* Compute seconds within the day */
335   SecondsInDay = ElapsedSeconds % SECSPERDAY;
336
337   /* Compute elapsed minutes within the day */
338   SecondsInMinute = SecondsInDay % SECSPERHOUR;
339
340   /* Compute elapsed time of day */
341   TimeFields->Hour = (CSHORT)(SecondsInDay / SECSPERHOUR);
342   TimeFields->Minute = (CSHORT)(SecondsInMinute / SECSPERMIN);
343   TimeFields->Second = (CSHORT)(SecondsInMinute % SECSPERMIN);
344
345   /* Compute elapsed days */
346   TimeFields->Day = (CSHORT)(ElapsedSeconds / SECSPERDAY);
347
348   /* The elapsed number of months and days cannot be calculated */
349   TimeFields->Month = 0;
350   TimeFields->Year = 0;
351 }
352
353
354 /* EOF */