1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/wine/timezone.c 5 * PURPOSE: Time conversion functions 6 * PROGRAMMER: Ariadne 7 * DOSDATE and DOSTIME structures from Onno Hovers 8 * UPDATE HISTORY: 9 * Created 19/01/99 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include <k32.h> 15 16 #define NDEBUG 17 #include <debug.h> 18 19 /* TYPES *********************************************************************/ 20 21 #define TICKSPERMIN 600000000 22 23 #define LL2FILETIME( ll, pft )\ 24 (pft)->dwLowDateTime = (UINT)(ll); \ 25 (pft)->dwHighDateTime = (UINT)((ll) >> 32); 26 #define FILETIME2LL( pft, ll) \ 27 ll = (((LONGLONG)((pft)->dwHighDateTime))<<32) + (pft)-> dwLowDateTime ; 28 29 static const int MonthLengths[2][12] = 30 { 31 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 32 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 33 }; 34 35 /* STATIC FUNCTIONS **********************************************************/ 36 37 static inline int IsLeapYear(int Year) 38 { 39 return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0; 40 } 41 42 /*********************************************************************** 43 * TIME_DayLightCompareDate 44 * 45 * Compares two dates without looking at the year. 46 * 47 * PARAMS 48 * date [in] The local time to compare. 49 * compareDate [in] The daylight savings begin or end date. 50 * 51 * RETURNS 52 * 53 * -1 if date < compareDate 54 * 0 if date == compareDate 55 * 1 if date > compareDate 56 * -2 if an error occurs 57 */ 58 static int 59 TIME_DayLightCompareDate(const SYSTEMTIME *date, const SYSTEMTIME *compareDate) 60 { 61 int limit_day, dayinsecs; 62 63 if (date->wMonth < compareDate->wMonth) 64 return -1; /* We are in a month before the date limit. */ 65 66 if (date->wMonth > compareDate->wMonth) 67 return 1; /* We are in a month after the date limit. */ 68 69 /* if year is 0 then date is in day-of-week format, otherwise 70 * it's absolute date. 71 */ 72 if (compareDate->wYear == 0) 73 { 74 WORD First; 75 /* compareDate->wDay is interpreted as number of the week in the month 76 * 5 means: the last week in the month */ 77 int weekofmonth = compareDate->wDay; 78 /* calculate the day of the first DayOfWeek in the month */ 79 First = ( 6 + compareDate->wDayOfWeek - date->wDayOfWeek + date->wDay 80 ) % 7 + 1; 81 limit_day = First + 7 * (weekofmonth - 1); 82 /* check needed for the 5th weekday of the month */ 83 if(limit_day > MonthLengths[date->wMonth==2 && IsLeapYear(date->wYear)] 84 [date->wMonth - 1]) 85 limit_day -= 7; 86 } 87 else 88 { 89 limit_day = compareDate->wDay; 90 } 91 92 /* convert to seconds */ 93 limit_day = ((limit_day * 24 + compareDate->wHour) * 60 + 94 compareDate->wMinute ) * 60; 95 dayinsecs = ((date->wDay * 24 + date->wHour) * 60 + 96 date->wMinute ) * 60 + date->wSecond; 97 /* and compare */ 98 return dayinsecs < limit_day ? -1 : 99 dayinsecs > limit_day ? 1 : 100 0; /* date is equal to the date limit. */ 101 } 102 103 /*********************************************************************** 104 * TIME_CompTimeZoneID 105 * 106 * Computes the local time bias for a given time and time zone. 107 * 108 * PARAMS 109 * pTZinfo [in] The time zone data. 110 * lpFileTime [in] The system or local time. 111 * islocal [in] it is local time. 112 * 113 * RETURNS 114 * TIME_ZONE_ID_INVALID An error occurred 115 * TIME_ZONE_ID_UNKNOWN There are no transition time known 116 * TIME_ZONE_ID_STANDARD Current time is standard time 117 * TIME_ZONE_ID_DAYLIGHT Current time is daylight savings time 118 */ 119 static 120 DWORD 121 TIME_CompTimeZoneID( const TIME_ZONE_INFORMATION *pTZinfo, FILETIME *lpFileTime, BOOL islocal ) 122 { 123 int ret, year; 124 BOOL beforeStandardDate, afterDaylightDate; 125 DWORD retval = TIME_ZONE_ID_INVALID; 126 LONGLONG llTime = 0; /* initialized to prevent gcc complaining */ 127 SYSTEMTIME SysTime; 128 FILETIME ftTemp; 129 130 if (pTZinfo->DaylightDate.wMonth != 0) 131 { 132 /* if year is 0 then date is in day-of-week format, otherwise 133 * it's absolute date. 134 */ 135 if (pTZinfo->StandardDate.wMonth == 0 || 136 (pTZinfo->StandardDate.wYear == 0 && 137 (pTZinfo->StandardDate.wDay<1 || 138 pTZinfo->StandardDate.wDay>5 || 139 pTZinfo->DaylightDate.wDay<1 || 140 pTZinfo->DaylightDate.wDay>5))) 141 { 142 SetLastError(ERROR_INVALID_PARAMETER); 143 return TIME_ZONE_ID_INVALID; 144 } 145 146 if (!islocal) { 147 FILETIME2LL( lpFileTime, llTime ); 148 llTime -= pTZinfo->Bias * (LONGLONG)TICKSPERMIN; 149 LL2FILETIME( llTime, &ftTemp) 150 lpFileTime = &ftTemp; 151 } 152 153 FileTimeToSystemTime(lpFileTime, &SysTime); 154 year = SysTime.wYear; 155 156 if (!islocal) { 157 llTime -= pTZinfo->DaylightBias * (LONGLONG)TICKSPERMIN; 158 LL2FILETIME( llTime, &ftTemp) 159 FileTimeToSystemTime(lpFileTime, &SysTime); 160 } 161 162 /* check for daylight savings */ 163 if(year == SysTime.wYear) { 164 ret = TIME_DayLightCompareDate( &SysTime, &pTZinfo->StandardDate); 165 if (ret == -2) 166 return TIME_ZONE_ID_INVALID; 167 168 beforeStandardDate = ret < 0; 169 } else 170 beforeStandardDate = SysTime.wYear < year; 171 172 if (!islocal) { 173 llTime -= ( pTZinfo->StandardBias - pTZinfo->DaylightBias ) 174 * (LONGLONG)TICKSPERMIN; 175 LL2FILETIME( llTime, &ftTemp) 176 FileTimeToSystemTime(lpFileTime, &SysTime); 177 } 178 179 if(year == SysTime.wYear) { 180 ret = TIME_DayLightCompareDate( &SysTime, &pTZinfo->DaylightDate); 181 if (ret == -2) 182 return TIME_ZONE_ID_INVALID; 183 184 afterDaylightDate = ret >= 0; 185 } else 186 afterDaylightDate = SysTime.wYear > year; 187 188 retval = TIME_ZONE_ID_STANDARD; 189 if( pTZinfo->DaylightDate.wMonth < pTZinfo->StandardDate.wMonth ) { 190 /* Northern hemisphere */ 191 if( beforeStandardDate && afterDaylightDate ) 192 retval = TIME_ZONE_ID_DAYLIGHT; 193 } else /* Down south */ 194 if( beforeStandardDate || afterDaylightDate ) 195 retval = TIME_ZONE_ID_DAYLIGHT; 196 } else 197 /* No transition date */ 198 retval = TIME_ZONE_ID_UNKNOWN; 199 200 return retval; 201 } 202 203 /*********************************************************************** 204 * TIME_TimeZoneID 205 * 206 * Calculates whether daylight savings is on now. 207 * 208 * PARAMS 209 * pTzi [in] Timezone info. 210 * 211 * RETURNS 212 * TIME_ZONE_ID_INVALID An error occurred 213 * TIME_ZONE_ID_UNKNOWN There are no transition time known 214 * TIME_ZONE_ID_STANDARD Current time is standard time 215 * TIME_ZONE_ID_DAYLIGHT Current time is daylight savings time 216 */ 217 static DWORD TIME_ZoneID( const TIME_ZONE_INFORMATION *pTzi ) 218 { 219 FILETIME ftTime; 220 GetSystemTimeAsFileTime( &ftTime); 221 return TIME_CompTimeZoneID( pTzi, &ftTime, FALSE); 222 } 223 224 /*********************************************************************** 225 * TIME_GetTimezoneBias 226 * 227 * Calculates the local time bias for a given time zone. 228 * 229 * PARAMS 230 * pTZinfo [in] The time zone data. 231 * lpFileTime [in] The system or local time. 232 * islocal [in] It is local time. 233 * pBias [out] The calculated bias in minutes. 234 * 235 * RETURNS 236 * TRUE when the time zone bias was calculated. 237 */ 238 static BOOL 239 TIME_GetTimezoneBias(const TIME_ZONE_INFORMATION *pTZinfo, FILETIME *lpFileTime, BOOL islocal, LONG *pBias) 240 { 241 LONG bias = pTZinfo->Bias; 242 DWORD tzid = TIME_CompTimeZoneID(pTZinfo, lpFileTime, islocal); 243 244 if( tzid == TIME_ZONE_ID_INVALID) 245 return FALSE; 246 if (tzid == TIME_ZONE_ID_DAYLIGHT) 247 bias += pTZinfo->DaylightBias; 248 else if (tzid == TIME_ZONE_ID_STANDARD) 249 bias += pTZinfo->StandardBias; 250 *pBias = bias; 251 return TRUE; 252 } 253 254 255 /* FUNCTIONS ****************************************************************/ 256 257 /* 258 * @implemented 259 */ 260 DWORD 261 WINAPI 262 GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation) 263 { 264 RTL_TIME_ZONE_INFORMATION TimeZoneInformation; 265 NTSTATUS Status; 266 267 DPRINT("GetTimeZoneInformation()\n"); 268 269 Status = NtQuerySystemInformation(SystemCurrentTimeZoneInformation, 270 &TimeZoneInformation, 271 sizeof(RTL_TIME_ZONE_INFORMATION), 272 NULL); 273 if (!NT_SUCCESS(Status)) 274 { 275 BaseSetLastNTError(Status); 276 return TIME_ZONE_ID_INVALID; 277 } 278 279 lpTimeZoneInformation->Bias = TimeZoneInformation.Bias; 280 281 wcsncpy(lpTimeZoneInformation->StandardName, 282 TimeZoneInformation.StandardName, 283 ARRAYSIZE(lpTimeZoneInformation->StandardName)); 284 lpTimeZoneInformation->StandardDate.wYear = TimeZoneInformation.StandardDate.Year; 285 lpTimeZoneInformation->StandardDate.wMonth = TimeZoneInformation.StandardDate.Month; 286 lpTimeZoneInformation->StandardDate.wDay = TimeZoneInformation.StandardDate.Day; 287 lpTimeZoneInformation->StandardDate.wHour = TimeZoneInformation.StandardDate.Hour; 288 lpTimeZoneInformation->StandardDate.wMinute = TimeZoneInformation.StandardDate.Minute; 289 lpTimeZoneInformation->StandardDate.wSecond = TimeZoneInformation.StandardDate.Second; 290 lpTimeZoneInformation->StandardDate.wMilliseconds = TimeZoneInformation.StandardDate.Milliseconds; 291 lpTimeZoneInformation->StandardDate.wDayOfWeek = TimeZoneInformation.StandardDate.Weekday; 292 lpTimeZoneInformation->StandardBias = TimeZoneInformation.StandardBias; 293 294 wcsncpy(lpTimeZoneInformation->DaylightName, 295 TimeZoneInformation.DaylightName, 296 ARRAYSIZE(lpTimeZoneInformation->DaylightName)); 297 lpTimeZoneInformation->DaylightDate.wYear = TimeZoneInformation.DaylightDate.Year; 298 lpTimeZoneInformation->DaylightDate.wMonth = TimeZoneInformation.DaylightDate.Month; 299 lpTimeZoneInformation->DaylightDate.wDay = TimeZoneInformation.DaylightDate.Day; 300 lpTimeZoneInformation->DaylightDate.wHour = TimeZoneInformation.DaylightDate.Hour; 301 lpTimeZoneInformation->DaylightDate.wMinute = TimeZoneInformation.DaylightDate.Minute; 302 lpTimeZoneInformation->DaylightDate.wSecond = TimeZoneInformation.DaylightDate.Second; 303 lpTimeZoneInformation->DaylightDate.wMilliseconds = TimeZoneInformation.DaylightDate.Milliseconds; 304 lpTimeZoneInformation->DaylightDate.wDayOfWeek = TimeZoneInformation.DaylightDate.Weekday; 305 lpTimeZoneInformation->DaylightBias = TimeZoneInformation.DaylightBias; 306 307 return TIME_ZoneID(lpTimeZoneInformation); 308 } 309 310 311 /* 312 * @implemented 313 */ 314 BOOL 315 WINAPI 316 SetTimeZoneInformation(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation) 317 { 318 RTL_TIME_ZONE_INFORMATION TimeZoneInformation; 319 NTSTATUS Status; 320 321 DPRINT("SetTimeZoneInformation()\n"); 322 323 TimeZoneInformation.Bias = lpTimeZoneInformation->Bias; 324 325 wcsncpy(TimeZoneInformation.StandardName, 326 lpTimeZoneInformation->StandardName, 327 ARRAYSIZE(TimeZoneInformation.StandardName)); 328 TimeZoneInformation.StandardDate.Year = lpTimeZoneInformation->StandardDate.wYear; 329 TimeZoneInformation.StandardDate.Month = lpTimeZoneInformation->StandardDate.wMonth; 330 TimeZoneInformation.StandardDate.Day = lpTimeZoneInformation->StandardDate.wDay; 331 TimeZoneInformation.StandardDate.Hour = lpTimeZoneInformation->StandardDate.wHour; 332 TimeZoneInformation.StandardDate.Minute = lpTimeZoneInformation->StandardDate.wMinute; 333 TimeZoneInformation.StandardDate.Second = lpTimeZoneInformation->StandardDate.wSecond; 334 TimeZoneInformation.StandardDate.Milliseconds = lpTimeZoneInformation->StandardDate.wMilliseconds; 335 TimeZoneInformation.StandardDate.Weekday = lpTimeZoneInformation->StandardDate.wDayOfWeek; 336 TimeZoneInformation.StandardBias = lpTimeZoneInformation->StandardBias; 337 338 wcsncpy(TimeZoneInformation.DaylightName, 339 lpTimeZoneInformation->DaylightName, 340 ARRAYSIZE(TimeZoneInformation.DaylightName)); 341 TimeZoneInformation.DaylightDate.Year = lpTimeZoneInformation->DaylightDate.wYear; 342 TimeZoneInformation.DaylightDate.Month = lpTimeZoneInformation->DaylightDate.wMonth; 343 TimeZoneInformation.DaylightDate.Day = lpTimeZoneInformation->DaylightDate.wDay; 344 TimeZoneInformation.DaylightDate.Hour = lpTimeZoneInformation->DaylightDate.wHour; 345 TimeZoneInformation.DaylightDate.Minute = lpTimeZoneInformation->DaylightDate.wMinute; 346 TimeZoneInformation.DaylightDate.Second = lpTimeZoneInformation->DaylightDate.wSecond; 347 TimeZoneInformation.DaylightDate.Milliseconds = lpTimeZoneInformation->DaylightDate.wMilliseconds; 348 TimeZoneInformation.DaylightDate.Weekday = lpTimeZoneInformation->DaylightDate.wDayOfWeek; 349 TimeZoneInformation.DaylightBias = lpTimeZoneInformation->DaylightBias; 350 351 Status = RtlSetTimeZoneInformation(&TimeZoneInformation); 352 if (!NT_SUCCESS(Status)) 353 { 354 DPRINT1("RtlSetTimeZoneInformation() failed (Status %lx)\n", Status); 355 BaseSetLastNTError(Status); 356 return FALSE; 357 } 358 359 Status = NtSetSystemInformation(SystemCurrentTimeZoneInformation, 360 (PVOID)&TimeZoneInformation, 361 sizeof(RTL_TIME_ZONE_INFORMATION)); 362 if (!NT_SUCCESS(Status)) 363 { 364 DPRINT1("NtSetSystemInformation() failed (Status %lx)\n", Status); 365 BaseSetLastNTError(Status); 366 return FALSE; 367 } 368 369 return TRUE; 370 } 371 372 /* 373 * @implemented 374 */ 375 BOOL 376 WINAPI 377 SystemTimeToTzSpecificLocalTime(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation, 378 CONST SYSTEMTIME *lpUniversalTime, 379 LPSYSTEMTIME lpLocalTime) 380 { 381 TIME_ZONE_INFORMATION TzInfo; 382 FILETIME FileTime; 383 LONGLONG llTime; 384 LONG lBias; 385 386 if (lpTimeZoneInformation != NULL) 387 { 388 TzInfo = *lpTimeZoneInformation; 389 } 390 else 391 { 392 if (GetTimeZoneInformation(&TzInfo) == TIME_ZONE_ID_INVALID) 393 return FALSE; 394 } 395 396 if (!lpUniversalTime || !lpLocalTime) 397 return FALSE; 398 399 if (!SystemTimeToFileTime(lpUniversalTime, &FileTime)) 400 return FALSE; 401 402 FILETIME2LL(&FileTime, llTime) 403 404 if (!TIME_GetTimezoneBias(&TzInfo, &FileTime, FALSE, &lBias)) 405 return FALSE; 406 407 /* convert minutes to 100-nanoseconds-ticks */ 408 llTime -= (LONGLONG)lBias * TICKSPERMIN; 409 410 LL2FILETIME( llTime, &FileTime) 411 412 return FileTimeToSystemTime(&FileTime, lpLocalTime); 413 } 414 415 416 /* 417 * @implemented (Wine 13 sep 2008) 418 */ 419 BOOL 420 WINAPI 421 TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation, 422 LPSYSTEMTIME lpLocalTime, 423 LPSYSTEMTIME lpUniversalTime) 424 { 425 FILETIME ft; 426 LONG lBias; 427 LONGLONG t; 428 TIME_ZONE_INFORMATION tzinfo; 429 430 if (lpTimeZoneInformation != NULL) 431 { 432 tzinfo = *lpTimeZoneInformation; 433 } 434 else 435 { 436 if (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_INVALID) 437 return FALSE; 438 } 439 440 if (!SystemTimeToFileTime(lpLocalTime, &ft)) 441 return FALSE; 442 FILETIME2LL( &ft, t) 443 if (!TIME_GetTimezoneBias(&tzinfo, &ft, TRUE, &lBias)) 444 return FALSE; 445 /* convert minutes to 100-nanoseconds-ticks */ 446 t += (LONGLONG)lBias * TICKSPERMIN; 447 LL2FILETIME( t, &ft) 448 return FileTimeToSystemTime(&ft, lpUniversalTime); 449 } 450 451 /* EOF */ 452