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
IsLeapYear(int Year)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
TIME_DayLightCompareDate(const SYSTEMTIME * date,const SYSTEMTIME * compareDate)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
TIME_CompTimeZoneID(const TIME_ZONE_INFORMATION * pTZinfo,FILETIME * lpFileTime,BOOL islocal)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 */
TIME_ZoneID(const TIME_ZONE_INFORMATION * pTzi)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
TIME_GetTimezoneBias(const TIME_ZONE_INFORMATION * pTZinfo,FILETIME * lpFileTime,BOOL islocal,LONG * pBias)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
GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)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
SetTimeZoneInformation(CONST TIME_ZONE_INFORMATION * lpTimeZoneInformation)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
SystemTimeToTzSpecificLocalTime(CONST TIME_ZONE_INFORMATION * lpTimeZoneInformation,CONST SYSTEMTIME * lpUniversalTime,LPSYSTEMTIME lpLocalTime)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
TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,LPSYSTEMTIME lpLocalTime,LPSYSTEMTIME lpUniversalTime)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