Synchronize with trunk.
[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;
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 + pTZinfo->DaylightBias )
149 * (LONGLONG)TICKSPERMIN;
150 LL2FILETIME( llTime, &ftTemp)
151 lpFileTime = &ftTemp;
152 }
153
154 FileTimeToSystemTime(lpFileTime, &SysTime);
155
156 /* check for daylight savings */
157 ret = TIME_DayLightCompareDate( &SysTime, &pTZinfo->StandardDate);
158 if (ret == -2)
159 return TIME_ZONE_ID_INVALID;
160
161 beforeStandardDate = ret < 0;
162
163 if (!islocal) {
164 llTime -= ( pTZinfo->StandardBias - pTZinfo->DaylightBias )
165 * (LONGLONG)TICKSPERMIN;
166 LL2FILETIME( llTime, &ftTemp)
167 FileTimeToSystemTime(lpFileTime, &SysTime);
168 }
169
170 ret = TIME_DayLightCompareDate( &SysTime, &pTZinfo->DaylightDate);
171 if (ret == -2)
172 return TIME_ZONE_ID_INVALID;
173
174 afterDaylightDate = ret >= 0;
175
176 retval = TIME_ZONE_ID_STANDARD;
177 if( pTZinfo->DaylightDate.wMonth < pTZinfo->StandardDate.wMonth ) {
178 /* Northern hemisphere */
179 if( beforeStandardDate && afterDaylightDate )
180 retval = TIME_ZONE_ID_DAYLIGHT;
181 } else /* Down south */
182 if( beforeStandardDate || afterDaylightDate )
183 retval = TIME_ZONE_ID_DAYLIGHT;
184 } else
185 /* No transition date */
186 retval = TIME_ZONE_ID_UNKNOWN;
187
188 return retval;
189 }
190
191 /***********************************************************************
192 * TIME_TimeZoneID
193 *
194 * Calculates whether daylight savings is on now.
195 *
196 * PARAMS
197 * pTzi [in] Timezone info.
198 *
199 * RETURNS
200 * TIME_ZONE_ID_INVALID An error occurred
201 * TIME_ZONE_ID_UNKNOWN There are no transition time known
202 * TIME_ZONE_ID_STANDARD Current time is standard time
203 * TIME_ZONE_ID_DAYLIGHT Current time is daylight savings time
204 */
205 static DWORD TIME_ZoneID( const TIME_ZONE_INFORMATION *pTzi )
206 {
207 FILETIME ftTime;
208 GetSystemTimeAsFileTime( &ftTime);
209 return TIME_CompTimeZoneID( pTzi, &ftTime, FALSE);
210 }
211
212 /***********************************************************************
213 * TIME_GetTimezoneBias
214 *
215 * Calculates the local time bias for a given time zone.
216 *
217 * PARAMS
218 * pTZinfo [in] The time zone data.
219 * lpFileTime [in] The system or local time.
220 * islocal [in] It is local time.
221 * pBias [out] The calculated bias in minutes.
222 *
223 * RETURNS
224 * TRUE when the time zone bias was calculated.
225 */
226 static BOOL
227 TIME_GetTimezoneBias(const TIME_ZONE_INFORMATION *pTZinfo, FILETIME *lpFileTime, BOOL islocal, LONG *pBias)
228 {
229 LONG bias = pTZinfo->Bias;
230 DWORD tzid = TIME_CompTimeZoneID(pTZinfo, lpFileTime, islocal);
231
232 if( tzid == TIME_ZONE_ID_INVALID)
233 return FALSE;
234 if (tzid == TIME_ZONE_ID_DAYLIGHT)
235 bias += pTZinfo->DaylightBias;
236 else if (tzid == TIME_ZONE_ID_STANDARD)
237 bias += pTZinfo->StandardBias;
238 *pBias = bias;
239 return TRUE;
240 }
241
242
243 /* FUNCTIONS ****************************************************************/
244
245 /*
246 * @implemented
247 */
248 DWORD
249 WINAPI
250 GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
251 {
252 NTSTATUS Status;
253
254 DPRINT("GetTimeZoneInformation()\n");
255
256 Status = NtQuerySystemInformation(SystemCurrentTimeZoneInformation,
257 lpTimeZoneInformation,
258 sizeof(TIME_ZONE_INFORMATION),
259 NULL);
260 if (!NT_SUCCESS(Status))
261 {
262 BaseSetLastNTError(Status);
263 return TIME_ZONE_ID_INVALID;
264 }
265
266 return TIME_ZoneID(lpTimeZoneInformation);
267 }
268
269
270 /*
271 * @implemented
272 */
273 BOOL
274 WINAPI
275 SetTimeZoneInformation(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation)
276 {
277 NTSTATUS Status;
278
279 DPRINT("SetTimeZoneInformation()\n");
280
281 Status = RtlSetTimeZoneInformation((LPTIME_ZONE_INFORMATION)lpTimeZoneInformation);
282 if (!NT_SUCCESS(Status))
283 {
284 DPRINT1("RtlSetTimeZoneInformation() failed (Status %lx)\n", Status);
285 BaseSetLastNTError(Status);
286 return FALSE;
287 }
288
289 Status = NtSetSystemInformation(SystemCurrentTimeZoneInformation,
290 (PVOID)lpTimeZoneInformation,
291 sizeof(TIME_ZONE_INFORMATION));
292 if (!NT_SUCCESS(Status))
293 {
294 DPRINT1("NtSetSystemInformation() failed (Status %lx)\n", Status);
295 BaseSetLastNTError(Status);
296 return FALSE;
297 }
298
299 return TRUE;
300 }
301
302 /*
303 * @implemented
304 */
305 BOOL
306 WINAPI
307 SystemTimeToTzSpecificLocalTime(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation,
308 CONST SYSTEMTIME *lpUniversalTime,
309 LPSYSTEMTIME lpLocalTime)
310 {
311 TIME_ZONE_INFORMATION TzInfo;
312 FILETIME FileTime;
313 LONGLONG llTime;
314 LONG lBias;
315
316 if (lpTimeZoneInformation != NULL)
317 {
318 TzInfo = *lpTimeZoneInformation;
319 }
320 else
321 {
322 if (GetTimeZoneInformation(&TzInfo) == TIME_ZONE_ID_INVALID)
323 return FALSE;
324 }
325
326 if (!lpUniversalTime || !lpLocalTime)
327 return FALSE;
328
329 if (!SystemTimeToFileTime(lpUniversalTime, &FileTime))
330 return FALSE;
331
332 FILETIME2LL(&FileTime, llTime)
333
334 if (!TIME_GetTimezoneBias(&TzInfo, &FileTime, FALSE, &lBias))
335 return FALSE;
336
337 /* convert minutes to 100-nanoseconds-ticks */
338 llTime -= (LONGLONG)lBias * TICKSPERMIN;
339
340 LL2FILETIME( llTime, &FileTime)
341
342 return FileTimeToSystemTime(&FileTime, lpLocalTime);
343 }
344
345
346 /*
347 * @implemented (Wine 13 sep 2008)
348 */
349 BOOL
350 WINAPI
351 TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,
352 LPSYSTEMTIME lpLocalTime,
353 LPSYSTEMTIME lpUniversalTime)
354 {
355 FILETIME ft;
356 LONG lBias;
357 LONGLONG t;
358 TIME_ZONE_INFORMATION tzinfo;
359
360 if (lpTimeZoneInformation != NULL)
361 {
362 tzinfo = *lpTimeZoneInformation;
363 }
364 else
365 {
366 if (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_INVALID)
367 return FALSE;
368 }
369
370 if (!SystemTimeToFileTime(lpLocalTime, &ft))
371 return FALSE;
372 FILETIME2LL( &ft, t)
373 if (!TIME_GetTimezoneBias(&tzinfo, &ft, TRUE, &lBias))
374 return FALSE;
375 /* convert minutes to 100-nanoseconds-ticks */
376 t += (LONGLONG)lBias * TICKSPERMIN;
377 LL2FILETIME( t, &ft)
378 return FileTimeToSystemTime(&ft, lpUniversalTime);
379 }
380
381 /* EOF */