Reactos
at master 423 lines 12 kB view raw
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 40static const unsigned int YearLengths[2] = 41{ 42 DAYSPERNORMALYEAR, DAYSPERLEAPYEAR 43}; 44static 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 50static __inline int IsLeapYear(int Year) 51{ 52 return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0; 53} 54 55static 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 */ 69BOOLEAN NTAPI 70RtlCutoverTimeToSystemTime(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 */ 168BOOLEAN 169NTAPI 170RtlTimeFieldsToTime(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 */ 213VOID 214NTAPI 215RtlTimeToElapsedTimeFields(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 */ 251VOID 252NTAPI 253RtlTimeToTimeFields(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 */ 310BOOLEAN 311NTAPI 312RtlTimeToSecondsSince1970(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 */ 332BOOLEAN 333NTAPI 334RtlTimeToSecondsSince1980(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 */ 354NTSTATUS 355NTAPI 356RtlLocalTimeToSystemTime(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 */ 379NTSTATUS 380NTAPI 381RtlSystemTimeToLocalTime(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 */ 404VOID 405NTAPI 406RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970, 407 OUT PLARGE_INTEGER Time) 408{ 409 Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970; 410} 411 412 413/* 414 * @implemented 415 */ 416VOID NTAPI 417RtlSecondsSince1980ToTime(IN ULONG SecondsSince1980, 418 OUT PLARGE_INTEGER Time) 419{ 420 Time->QuadPart = ((LONGLONG)SecondsSince1980 * TICKSPERSEC) + TICKSTO1980; 421} 422 423/* EOF */