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