* Sync up to trunk head (r65481).
[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: lib/kernel32/misc/time.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 FUNTIONS **********************************************************/
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 NTSTATUS Status;
265
266 DPRINT("GetTimeZoneInformation()\n");
267
268 Status = NtQuerySystemInformation(SystemCurrentTimeZoneInformation,
269 lpTimeZoneInformation,
270 sizeof(TIME_ZONE_INFORMATION),
271 NULL);
272 if (!NT_SUCCESS(Status))
273 {
274 BaseSetLastNTError(Status);
275 return TIME_ZONE_ID_INVALID;
276 }
277
278 return TIME_ZoneID(lpTimeZoneInformation);
279 }
280
281
282 /*
283 * @implemented
284 */
285 BOOL
286 WINAPI
287 SetTimeZoneInformation(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation)
288 {
289 NTSTATUS Status;
290
291 DPRINT("SetTimeZoneInformation()\n");
292
293 Status = RtlSetTimeZoneInformation((LPTIME_ZONE_INFORMATION)lpTimeZoneInformation);
294 if (!NT_SUCCESS(Status))
295 {
296 DPRINT1("RtlSetTimeZoneInformation() failed (Status %lx)\n", Status);
297 BaseSetLastNTError(Status);
298 return FALSE;
299 }
300
301 Status = NtSetSystemInformation(SystemCurrentTimeZoneInformation,
302 (PVOID)lpTimeZoneInformation,
303 sizeof(TIME_ZONE_INFORMATION));
304 if (!NT_SUCCESS(Status))
305 {
306 DPRINT1("NtSetSystemInformation() failed (Status %lx)\n", Status);
307 BaseSetLastNTError(Status);
308 return FALSE;
309 }
310
311 return TRUE;
312 }
313
314 /*
315 * @implemented
316 */
317 BOOL
318 WINAPI
319 SystemTimeToTzSpecificLocalTime(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation,
320 CONST SYSTEMTIME *lpUniversalTime,
321 LPSYSTEMTIME lpLocalTime)
322 {
323 TIME_ZONE_INFORMATION TzInfo;
324 FILETIME FileTime;
325 LONGLONG llTime;
326 LONG lBias;
327
328 if (lpTimeZoneInformation != NULL)
329 {
330 TzInfo = *lpTimeZoneInformation;
331 }
332 else
333 {
334 if (GetTimeZoneInformation(&TzInfo) == TIME_ZONE_ID_INVALID)
335 return FALSE;
336 }
337
338 if (!lpUniversalTime || !lpLocalTime)
339 return FALSE;
340
341 if (!SystemTimeToFileTime(lpUniversalTime, &FileTime))
342 return FALSE;
343
344 FILETIME2LL(&FileTime, llTime)
345
346 if (!TIME_GetTimezoneBias(&TzInfo, &FileTime, FALSE, &lBias))
347 return FALSE;
348
349 /* convert minutes to 100-nanoseconds-ticks */
350 llTime -= (LONGLONG)lBias * TICKSPERMIN;
351
352 LL2FILETIME( llTime, &FileTime)
353
354 return FileTimeToSystemTime(&FileTime, lpLocalTime);
355 }
356
357
358 /*
359 * @implemented (Wine 13 sep 2008)
360 */
361 BOOL
362 WINAPI
363 TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,
364 LPSYSTEMTIME lpLocalTime,
365 LPSYSTEMTIME lpUniversalTime)
366 {
367 FILETIME ft;
368 LONG lBias;
369 LONGLONG t;
370 TIME_ZONE_INFORMATION tzinfo;
371
372 if (lpTimeZoneInformation != NULL)
373 {
374 tzinfo = *lpTimeZoneInformation;
375 }
376 else
377 {
378 if (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_INVALID)
379 return FALSE;
380 }
381
382 if (!SystemTimeToFileTime(lpLocalTime, &ft))
383 return FALSE;
384 FILETIME2LL( &ft, t)
385 if (!TIME_GetTimezoneBias(&tzinfo, &ft, TRUE, &lBias))
386 return FALSE;
387 /* convert minutes to 100-nanoseconds-ticks */
388 t += (LONGLONG)lBias * TICKSPERMIN;
389 LL2FILETIME( t, &ft)
390 return FileTimeToSystemTime(&ft, lpUniversalTime);
391 }
392
393 /* EOF */