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
7 * DOSDATE and DOSTIME structures from Onno Hovers
12 /* INCLUDES ******************************************************************/
19 /* TYPES *********************************************************************/
21 #define TICKSPERMIN 600000000
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 ;
29 static const int MonthLengths
[2][12] =
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 }
35 /* STATIC FUNCTIONS **********************************************************/
37 static inline int IsLeapYear(int Year
)
39 return Year
% 4 == 0 && (Year
% 100 != 0 || Year
% 400 == 0) ? 1 : 0;
42 /***********************************************************************
43 * TIME_DayLightCompareDate
45 * Compares two dates without looking at the year.
48 * date [in] The local time to compare.
49 * compareDate [in] The daylight savings begin or end date.
53 * -1 if date < compareDate
54 * 0 if date == compareDate
55 * 1 if date > compareDate
56 * -2 if an error occurs
59 TIME_DayLightCompareDate(const SYSTEMTIME
*date
, const SYSTEMTIME
*compareDate
)
61 int limit_day
, dayinsecs
;
63 if (date
->wMonth
< compareDate
->wMonth
)
64 return -1; /* We are in a month before the date limit. */
66 if (date
->wMonth
> compareDate
->wMonth
)
67 return 1; /* We are in a month after the date limit. */
69 /* if year is 0 then date is in day-of-week format, otherwise
72 if (compareDate
->wYear
== 0)
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
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
)]
89 limit_day
= compareDate
->wDay
;
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
;
98 return dayinsecs
< limit_day
? -1 :
99 dayinsecs
> limit_day
? 1 :
100 0; /* date is equal to the date limit. */
103 /***********************************************************************
104 * TIME_CompTimeZoneID
106 * Computes the local time bias for a given time and time zone.
109 * pTZinfo [in] The time zone data.
110 * lpFileTime [in] The system or local time.
111 * islocal [in] it is local time.
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
121 TIME_CompTimeZoneID( const TIME_ZONE_INFORMATION
*pTZinfo
, FILETIME
*lpFileTime
, BOOL islocal
)
124 BOOL beforeStandardDate
, afterDaylightDate
;
125 DWORD retval
= TIME_ZONE_ID_INVALID
;
126 LONGLONG llTime
= 0; /* initialized to prevent gcc complaining */
130 if (pTZinfo
->DaylightDate
.wMonth
!= 0)
132 /* if year is 0 then date is in day-of-week format, otherwise
133 * it's absolute date.
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)))
142 SetLastError(ERROR_INVALID_PARAMETER
);
143 return TIME_ZONE_ID_INVALID
;
147 FILETIME2LL( lpFileTime
, llTime
);
148 llTime
-= pTZinfo
->Bias
* (LONGLONG
)TICKSPERMIN
;
149 LL2FILETIME( llTime
, &ftTemp
)
150 lpFileTime
= &ftTemp
;
153 FileTimeToSystemTime(lpFileTime
, &SysTime
);
154 year
= SysTime
.wYear
;
157 llTime
-= pTZinfo
->DaylightBias
* (LONGLONG
)TICKSPERMIN
;
158 LL2FILETIME( llTime
, &ftTemp
)
159 FileTimeToSystemTime(lpFileTime
, &SysTime
);
162 /* check for daylight savings */
163 if(year
== SysTime
.wYear
) {
164 ret
= TIME_DayLightCompareDate( &SysTime
, &pTZinfo
->StandardDate
);
166 return TIME_ZONE_ID_INVALID
;
168 beforeStandardDate
= ret
< 0;
170 beforeStandardDate
= SysTime
.wYear
< year
;
173 llTime
-= ( pTZinfo
->StandardBias
- pTZinfo
->DaylightBias
)
174 * (LONGLONG
)TICKSPERMIN
;
175 LL2FILETIME( llTime
, &ftTemp
)
176 FileTimeToSystemTime(lpFileTime
, &SysTime
);
179 if(year
== SysTime
.wYear
) {
180 ret
= TIME_DayLightCompareDate( &SysTime
, &pTZinfo
->DaylightDate
);
182 return TIME_ZONE_ID_INVALID
;
184 afterDaylightDate
= ret
>= 0;
186 afterDaylightDate
= SysTime
.wYear
> year
;
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
;
197 /* No transition date */
198 retval
= TIME_ZONE_ID_UNKNOWN
;
203 /***********************************************************************
206 * Calculates whether daylight savings is on now.
209 * pTzi [in] Timezone info.
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
217 static DWORD
TIME_ZoneID( const TIME_ZONE_INFORMATION
*pTzi
)
220 GetSystemTimeAsFileTime( &ftTime
);
221 return TIME_CompTimeZoneID( pTzi
, &ftTime
, FALSE
);
224 /***********************************************************************
225 * TIME_GetTimezoneBias
227 * Calculates the local time bias for a given time zone.
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.
236 * TRUE when the time zone bias was calculated.
239 TIME_GetTimezoneBias(const TIME_ZONE_INFORMATION
*pTZinfo
, FILETIME
*lpFileTime
, BOOL islocal
, LONG
*pBias
)
241 LONG bias
= pTZinfo
->Bias
;
242 DWORD tzid
= TIME_CompTimeZoneID(pTZinfo
, lpFileTime
, islocal
);
244 if( tzid
== TIME_ZONE_ID_INVALID
)
246 if (tzid
== TIME_ZONE_ID_DAYLIGHT
)
247 bias
+= pTZinfo
->DaylightBias
;
248 else if (tzid
== TIME_ZONE_ID_STANDARD
)
249 bias
+= pTZinfo
->StandardBias
;
255 /* FUNCTIONS ****************************************************************/
262 GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation
)
264 RTL_TIME_ZONE_INFORMATION TimeZoneInformation
;
267 DPRINT("GetTimeZoneInformation()\n");
269 Status
= NtQuerySystemInformation(SystemCurrentTimeZoneInformation
,
270 &TimeZoneInformation
,
271 sizeof(RTL_TIME_ZONE_INFORMATION
),
273 if (!NT_SUCCESS(Status
))
275 BaseSetLastNTError(Status
);
276 return TIME_ZONE_ID_INVALID
;
279 lpTimeZoneInformation
->Bias
= TimeZoneInformation
.Bias
;
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
.wDayOfWeek
= TimeZoneInformation
.StandardDate
.Weekday
;
291 lpTimeZoneInformation
->StandardBias
= TimeZoneInformation
.StandardBias
;
293 wcsncpy(lpTimeZoneInformation
->DaylightName
,
294 TimeZoneInformation
.DaylightName
,
295 ARRAYSIZE(lpTimeZoneInformation
->DaylightName
));
296 lpTimeZoneInformation
->DaylightDate
.wYear
= TimeZoneInformation
.DaylightDate
.Year
;
297 lpTimeZoneInformation
->DaylightDate
.wMonth
= TimeZoneInformation
.DaylightDate
.Month
;
298 lpTimeZoneInformation
->DaylightDate
.wDay
= TimeZoneInformation
.DaylightDate
.Day
;
299 lpTimeZoneInformation
->DaylightDate
.wHour
= TimeZoneInformation
.DaylightDate
.Hour
;
300 lpTimeZoneInformation
->DaylightDate
.wMinute
= TimeZoneInformation
.DaylightDate
.Minute
;
301 lpTimeZoneInformation
->DaylightDate
.wSecond
= TimeZoneInformation
.DaylightDate
.Second
;
302 lpTimeZoneInformation
->DaylightDate
.wDayOfWeek
= TimeZoneInformation
.DaylightDate
.Weekday
;
303 lpTimeZoneInformation
->DaylightBias
= TimeZoneInformation
.DaylightBias
;
305 return TIME_ZoneID(lpTimeZoneInformation
);
314 SetTimeZoneInformation(CONST TIME_ZONE_INFORMATION
*lpTimeZoneInformation
)
316 RTL_TIME_ZONE_INFORMATION TimeZoneInformation
;
319 DPRINT("SetTimeZoneInformation()\n");
321 TimeZoneInformation
.Bias
= lpTimeZoneInformation
->Bias
;
323 wcsncpy(TimeZoneInformation
.StandardName
,
324 lpTimeZoneInformation
->StandardName
,
325 ARRAYSIZE(TimeZoneInformation
.StandardName
));
326 TimeZoneInformation
.StandardDate
.Year
= lpTimeZoneInformation
->StandardDate
.wYear
;
327 TimeZoneInformation
.StandardDate
.Month
= lpTimeZoneInformation
->StandardDate
.wMonth
;
328 TimeZoneInformation
.StandardDate
.Day
= lpTimeZoneInformation
->StandardDate
.wDay
;
329 TimeZoneInformation
.StandardDate
.Hour
= lpTimeZoneInformation
->StandardDate
.wHour
;
330 TimeZoneInformation
.StandardDate
.Minute
= lpTimeZoneInformation
->StandardDate
.wMinute
;
331 TimeZoneInformation
.StandardDate
.Second
= lpTimeZoneInformation
->StandardDate
.wSecond
;
332 TimeZoneInformation
.StandardDate
.Weekday
= lpTimeZoneInformation
->StandardDate
.wDayOfWeek
;
333 TimeZoneInformation
.StandardBias
= lpTimeZoneInformation
->StandardBias
;
335 wcsncpy(TimeZoneInformation
.DaylightName
,
336 lpTimeZoneInformation
->DaylightName
,
337 ARRAYSIZE(TimeZoneInformation
.DaylightName
));
338 TimeZoneInformation
.DaylightDate
.Year
= lpTimeZoneInformation
->DaylightDate
.wYear
;
339 TimeZoneInformation
.DaylightDate
.Month
= lpTimeZoneInformation
->DaylightDate
.wMonth
;
340 TimeZoneInformation
.DaylightDate
.Day
= lpTimeZoneInformation
->DaylightDate
.wDay
;
341 TimeZoneInformation
.DaylightDate
.Hour
= lpTimeZoneInformation
->DaylightDate
.wHour
;
342 TimeZoneInformation
.DaylightDate
.Minute
= lpTimeZoneInformation
->DaylightDate
.wMinute
;
343 TimeZoneInformation
.DaylightDate
.Second
= lpTimeZoneInformation
->DaylightDate
.wSecond
;
344 TimeZoneInformation
.DaylightDate
.Weekday
= lpTimeZoneInformation
->DaylightDate
.wDayOfWeek
;
345 TimeZoneInformation
.DaylightBias
= lpTimeZoneInformation
->DaylightBias
;
347 Status
= RtlSetTimeZoneInformation(&TimeZoneInformation
);
348 if (!NT_SUCCESS(Status
))
350 DPRINT1("RtlSetTimeZoneInformation() failed (Status %lx)\n", Status
);
351 BaseSetLastNTError(Status
);
355 Status
= NtSetSystemInformation(SystemCurrentTimeZoneInformation
,
356 (PVOID
)&TimeZoneInformation
,
357 sizeof(RTL_TIME_ZONE_INFORMATION
));
358 if (!NT_SUCCESS(Status
))
360 DPRINT1("NtSetSystemInformation() failed (Status %lx)\n", Status
);
361 BaseSetLastNTError(Status
);
373 SystemTimeToTzSpecificLocalTime(CONST TIME_ZONE_INFORMATION
*lpTimeZoneInformation
,
374 CONST SYSTEMTIME
*lpUniversalTime
,
375 LPSYSTEMTIME lpLocalTime
)
377 TIME_ZONE_INFORMATION TzInfo
;
382 if (lpTimeZoneInformation
!= NULL
)
384 TzInfo
= *lpTimeZoneInformation
;
388 if (GetTimeZoneInformation(&TzInfo
) == TIME_ZONE_ID_INVALID
)
392 if (!lpUniversalTime
|| !lpLocalTime
)
395 if (!SystemTimeToFileTime(lpUniversalTime
, &FileTime
))
398 FILETIME2LL(&FileTime
, llTime
)
400 if (!TIME_GetTimezoneBias(&TzInfo
, &FileTime
, FALSE
, &lBias
))
403 /* convert minutes to 100-nanoseconds-ticks */
404 llTime
-= (LONGLONG
)lBias
* TICKSPERMIN
;
406 LL2FILETIME( llTime
, &FileTime
)
408 return FileTimeToSystemTime(&FileTime
, lpLocalTime
);
413 * @implemented (Wine 13 sep 2008)
417 TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation
,
418 LPSYSTEMTIME lpLocalTime
,
419 LPSYSTEMTIME lpUniversalTime
)
424 TIME_ZONE_INFORMATION tzinfo
;
426 if (lpTimeZoneInformation
!= NULL
)
428 tzinfo
= *lpTimeZoneInformation
;
432 if (GetTimeZoneInformation(&tzinfo
) == TIME_ZONE_ID_INVALID
)
436 if (!SystemTimeToFileTime(lpLocalTime
, &ft
))
439 if (!TIME_GetTimezoneBias(&tzinfo
, &ft
, TRUE
, &lBias
))
441 /* convert minutes to 100-nanoseconds-ticks */
442 t
+= (LONGLONG
)lBias
* TICKSPERMIN
;
444 return FileTimeToSystemTime(&ft
, lpUniversalTime
);