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 ULONG CurMonth; 174 TIME_FIELDS IntTimeFields; 175 176 RtlCopyMemory(&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 (TRUE) 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 { 300 Days = Days - Months[CurMonth]; 301 } 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(IN PLARGE_INTEGER Time, 313 OUT PULONG SecondsSince1970) 314 { 315 LARGE_INTEGER IntTime; 316 317 IntTime.QuadPart = Time->QuadPart - TICKSTO1970; 318 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC; 319 320 if (IntTime.u.HighPart != 0) 321 return FALSE; 322 323 *SecondsSince1970 = IntTime.u.LowPart; 324 325 return TRUE; 326 } 327 328 329 /* 330 * @implemented 331 */ 332 BOOLEAN 333 NTAPI 334 RtlTimeToSecondsSince1980(IN PLARGE_INTEGER Time, 335 OUT PULONG SecondsSince1980) 336 { 337 LARGE_INTEGER IntTime; 338 339 IntTime.QuadPart = Time->QuadPart - TICKSTO1980; 340 IntTime.QuadPart = IntTime.QuadPart / TICKSPERSEC; 341 342 if (IntTime.u.HighPart != 0) 343 return FALSE; 344 345 *SecondsSince1980 = IntTime.u.LowPart; 346 347 return TRUE; 348 } 349 350 351 /* 352 * @implemented 353 */ 354 NTSTATUS 355 NTAPI 356 RtlLocalTimeToSystemTime(IN PLARGE_INTEGER LocalTime, 357 OUT PLARGE_INTEGER SystemTime) 358 { 359 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation; 360 NTSTATUS Status; 361 362 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation, 363 &TimeInformation, 364 sizeof(TimeInformation), 365 NULL); 366 if (!NT_SUCCESS(Status)) 367 return Status; 368 369 SystemTime->QuadPart = LocalTime->QuadPart + 370 TimeInformation.TimeZoneBias.QuadPart; 371 372 return STATUS_SUCCESS; 373 } 374 375 376 /* 377 * @implemented 378 */ 379 NTSTATUS 380 NTAPI 381 RtlSystemTimeToLocalTime(IN PLARGE_INTEGER SystemTime, 382 OUT PLARGE_INTEGER LocalTime) 383 { 384 SYSTEM_TIMEOFDAY_INFORMATION TimeInformation; 385 NTSTATUS Status; 386 387 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation, 388 &TimeInformation, 389 sizeof(TimeInformation), 390 NULL); 391 if (!NT_SUCCESS(Status)) 392 return Status; 393 394 LocalTime->QuadPart = SystemTime->QuadPart - 395 TimeInformation.TimeZoneBias.QuadPart; 396 397 return STATUS_SUCCESS; 398 } 399 400 401 /* 402 * @implemented 403 */ 404 VOID 405 NTAPI 406 RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970, 407 OUT PLARGE_INTEGER Time) 408 { 409 Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970; 410 } 411 412 413 /* 414 * @implemented 415 */ 416 VOID NTAPI 417 RtlSecondsSince1980ToTime(IN ULONG SecondsSince1980, 418 OUT PLARGE_INTEGER Time) 419 { 420 Time->QuadPart = ((LONGLONG)SecondsSince1980 * TICKSPERSEC) + TICKSTO1980; 421 } 422 423 /* EOF */ 424