4 * RtlTimeToTimeFields, RtlTimeFieldsToTime and defines are taken from ReactOS and
5 * adapted to wine with special permissions of the author. This code is
6 * Copyright 2002 Rex Jolliff (rex@lvcablemodem.com)
8 * Copyright 1999 Juergen Schmied
9 * Copyright 2007 Dmitry Timoshkov
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/port.h"
35 #ifdef HAVE_SYS_TIME_H
36 # include <sys/time.h>
42 #define NONAMELESSUNION
43 #define NONAMELESSSTRUCT
45 #define WIN32_NO_STATUS
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
50 #include "ntdll_misc.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(ntdll
);
54 static RTL_CRITICAL_SECTION TIME_tz_section
;
55 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
57 0, 0, &TIME_tz_section
,
58 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
59 0, 0, { (DWORD_PTR
)(__FILE__
": TIME_tz_section") }
61 static RTL_CRITICAL_SECTION TIME_tz_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
63 #define SETTIME_MAX_ADJUST 120
65 #define TICKSPERSEC 10000000
66 #define TICKSPERMSEC 10000
67 #define SECSPERDAY 86400
68 #define SECSPERHOUR 3600
70 #define MINSPERHOUR 60
71 #define HOURSPERDAY 24
72 #define EPOCHWEEKDAY 1 /* Jan 1, 1601 was Monday */
74 #define EPOCHYEAR 1601
75 #define DAYSPERNORMALYEAR 365
76 #define DAYSPERLEAPYEAR 366
77 #define MONSPERYEAR 12
78 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
79 #define DAYSPERNORMALCENTURY (365 * 100 + 24)
80 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
82 /* 1601 to 1970 is 369 years plus 89 leap days */
83 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
84 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
85 /* 1601 to 1980 is 379 years plus 91 leap days */
86 #define SECS_1601_TO_1980 ((379 * 365 + 91) * (ULONGLONG)SECSPERDAY)
87 #define TICKS_1601_TO_1980 (SECS_1601_TO_1980 * TICKSPERSEC)
88 /* max ticks that can be represented as Unix time */
89 #define TICKS_1601_TO_UNIX_MAX ((SECS_1601_TO_1970 + INT_MAX) * TICKSPERSEC)
92 static const int MonthLengths
[2][MONSPERYEAR
] =
94 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
95 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
98 static inline int IsLeapYear(int Year
)
100 return Year
% 4 == 0 && (Year
% 100 != 0 || Year
% 400 == 0) ? 1 : 0;
103 /******************************************************************************
104 * RtlTimeToTimeFields [NTDLL.@]
106 * Convert a time into a TIME_FIELDS structure.
109 * liTime [I] Time to convert.
110 * TimeFields [O] Destination for the converted time.
115 VOID WINAPI
RtlTimeToTimeFields(
116 const LARGE_INTEGER
*liTime
,
117 PTIME_FIELDS TimeFields
)
120 long int cleaps
, years
, yearday
, months
;
124 /* Extract millisecond from time and convert time into seconds */
125 TimeFields
->Milliseconds
=
126 (CSHORT
) (( liTime
->QuadPart
% TICKSPERSEC
) / TICKSPERMSEC
);
127 Time
= liTime
->QuadPart
/ TICKSPERSEC
;
129 /* The native version of RtlTimeToTimeFields does not take leap seconds
132 /* Split the time into days and seconds within the day */
133 Days
= Time
/ SECSPERDAY
;
134 SecondsInDay
= Time
% SECSPERDAY
;
136 /* compute time of day */
137 TimeFields
->Hour
= (CSHORT
) (SecondsInDay
/ SECSPERHOUR
);
138 SecondsInDay
= SecondsInDay
% SECSPERHOUR
;
139 TimeFields
->Minute
= (CSHORT
) (SecondsInDay
/ SECSPERMIN
);
140 TimeFields
->Second
= (CSHORT
) (SecondsInDay
% SECSPERMIN
);
142 /* compute day of week */
143 TimeFields
->Weekday
= (CSHORT
) ((EPOCHWEEKDAY
+ Days
) % DAYSPERWEEK
);
145 /* compute year, month and day of month. */
146 cleaps
=( 3 * ((4 * Days
+ 1227) / DAYSPERQUADRICENTENNIUM
) + 3 ) / 4;
147 Days
+= 28188 + cleaps
;
148 years
= (20 * Days
- 2442) / (5 * DAYSPERNORMALQUADRENNIUM
);
149 yearday
= Days
- (years
* DAYSPERNORMALQUADRENNIUM
)/4;
150 months
= (64 * yearday
) / 1959;
151 /* the result is based on a year starting on March.
152 * To convert take 12 from Januari and Februari and
153 * increase the year by one. */
155 TimeFields
->Month
= months
- 1;
156 TimeFields
->Year
= years
+ 1524;
158 TimeFields
->Month
= months
- 13;
159 TimeFields
->Year
= years
+ 1525;
161 /* calculation of day of month is based on the wonderful
162 * sequence of INT( n * 30.6): it reproduces the
163 * 31-30-31-30-31-31 month lengths exactly for small n's */
164 TimeFields
->Day
= yearday
- (1959 * months
) / 64 ;
168 /******************************************************************************
169 * RtlTimeFieldsToTime [NTDLL.@]
171 * Convert a TIME_FIELDS structure into a time.
174 * ftTimeFields [I] TIME_FIELDS structure to convert.
175 * Time [O] Destination for the converted time.
181 BOOLEAN WINAPI
RtlTimeFieldsToTime(
182 PTIME_FIELDS tfTimeFields
,
185 int month
, year
, cleaps
, day
;
187 /* FIXME: normalize the TIME_FIELDS structure here */
188 /* No, native just returns 0 (error) if the fields are not */
189 if( tfTimeFields
->Milliseconds
< 0 || tfTimeFields
->Milliseconds
> 999 ||
190 tfTimeFields
->Second
< 0 || tfTimeFields
->Second
> 59 ||
191 tfTimeFields
->Minute
< 0 || tfTimeFields
->Minute
> 59 ||
192 tfTimeFields
->Hour
< 0 || tfTimeFields
->Hour
> 23 ||
193 tfTimeFields
->Month
< 1 || tfTimeFields
->Month
> 12 ||
194 tfTimeFields
->Day
< 1 ||
195 tfTimeFields
->Day
> MonthLengths
196 [ tfTimeFields
->Month
==2 || IsLeapYear(tfTimeFields
->Year
)]
197 [ tfTimeFields
->Month
- 1] ||
198 tfTimeFields
->Year
< 1601 )
201 /* now calculate a day count from the date
202 * First start counting years from March. This way the leap days
203 * are added at the end of the year, not somewhere in the middle.
204 * Formula's become so much less complicate that way.
205 * To convert: add 12 to the month numbers of Jan and Feb, and
206 * take 1 from the year */
207 if(tfTimeFields
->Month
< 3) {
208 month
= tfTimeFields
->Month
+ 13;
209 year
= tfTimeFields
->Year
- 1;
211 month
= tfTimeFields
->Month
+ 1;
212 year
= tfTimeFields
->Year
;
214 cleaps
= (3 * (year
/ 100) + 3) / 4; /* nr of "century leap years"*/
215 day
= (36525 * year
) / 100 - cleaps
+ /* year * dayperyr, corrected */
216 (1959 * month
) / 64 + /* months * daypermonth */
217 tfTimeFields
->Day
- /* day of the month */
218 584817 ; /* zero that on 1601-01-01 */
221 Time
->QuadPart
= (((((LONGLONG
) day
* HOURSPERDAY
+
222 tfTimeFields
->Hour
) * MINSPERHOUR
+
223 tfTimeFields
->Minute
) * SECSPERMIN
+
224 tfTimeFields
->Second
) * 1000 +
225 tfTimeFields
->Milliseconds
) * TICKSPERMSEC
;
230 /***********************************************************************
231 * TIME_GetBias [internal]
233 * Helper function calculates delta local time from UTC.
236 * utc [I] The current utc time.
237 * pdaylight [I] Local daylight.
240 * The bias for the current timezone.
242 static int TIME_GetBias(time_t utc
, int *pdaylight
)
245 static time_t last_utc
;
246 static int last_bias
;
247 static int last_daylight
;
250 RtlEnterCriticalSection( &TIME_tz_section
);
253 ptm
= localtime(&utc
);
254 last_daylight
= ptm
->tm_isdst
; /* daylight for local timezone */
256 ptm
->tm_isdst
= last_daylight
; /* use local daylight, not that of Greenwich */
258 last_bias
= (int)(utc
- mktime(ptm
));
261 *pdaylight
= last_daylight
;
264 RtlLeaveCriticalSection( &TIME_tz_section
);
268 /******************************************************************************
269 * RtlLocalTimeToSystemTime [NTDLL.@]
271 * Convert a local time into system time.
274 * LocalTime [I] Local time to convert.
275 * SystemTime [O] Destination for the converted time.
278 * Success: STATUS_SUCCESS.
279 * Failure: An NTSTATUS error code indicating the problem.
281 NTSTATUS WINAPI
RtlLocalTimeToSystemTime( const LARGE_INTEGER
*LocalTime
,
282 PLARGE_INTEGER SystemTime
)
287 TRACE("(%p, %p)\n", LocalTime
, SystemTime
);
290 bias
= TIME_GetBias(gmt
, &daylight
);
292 SystemTime
->QuadPart
= LocalTime
->QuadPart
- bias
* (LONGLONG
)TICKSPERSEC
;
293 return STATUS_SUCCESS
;
296 /******************************************************************************
297 * RtlSystemTimeToLocalTime [NTDLL.@]
299 * Convert a system time into a local time.
302 * SystemTime [I] System time to convert.
303 * LocalTime [O] Destination for the converted time.
306 * Success: STATUS_SUCCESS.
307 * Failure: An NTSTATUS error code indicating the problem.
309 NTSTATUS WINAPI
RtlSystemTimeToLocalTime( const LARGE_INTEGER
*SystemTime
,
310 PLARGE_INTEGER LocalTime
)
315 TRACE("(%p, %p)\n", SystemTime
, LocalTime
);
318 bias
= TIME_GetBias(gmt
, &daylight
);
320 LocalTime
->QuadPart
= SystemTime
->QuadPart
+ bias
* (LONGLONG
)TICKSPERSEC
;
321 return STATUS_SUCCESS
;
324 /******************************************************************************
325 * RtlTimeToSecondsSince1970 [NTDLL.@]
327 * Convert a time into a count of seconds since 1970.
330 * Time [I] Time to convert.
331 * Seconds [O] Destination for the converted time.
335 * Failure: FALSE, if the resulting value will not fit in a DWORD.
337 BOOLEAN WINAPI
RtlTimeToSecondsSince1970( const LARGE_INTEGER
*Time
, LPDWORD Seconds
)
339 ULONGLONG tmp
= ((ULONGLONG
)Time
->u
.HighPart
<< 32) | Time
->u
.LowPart
;
340 tmp
= RtlLargeIntegerDivide( tmp
, TICKSPERSEC
, NULL
);
341 tmp
-= SECS_1601_TO_1970
;
342 if (tmp
> 0xffffffff) return FALSE
;
343 *Seconds
= (DWORD
)tmp
;
347 /******************************************************************************
348 * RtlTimeToSecondsSince1980 [NTDLL.@]
350 * Convert a time into a count of seconds since 1980.
353 * Time [I] Time to convert.
354 * Seconds [O] Destination for the converted time.
358 * Failure: FALSE, if the resulting value will not fit in a DWORD.
360 BOOLEAN WINAPI
RtlTimeToSecondsSince1980( const LARGE_INTEGER
*Time
, LPDWORD Seconds
)
362 ULONGLONG tmp
= ((ULONGLONG
)Time
->u
.HighPart
<< 32) | Time
->u
.LowPart
;
363 tmp
= RtlLargeIntegerDivide( tmp
, TICKSPERSEC
, NULL
);
364 tmp
-= SECS_1601_TO_1980
;
365 if (tmp
> 0xffffffff) return FALSE
;
366 *Seconds
= (DWORD
)tmp
;
370 /******************************************************************************
371 * RtlSecondsSince1970ToTime [NTDLL.@]
373 * Convert a count of seconds since 1970 to a time.
376 * Seconds [I] Time to convert.
377 * Time [O] Destination for the converted time.
382 void WINAPI
RtlSecondsSince1970ToTime( DWORD Seconds
, LARGE_INTEGER
*Time
)
384 ULONGLONG secs
= Seconds
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1970
;
385 Time
->u
.LowPart
= (DWORD
)secs
;
386 Time
->u
.HighPart
= (DWORD
)(secs
>> 32);
389 /******************************************************************************
390 * RtlSecondsSince1980ToTime [NTDLL.@]
392 * Convert a count of seconds since 1980 to a time.
395 * Seconds [I] Time to convert.
396 * Time [O] Destination for the converted time.
401 void WINAPI
RtlSecondsSince1980ToTime( DWORD Seconds
, LARGE_INTEGER
*Time
)
403 ULONGLONG secs
= Seconds
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1980
;
404 Time
->u
.LowPart
= (DWORD
)secs
;
405 Time
->u
.HighPart
= (DWORD
)(secs
>> 32);
408 /******************************************************************************
409 * RtlTimeToElapsedTimeFields [NTDLL.@]
411 * Convert a time to a count of elapsed seconds.
414 * Time [I] Time to convert.
415 * TimeFields [O] Destination for the converted time.
420 void WINAPI
RtlTimeToElapsedTimeFields( const LARGE_INTEGER
*Time
, PTIME_FIELDS TimeFields
)
425 time
= RtlExtendedLargeIntegerDivide( Time
->QuadPart
, TICKSPERSEC
, &rem
);
426 TimeFields
->Milliseconds
= rem
/ TICKSPERMSEC
;
428 /* time is now in seconds */
429 TimeFields
->Year
= 0;
430 TimeFields
->Month
= 0;
431 TimeFields
->Day
= RtlExtendedLargeIntegerDivide( time
, SECSPERDAY
, &rem
);
433 /* rem is now the remaining seconds in the last day */
434 TimeFields
->Second
= rem
% 60;
436 TimeFields
->Minute
= rem
% 60;
437 TimeFields
->Hour
= rem
/ 60;
440 /***********************************************************************
441 * NtQuerySystemTime [NTDLL.@]
442 * ZwQuerySystemTime [NTDLL.@]
444 * Get the current system time.
447 * Time [O] Destination for the current system time.
450 * Success: STATUS_SUCCESS.
451 * Failure: An NTSTATUS error code indicating the problem.
453 NTSTATUS WINAPI
NtQuerySystemTime( PLARGE_INTEGER Time
)
457 gettimeofday( &now
, 0 );
458 Time
->QuadPart
= now
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1970
;
459 Time
->QuadPart
+= now
.tv_usec
* 10;
460 return STATUS_SUCCESS
;
463 /******************************************************************************
464 * NtQueryPerformanceCounter [NTDLL.@]
466 * Note: Windows uses a timer clocked at a multiple of 1193182 Hz. There is a
467 * good number of applications that crash when the returned frequency is either
468 * lower or higher than what Windows gives. Also too high counter values are
469 * reported to give problems.
471 NTSTATUS WINAPI
NtQueryPerformanceCounter( PLARGE_INTEGER Counter
, PLARGE_INTEGER Frequency
)
475 if (!Counter
) return STATUS_ACCESS_VIOLATION
;
477 /* convert a counter that increments at a rate of 10 MHz
478 * to one of 1.193182 MHz, with some care for arithmetic
479 * overflow and good accuracy (21/176 = 0.11931818) */
480 NtQuerySystemTime( &now
);
481 Counter
->QuadPart
= ((now
.QuadPart
- server_start_time
) * 21) / 176;
482 if (Frequency
) Frequency
->QuadPart
= 1193182;
483 return STATUS_SUCCESS
;
487 /******************************************************************************
488 * NtGetTickCount (NTDLL.@)
489 * ZwGetTickCount (NTDLL.@)
491 ULONG WINAPI
NtGetTickCount(void)
495 NtQuerySystemTime( &now
);
496 return (now
.QuadPart
- server_start_time
) / 10000;
499 /* calculate the mday of dst change date, so that for instance Sun 5 Oct 2007
500 * (last Sunday in October of 2007) becomes Sun Oct 28 2007
502 * Note: year, day and month must be in unix format.
504 static int weekday_to_mday(int year
, int day
, int mon
, int day_of_week
)
510 /* find first day in the month matching week day of the date */
511 memset(&date
, 0, sizeof(date
));
520 } while (date
.tm_wday
!= day_of_week
|| date
.tm_mon
!= mon
);
524 /* find number of week days in the month matching week day of the date */
525 wday
= 1; /* 1 - 1st, ...., 5 - last */
532 tm
= localtime(&tmp
);
533 if (tm
->tm_mon
!= mon
)
542 static BOOL
match_tz_date(const RTL_SYSTEM_TIME
*st
, const RTL_SYSTEM_TIME
*reg_st
)
546 if (st
->wMonth
!= reg_st
->wMonth
) return FALSE
;
548 if (!st
->wMonth
) return TRUE
; /* no transition dates */
551 if (!reg_st
->wYear
) /* date in a day-of-week format */
552 wDay
= weekday_to_mday(st
->wYear
- 1900, reg_st
->wDay
, reg_st
->wMonth
- 1, reg_st
->wDayOfWeek
);
554 if (st
->wDay
!= wDay
||
555 st
->wHour
!= reg_st
->wHour
||
556 st
->wMinute
!= reg_st
->wMinute
||
557 st
->wSecond
!= reg_st
->wSecond
||
558 st
->wMilliseconds
!= reg_st
->wMilliseconds
) return FALSE
;
563 static BOOL
match_tz_info(const RTL_TIME_ZONE_INFORMATION
*tzi
, const RTL_TIME_ZONE_INFORMATION
*reg_tzi
)
565 if (tzi
->Bias
== reg_tzi
->Bias
&&
566 match_tz_date(&tzi
->StandardDate
, ®_tzi
->StandardDate
) &&
567 match_tz_date(&tzi
->DaylightDate
, ®_tzi
->DaylightDate
))
573 static BOOL
reg_query_value(HKEY hkey
, LPCWSTR name
, DWORD type
, void *data
, DWORD count
)
575 UNICODE_STRING nameW
;
577 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buf
;
579 if (count
> sizeof(buf
) - sizeof(KEY_VALUE_PARTIAL_INFORMATION
))
582 RtlInitUnicodeString(&nameW
, name
);
584 if (NtQueryValueKey(hkey
, &nameW
, KeyValuePartialInformation
,
585 buf
, sizeof(buf
), &count
))
588 if (info
->Type
!= type
) return FALSE
;
590 memcpy(data
, info
->Data
, info
->DataLength
);
594 static void find_reg_tz_info(RTL_TIME_ZONE_INFORMATION
*tzi
)
596 static const WCHAR Time_ZonesW
[] = { 'M','a','c','h','i','n','e','\\',
597 'S','o','f','t','w','a','r','e','\\',
598 'M','i','c','r','o','s','o','f','t','\\',
599 'W','i','n','d','o','w','s',' ','N','T','\\',
600 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
601 'T','i','m','e',' ','Z','o','n','e','s',0 };
604 OBJECT_ATTRIBUTES attr
;
605 UNICODE_STRING nameW
;
608 attr
.Length
= sizeof(attr
);
609 attr
.RootDirectory
= 0;
610 attr
.ObjectName
= &nameW
;
612 attr
.SecurityDescriptor
= NULL
;
613 attr
.SecurityQualityOfService
= NULL
;
614 RtlInitUnicodeString(&nameW
, Time_ZonesW
);
615 if (NtOpenKey(&hkey
, KEY_READ
, &attr
))
617 WARN("Unable to open the time zones key\n");
623 nameW
.Length
= sizeof(buf
);
624 nameW
.MaximumLength
= sizeof(buf
);
626 while (!RtlpNtEnumerateSubKey(hkey
, &nameW
, idx
++))
628 static const WCHAR stdW
[] = { 'S','t','d',0 };
629 static const WCHAR dltW
[] = { 'D','l','t',0 };
630 static const WCHAR tziW
[] = { 'T','Z','I',0 };
631 RTL_TIME_ZONE_INFORMATION reg_tzi
;
638 RTL_SYSTEM_TIME std_date
;
639 RTL_SYSTEM_TIME dlt_date
;
642 attr
.Length
= sizeof(attr
);
643 attr
.RootDirectory
= hkey
;
644 attr
.ObjectName
= &nameW
;
646 attr
.SecurityDescriptor
= NULL
;
647 attr
.SecurityQualityOfService
= NULL
;
648 if (NtOpenKey(&hSubkey
, KEY_READ
, &attr
))
650 WARN("Unable to open subkey %s\n", debugstr_wn(nameW
.Buffer
, nameW
.Length
/sizeof(WCHAR
)));
654 #define get_value(hkey, name, type, data, len) \
655 if (!reg_query_value(hkey, name, type, data, len)) \
657 WARN("can't read data from %s\n", debugstr_w(name)); \
662 get_value(hSubkey
, stdW
, REG_SZ
, reg_tzi
.StandardName
, sizeof(reg_tzi
.StandardName
));
663 get_value(hSubkey
, dltW
, REG_SZ
, reg_tzi
.DaylightName
, sizeof(reg_tzi
.DaylightName
));
664 get_value(hSubkey
, tziW
, REG_BINARY
, &tz_data
, sizeof(tz_data
));
668 reg_tzi
.Bias
= tz_data
.bias
;
669 reg_tzi
.StandardBias
= tz_data
.std_bias
;
670 reg_tzi
.DaylightBias
= tz_data
.dlt_bias
;
671 reg_tzi
.StandardDate
= tz_data
.std_date
;
672 reg_tzi
.DaylightDate
= tz_data
.dlt_date
;
674 TRACE("%s: bias %d\n", debugstr_wn(nameW
.Buffer
, nameW
.Length
/sizeof(WCHAR
)), reg_tzi
.Bias
);
675 TRACE("std (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
676 reg_tzi
.StandardDate
.wDay
, reg_tzi
.StandardDate
.wMonth
,
677 reg_tzi
.StandardDate
.wYear
, reg_tzi
.StandardDate
.wDayOfWeek
,
678 reg_tzi
.StandardDate
.wHour
, reg_tzi
.StandardDate
.wMinute
,
679 reg_tzi
.StandardDate
.wSecond
, reg_tzi
.StandardDate
.wMilliseconds
,
680 reg_tzi
.StandardBias
);
681 TRACE("dst (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
682 reg_tzi
.DaylightDate
.wDay
, reg_tzi
.DaylightDate
.wMonth
,
683 reg_tzi
.DaylightDate
.wYear
, reg_tzi
.DaylightDate
.wDayOfWeek
,
684 reg_tzi
.DaylightDate
.wHour
, reg_tzi
.DaylightDate
.wMinute
,
685 reg_tzi
.DaylightDate
.wSecond
, reg_tzi
.DaylightDate
.wMilliseconds
,
686 reg_tzi
.DaylightBias
);
690 if (match_tz_info(tzi
, ®_tzi
))
692 memcpy(tzi
, ®_tzi
, sizeof(*tzi
));
698 nameW
.Length
= sizeof(buf
);
699 nameW
.MaximumLength
= sizeof(buf
);
704 FIXME("Can't find matching timezone information in the registry for "
705 "bias %d, std (d/m/y): %u/%02u/%04u, dlt (d/m/y): %u/%02u/%04u\n",
707 tzi
->StandardDate
.wDay
, tzi
->StandardDate
.wMonth
, tzi
->StandardDate
.wYear
,
708 tzi
->DaylightDate
.wDay
, tzi
->DaylightDate
.wMonth
, tzi
->DaylightDate
.wYear
);
711 static time_t find_dst_change(unsigned long min
, unsigned long max
, int *is_dst
)
717 tm
= localtime(&start
);
718 *is_dst
= !tm
->tm_isdst
;
719 TRACE("starting date isdst %d, %s", !*is_dst
, ctime(&start
));
723 time_t pos
= (min
+ max
) / 2;
724 tm
= localtime(&pos
);
726 if (tm
->tm_isdst
!= *is_dst
)
734 static void init_tz_info(RTL_TIME_ZONE_INFORMATION
*tzi
, int *valid_year
)
737 time_t year_start
, year_end
, tmp
, dlt
= 0, std
= 0;
740 year_start
= time(NULL
);
741 tm
= localtime(&year_start
);
743 if (*valid_year
== tm
->tm_year
) return;
745 memset(tzi
, 0, sizeof(*tzi
));
747 TRACE("tz data will be valid through year %d\n", tm
->tm_year
+ 1900);
748 *valid_year
= tm
->tm_year
;
752 tm
->tm_mon
= tm
->tm_hour
= tm
->tm_min
= tm
->tm_sec
= tm
->tm_wday
= tm
->tm_yday
= 0;
753 year_start
= mktime(tm
);
754 TRACE("year_start: %s", ctime(&year_start
));
756 tm
->tm_mday
= tm
->tm_wday
= tm
->tm_yday
= 0;
759 tm
->tm_min
= tm
->tm_sec
= 59;
760 year_end
= mktime(tm
);
761 TRACE("year_end: %s", ctime(&year_end
));
763 tm
= gmtime(&year_start
);
764 tzi
->Bias
= (LONG
)(mktime(tm
) - year_start
) / 60;
765 TRACE("bias: %d\n", tzi
->Bias
);
767 tmp
= find_dst_change(year_start
, year_end
, &is_dst
);
773 tmp
= find_dst_change(tmp
, year_end
, &is_dst
);
779 TRACE("std: %s", ctime(&std
));
780 TRACE("dlt: %s", ctime(&dlt
));
782 if (dlt
== std
|| !dlt
|| !std
)
784 TRACE("there is no daylight saving rules in this time zone\n");
788 tmp
= dlt
- tzi
->Bias
* 60;
790 TRACE("dlt gmtime: %s", asctime(tm
));
792 tzi
->DaylightBias
= -60;
793 tzi
->DaylightDate
.wYear
= tm
->tm_year
+ 1900;
794 tzi
->DaylightDate
.wMonth
= tm
->tm_mon
+ 1;
795 tzi
->DaylightDate
.wDayOfWeek
= tm
->tm_wday
;
796 tzi
->DaylightDate
.wDay
= tm
->tm_mday
;
797 tzi
->DaylightDate
.wHour
= tm
->tm_hour
;
798 tzi
->DaylightDate
.wMinute
= tm
->tm_min
;
799 tzi
->DaylightDate
.wSecond
= tm
->tm_sec
;
800 tzi
->DaylightDate
.wMilliseconds
= 0;
802 TRACE("daylight (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
803 tzi
->DaylightDate
.wDay
, tzi
->DaylightDate
.wMonth
,
804 tzi
->DaylightDate
.wYear
, tzi
->DaylightDate
.wDayOfWeek
,
805 tzi
->DaylightDate
.wHour
, tzi
->DaylightDate
.wMinute
,
806 tzi
->DaylightDate
.wSecond
, tzi
->DaylightDate
.wMilliseconds
,
809 tmp
= std
- tzi
->Bias
* 60 - tzi
->DaylightBias
* 60;
811 TRACE("std gmtime: %s", asctime(tm
));
813 tzi
->StandardBias
= 0;
814 tzi
->StandardDate
.wYear
= tm
->tm_year
+ 1900;
815 tzi
->StandardDate
.wMonth
= tm
->tm_mon
+ 1;
816 tzi
->StandardDate
.wDayOfWeek
= tm
->tm_wday
;
817 tzi
->StandardDate
.wDay
= tm
->tm_mday
;
818 tzi
->StandardDate
.wHour
= tm
->tm_hour
;
819 tzi
->StandardDate
.wMinute
= tm
->tm_min
;
820 tzi
->StandardDate
.wSecond
= tm
->tm_sec
;
821 tzi
->StandardDate
.wMilliseconds
= 0;
823 TRACE("standard (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
824 tzi
->StandardDate
.wDay
, tzi
->StandardDate
.wMonth
,
825 tzi
->StandardDate
.wYear
, tzi
->StandardDate
.wDayOfWeek
,
826 tzi
->StandardDate
.wHour
, tzi
->StandardDate
.wMinute
,
827 tzi
->StandardDate
.wSecond
, tzi
->StandardDate
.wMilliseconds
,
830 find_reg_tz_info(tzi
);
833 /***********************************************************************
834 * RtlQueryTimeZoneInformation [NTDLL.@]
836 * Get information about the current timezone.
839 * tzinfo [O] Destination for the retrieved timezone info.
842 * Success: STATUS_SUCCESS.
843 * Failure: An NTSTATUS error code indicating the problem.
845 NTSTATUS WINAPI
RtlQueryTimeZoneInformation(RTL_TIME_ZONE_INFORMATION
*tzinfo
)
847 static RTL_TIME_ZONE_INFORMATION
*cached_tzi
;
848 static int current_year
= -1;
850 RtlEnterCriticalSection(&TIME_tz_section
);
853 cached_tzi
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*cached_tzi
));
855 init_tz_info(cached_tzi
, ¤t_year
);
856 *tzinfo
= *cached_tzi
;
858 RtlLeaveCriticalSection(&TIME_tz_section
);
860 return STATUS_SUCCESS
;
863 /***********************************************************************
864 * RtlSetTimeZoneInformation [NTDLL.@]
866 * Set the current time zone information.
869 * tzinfo [I] Timezone information to set.
872 * Success: STATUS_SUCCESS.
873 * Failure: An NTSTATUS error code indicating the problem.
876 NTSTATUS WINAPI
RtlSetTimeZoneInformation( const RTL_TIME_ZONE_INFORMATION
*tzinfo
)
878 return STATUS_PRIVILEGE_NOT_HELD
;
881 /***********************************************************************
882 * NtSetSystemTime [NTDLL.@]
883 * ZwSetSystemTime [NTDLL.@]
885 * Set the system time.
888 * NewTime [I] The time to set.
889 * OldTime [O] Optional destination for the previous system time.
892 * Success: STATUS_SUCCESS.
893 * Failure: An NTSTATUS error code indicating the problem.
895 NTSTATUS WINAPI
NtSetSystemTime(const LARGE_INTEGER
*NewTime
, LARGE_INTEGER
*OldTime
)
905 /* Return the old time if necessary */
907 NtQuerySystemTime(OldTime
);
909 RtlTimeToTimeFields(NewTime
, &tf
);
911 /* call gettimeofday to get the current timezone */
912 gettimeofday(&tv
, &tz
);
914 /* get delta local time from utc */
915 bias
= TIME_GetBias(oldsec
, &dst
);
917 /* get the number of seconds */
918 t
.tm_sec
= tf
.Second
;
919 t
.tm_min
= tf
.Minute
;
922 t
.tm_mon
= tf
.Month
- 1;
923 t
.tm_year
= tf
.Year
- 1900;
926 /* correct for timezone and daylight */
929 /* set the new time */
931 tv
.tv_usec
= tf
.Milliseconds
* 1000;
933 /* error and sanity check*/
934 if(sec
== (time_t)-1 || abs((int)(sec
-oldsec
)) > SETTIME_MAX_ADJUST
) {
937 #ifdef HAVE_SETTIMEOFDAY
938 err
= settimeofday(&tv
, NULL
); /* 0 is OK, -1 is error */
940 return STATUS_SUCCESS
;
946 ERR("Cannot set time to %d/%d/%d %d:%d:%d Time adjustment %ld %s\n",
947 tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
,
949 err
== -1 ? "No Permission"
950 : sec
== (time_t)-1 ? "" : "is too large." );
953 return STATUS_INVALID_PARAMETER
;
955 return STATUS_PRIVILEGE_NOT_HELD
;
957 return STATUS_NOT_IMPLEMENTED
;