xref: /reactos/dll/win32/kernel32/wine/timezone.c (revision bd2f3d39)
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