8869848d491fb21dcb8790c0fbe1f1dc5893d26a
[reactos.git] / dll / win32 / kernel32 / wine / timezone.c
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.wDayOfWeek = TimeZoneInformation.StandardDate.Weekday;
291 lpTimeZoneInformation->StandardBias = TimeZoneInformation.StandardBias;
292
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;
304
305 return TIME_ZoneID(lpTimeZoneInformation);
306 }
307
308
309 /*
310 * @implemented
311 */
312 BOOL
313 WINAPI
314 SetTimeZoneInformation(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation)
315 {
316 RTL_TIME_ZONE_INFORMATION TimeZoneInformation;
317 NTSTATUS Status;
318
319 DPRINT("SetTimeZoneInformation()\n");
320
321 TimeZoneInformation.Bias = lpTimeZoneInformation->Bias;
322
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;
334
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;
346
347 Status = RtlSetTimeZoneInformation(&TimeZoneInformation);
348 if (!NT_SUCCESS(Status))
349 {
350 DPRINT1("RtlSetTimeZoneInformation() failed (Status %lx)\n", Status);
351 BaseSetLastNTError(Status);
352 return FALSE;
353 }
354
355 Status = NtSetSystemInformation(SystemCurrentTimeZoneInformation,
356 (PVOID)&TimeZoneInformation,
357 sizeof(RTL_TIME_ZONE_INFORMATION));
358 if (!NT_SUCCESS(Status))
359 {
360 DPRINT1("NtSetSystemInformation() failed (Status %lx)\n", Status);
361 BaseSetLastNTError(Status);
362 return FALSE;
363 }
364
365 return TRUE;
366 }
367
368 /*
369 * @implemented
370 */
371 BOOL
372 WINAPI
373 SystemTimeToTzSpecificLocalTime(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation,
374 CONST SYSTEMTIME *lpUniversalTime,
375 LPSYSTEMTIME lpLocalTime)
376 {
377 TIME_ZONE_INFORMATION TzInfo;
378 FILETIME FileTime;
379 LONGLONG llTime;
380 LONG lBias;
381
382 if (lpTimeZoneInformation != NULL)
383 {
384 TzInfo = *lpTimeZoneInformation;
385 }
386 else
387 {
388 if (GetTimeZoneInformation(&TzInfo) == TIME_ZONE_ID_INVALID)
389 return FALSE;
390 }
391
392 if (!lpUniversalTime || !lpLocalTime)
393 return FALSE;
394
395 if (!SystemTimeToFileTime(lpUniversalTime, &FileTime))
396 return FALSE;
397
398 FILETIME2LL(&FileTime, llTime)
399
400 if (!TIME_GetTimezoneBias(&TzInfo, &FileTime, FALSE, &lBias))
401 return FALSE;
402
403 /* convert minutes to 100-nanoseconds-ticks */
404 llTime -= (LONGLONG)lBias * TICKSPERMIN;
405
406 LL2FILETIME( llTime, &FileTime)
407
408 return FileTimeToSystemTime(&FileTime, lpLocalTime);
409 }
410
411
412 /*
413 * @implemented (Wine 13 sep 2008)
414 */
415 BOOL
416 WINAPI
417 TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,
418 LPSYSTEMTIME lpLocalTime,
419 LPSYSTEMTIME lpUniversalTime)
420 {
421 FILETIME ft;
422 LONG lBias;
423 LONGLONG t;
424 TIME_ZONE_INFORMATION tzinfo;
425
426 if (lpTimeZoneInformation != NULL)
427 {
428 tzinfo = *lpTimeZoneInformation;
429 }
430 else
431 {
432 if (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_INVALID)
433 return FALSE;
434 }
435
436 if (!SystemTimeToFileTime(lpLocalTime, &ft))
437 return FALSE;
438 FILETIME2LL( &ft, t)
439 if (!TIME_GetTimezoneBias(&tzinfo, &ft, TRUE, &lBias))
440 return FALSE;
441 /* convert minutes to 100-nanoseconds-ticks */
442 t += (LONGLONG)lBias * TICKSPERMIN;
443 LL2FILETIME( t, &ft)
444 return FileTimeToSystemTime(&ft, lpUniversalTime);
445 }
446
447 /* EOF */