Fixed bug in RtlTimeToTimeFields().
[reactos.git] / reactos / lib / ntdll / rtl / time.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: kernel/rtl/time.c
5 * PURPOSE: Conversion between Time and TimeFields
6 * PROGRAMMER: Rex Jolliff (rex@lvcablemodem.com)
7 * UPDATE HISTORY:
8 * Created 22/05/98
9 * 08/03/98 RJJ Implemented these functions
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15
16 #include <internal/debug.h>
17
18 #define TICKSPERSEC 10000000
19 #define TICKSPERMSEC 10000
20 #define SECSPERDAY 86400
21 #define SECSPERHOUR 3600
22 #define SECSPERMIN 60
23 #define MINSPERHOUR 60
24 #define HOURSPERDAY 24
25 #define EPOCHWEEKDAY 0
26 #define DAYSPERWEEK 7
27 #define EPOCHYEAR 1601
28 #define DAYSPERNORMALYEAR 365
29 #define DAYSPERLEAPYEAR 366
30 #define MONSPERYEAR 12
31
32 static const int YearLengths[2] = {DAYSPERNORMALYEAR, DAYSPERLEAPYEAR};
33 static const int MonthLengths[2][MONSPERYEAR] =
34 {
35 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
36 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
37 };
38
39 static __inline int IsLeapYear(int Year)
40 {
41 return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
42 }
43
44 static __inline void NormalizeTimeFields(CSHORT *FieldToNormalize,
45 CSHORT *CarryField,
46 int Modulus)
47 {
48 *FieldToNormalize = (CSHORT) (*FieldToNormalize - Modulus);
49 *CarryField = (CSHORT) (*CarryField + 1);
50 }
51
52 /* FUNCTIONS *****************************************************************/
53
54 VOID RtlTimeToTimeFields(PLARGE_INTEGER liTime,
55 PTIME_FIELDS TimeFields)
56 {
57 const int *Months;
58 int LeapSecondCorrections, SecondsInDay, CurYear;
59 int LeapYear, CurMonth, GMTOffset;
60 long int Days;
61 long long int Time = (long long int)liTime->QuadPart;
62
63 /* Extract millisecond from time and convert time into seconds */
64 TimeFields->Milliseconds = (CSHORT) ((Time % TICKSPERSEC) / TICKSPERMSEC);
65 Time = Time / TICKSPERSEC;
66
67 /* FIXME: Compute the number of leap second corrections here */
68 LeapSecondCorrections = 0;
69
70 /* FIXME: get the GMT offset here */
71 GMTOffset = 0;
72
73 /* Split the time into days and seconds within the day */
74 Days = Time / SECSPERDAY;
75 SecondsInDay = Time % SECSPERDAY;
76
77 /* Adjust the values for GMT and leap seconds */
78 SecondsInDay += (GMTOffset - LeapSecondCorrections);
79 while (SecondsInDay < 0)
80 {
81 SecondsInDay += SECSPERDAY;
82 Days--;
83 }
84 while (SecondsInDay >= SECSPERDAY)
85 {
86 SecondsInDay -= SECSPERDAY;
87 Days++;
88 }
89
90 /* compute time of day */
91 TimeFields->Hour = (CSHORT) (SecondsInDay / SECSPERHOUR);
92 SecondsInDay = SecondsInDay % SECSPERHOUR;
93 TimeFields->Minute = (CSHORT) (SecondsInDay / SECSPERMIN);
94 TimeFields->Second = (CSHORT) (SecondsInDay % SECSPERMIN);
95
96 /* FIXME: handle the possibility that we are on a leap second (i.e. Second = 60) */
97
98 /* compute day of week */
99 TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
100
101 /* compute year */
102 CurYear = EPOCHYEAR;
103 /* FIXME: handle calendar modifications */
104 while (1)
105 {
106 LeapYear = IsLeapYear(CurYear);
107 if (Days < (long) YearLengths[LeapYear])
108 {
109 break;
110 }
111 CurYear++;
112 Days = Days - (long) YearLengths[LeapYear];
113 }
114 TimeFields->Year = (CSHORT) CurYear;
115
116 /* Compute month of year */
117 Months = MonthLengths[LeapYear];
118 for (CurMonth = 0; Days >= (long) Months[CurMonth]; CurMonth++)
119 Days = Days - (long) Months[CurMonth];
120 TimeFields->Month = (CSHORT) (CurMonth + 1);
121 TimeFields->Day = (CSHORT) (Days + 1);
122 }
123
124 BOOLEAN RtlTimeFieldsToTime(PTIME_FIELDS tfTimeFields,
125 PLARGE_INTEGER Time)
126 {
127 int CurYear, CurMonth;
128 long long int rcTime;
129 TIME_FIELDS TimeFields = *tfTimeFields;
130
131 rcTime = 0;
132
133 /* FIXME: normalize the TIME_FIELDS structure here */
134 while (TimeFields.Second >= SECSPERMIN)
135 {
136 NormalizeTimeFields(&TimeFields.Second,
137 &TimeFields.Minute,
138 SECSPERMIN);
139 }
140 while (TimeFields.Minute >= MINSPERHOUR)
141 {
142 NormalizeTimeFields(&TimeFields.Minute,
143 &TimeFields.Hour,
144 MINSPERHOUR);
145 }
146 while (TimeFields.Hour >= HOURSPERDAY)
147 {
148 NormalizeTimeFields(&TimeFields.Hour,
149 &TimeFields.Day,
150 HOURSPERDAY);
151 }
152 while (TimeFields.Day >
153 MonthLengths[IsLeapYear(TimeFields.Year)][TimeFields.Month - 1])
154 {
155 NormalizeTimeFields(&TimeFields.Day,
156 &TimeFields.Month,
157 SECSPERMIN);
158 }
159 while (TimeFields.Month > MONSPERYEAR)
160 {
161 NormalizeTimeFields(&TimeFields.Month,
162 &TimeFields.Year,
163 MONSPERYEAR);
164 }
165
166 /* FIXME: handle calendar corrections here */
167 for (CurYear = EPOCHYEAR; CurYear < TimeFields.Year; CurYear++)
168 {
169 rcTime += YearLengths[IsLeapYear(CurYear)];
170 }
171 for (CurMonth = 1; CurMonth < TimeFields.Month; CurMonth++)
172 {
173 rcTime += MonthLengths[IsLeapYear(CurYear)][CurMonth - 1];
174 }
175 rcTime += TimeFields.Day - 1;
176 rcTime *= SECSPERDAY;
177 rcTime += TimeFields.Hour * SECSPERHOUR + TimeFields.Minute * SECSPERMIN +
178 TimeFields.Second;
179 rcTime *= TICKSPERSEC;
180 rcTime += TimeFields.Milliseconds * TICKSPERMSEC;
181 *Time = *(LARGE_INTEGER *)&rcTime;
182
183 return TRUE;
184 }