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 # include <mach/mach_time.h>
46 #define WIN32_NO_STATUS
49 #include "wine/exception.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
52 #include "ntdll_misc.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(ntdll
);
56 #define SHORT_TZ_NAME_MAX 8
59 char short_name
[SHORT_TZ_NAME_MAX
];
62 static int init_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION
*tzi
);
64 static RTL_CRITICAL_SECTION TIME_tz_section
;
65 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
67 0, 0, &TIME_tz_section
,
68 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
69 0, 0, { (DWORD_PTR
)(__FILE__
": TIME_tz_section") }
71 static RTL_CRITICAL_SECTION TIME_tz_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
73 #define TICKSPERSEC 10000000
74 #define TICKSPERMSEC 10000
75 #define SECSPERDAY 86400
76 #define SECSPERHOUR 3600
78 #define MINSPERHOUR 60
79 #define HOURSPERDAY 24
80 #define EPOCHWEEKDAY 1 /* Jan 1, 1601 was Monday */
82 #define MONSPERYEAR 12
83 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
84 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
86 /* 1601 to 1970 is 369 years plus 89 leap days */
87 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
88 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
89 /* 1601 to 1980 is 379 years plus 91 leap days */
90 #define SECS_1601_TO_1980 ((379 * 365 + 91) * (ULONGLONG)SECSPERDAY)
91 #define TICKS_1601_TO_1980 (SECS_1601_TO_1980 * TICKSPERSEC)
94 static const int MonthLengths
[2][MONSPERYEAR
] =
96 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
97 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
100 static inline BOOL
IsLeapYear(int Year
)
102 return Year
% 4 == 0 && (Year
% 100 != 0 || Year
% 400 == 0);
105 /* return a monotonic time counter, in Win32 ticks */
106 static inline ULONGLONG
monotonic_counter(void)
111 static mach_timebase_info_data_t timebase
;
113 if (!timebase
.denom
) mach_timebase_info( &timebase
);
114 return mach_absolute_time() * timebase
.numer
/ timebase
.denom
/ 100;
115 #elif defined(HAVE_CLOCK_GETTIME)
117 #ifdef CLOCK_MONOTONIC_RAW
118 if (!clock_gettime( CLOCK_MONOTONIC_RAW
, &ts
))
119 return ts
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ ts
.tv_nsec
/ 100;
121 if (!clock_gettime( CLOCK_MONOTONIC
, &ts
))
122 return ts
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ ts
.tv_nsec
/ 100;
125 gettimeofday( &now
, 0 );
126 return now
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ now
.tv_usec
* 10 + TICKS_1601_TO_1970
- server_start_time
;
129 /******************************************************************************
130 * RtlTimeToTimeFields [NTDLL.@]
132 * Convert a time into a TIME_FIELDS structure.
135 * liTime [I] Time to convert.
136 * TimeFields [O] Destination for the converted time.
141 VOID WINAPI
RtlTimeToTimeFields(
142 const LARGE_INTEGER
*liTime
,
143 PTIME_FIELDS TimeFields
)
146 long int cleaps
, years
, yearday
, months
;
150 /* Extract millisecond from time and convert time into seconds */
151 TimeFields
->Milliseconds
=
152 (CSHORT
) (( liTime
->QuadPart
% TICKSPERSEC
) / TICKSPERMSEC
);
153 Time
= liTime
->QuadPart
/ TICKSPERSEC
;
155 /* The native version of RtlTimeToTimeFields does not take leap seconds
158 /* Split the time into days and seconds within the day */
159 Days
= Time
/ SECSPERDAY
;
160 SecondsInDay
= Time
% SECSPERDAY
;
162 /* compute time of day */
163 TimeFields
->Hour
= (CSHORT
) (SecondsInDay
/ SECSPERHOUR
);
164 SecondsInDay
= SecondsInDay
% SECSPERHOUR
;
165 TimeFields
->Minute
= (CSHORT
) (SecondsInDay
/ SECSPERMIN
);
166 TimeFields
->Second
= (CSHORT
) (SecondsInDay
% SECSPERMIN
);
168 /* compute day of week */
169 TimeFields
->Weekday
= (CSHORT
) ((EPOCHWEEKDAY
+ Days
) % DAYSPERWEEK
);
171 /* compute year, month and day of month. */
172 cleaps
=( 3 * ((4 * Days
+ 1227) / DAYSPERQUADRICENTENNIUM
) + 3 ) / 4;
173 Days
+= 28188 + cleaps
;
174 years
= (20 * Days
- 2442) / (5 * DAYSPERNORMALQUADRENNIUM
);
175 yearday
= Days
- (years
* DAYSPERNORMALQUADRENNIUM
)/4;
176 months
= (64 * yearday
) / 1959;
177 /* the result is based on a year starting on March.
178 * To convert take 12 from Januari and Februari and
179 * increase the year by one. */
181 TimeFields
->Month
= months
- 1;
182 TimeFields
->Year
= years
+ 1524;
184 TimeFields
->Month
= months
- 13;
185 TimeFields
->Year
= years
+ 1525;
187 /* calculation of day of month is based on the wonderful
188 * sequence of INT( n * 30.6): it reproduces the
189 * 31-30-31-30-31-31 month lengths exactly for small n's */
190 TimeFields
->Day
= yearday
- (1959 * months
) / 64 ;
194 /******************************************************************************
195 * RtlTimeFieldsToTime [NTDLL.@]
197 * Convert a TIME_FIELDS structure into a time.
200 * ftTimeFields [I] TIME_FIELDS structure to convert.
201 * Time [O] Destination for the converted time.
207 BOOLEAN WINAPI
RtlTimeFieldsToTime(
208 PTIME_FIELDS tfTimeFields
,
211 int month
, year
, cleaps
, day
;
213 /* FIXME: normalize the TIME_FIELDS structure here */
214 /* No, native just returns 0 (error) if the fields are not */
215 if( tfTimeFields
->Milliseconds
< 0 || tfTimeFields
->Milliseconds
> 999 ||
216 tfTimeFields
->Second
< 0 || tfTimeFields
->Second
> 59 ||
217 tfTimeFields
->Minute
< 0 || tfTimeFields
->Minute
> 59 ||
218 tfTimeFields
->Hour
< 0 || tfTimeFields
->Hour
> 23 ||
219 tfTimeFields
->Month
< 1 || tfTimeFields
->Month
> 12 ||
220 tfTimeFields
->Day
< 1 ||
221 tfTimeFields
->Day
> MonthLengths
222 [ tfTimeFields
->Month
==2 || IsLeapYear(tfTimeFields
->Year
)]
223 [ tfTimeFields
->Month
- 1] ||
224 tfTimeFields
->Year
< 1601 )
227 /* now calculate a day count from the date
228 * First start counting years from March. This way the leap days
229 * are added at the end of the year, not somewhere in the middle.
230 * Formula's become so much less complicate that way.
231 * To convert: add 12 to the month numbers of Jan and Feb, and
232 * take 1 from the year */
233 if(tfTimeFields
->Month
< 3) {
234 month
= tfTimeFields
->Month
+ 13;
235 year
= tfTimeFields
->Year
- 1;
237 month
= tfTimeFields
->Month
+ 1;
238 year
= tfTimeFields
->Year
;
240 cleaps
= (3 * (year
/ 100) + 3) / 4; /* nr of "century leap years"*/
241 day
= (36525 * year
) / 100 - cleaps
+ /* year * dayperyr, corrected */
242 (1959 * month
) / 64 + /* months * daypermonth */
243 tfTimeFields
->Day
- /* day of the month */
244 584817 ; /* zero that on 1601-01-01 */
247 Time
->QuadPart
= (((((LONGLONG
) day
* HOURSPERDAY
+
248 tfTimeFields
->Hour
) * MINSPERHOUR
+
249 tfTimeFields
->Minute
) * SECSPERMIN
+
250 tfTimeFields
->Second
) * 1000 +
251 tfTimeFields
->Milliseconds
) * TICKSPERMSEC
;
256 /***********************************************************************
257 * TIME_GetBias [internal]
259 * Helper function calculates delta local time from UTC.
262 * utc [I] The current utc time.
263 * pdaylight [I] Local daylight.
266 * The bias for the current timezone.
268 static LONG
TIME_GetBias(void)
270 static time_t last_utc
;
271 static LONG last_bias
;
277 RtlEnterCriticalSection( &TIME_tz_section
);
280 RTL_DYNAMIC_TIME_ZONE_INFORMATION tzi
;
281 int is_dst
= init_tz_info( &tzi
);
284 last_bias
= tzi
.Bias
;
285 last_bias
+= is_dst
? tzi
.DaylightBias
: tzi
.StandardBias
;
286 last_bias
*= SECSPERMIN
;
291 RtlLeaveCriticalSection( &TIME_tz_section
);
295 /******************************************************************************
296 * RtlLocalTimeToSystemTime [NTDLL.@]
298 * Convert a local time into system time.
301 * LocalTime [I] Local time to convert.
302 * SystemTime [O] Destination for the converted time.
305 * Success: STATUS_SUCCESS.
306 * Failure: An NTSTATUS error code indicating the problem.
308 NTSTATUS WINAPI
RtlLocalTimeToSystemTime( const LARGE_INTEGER
*LocalTime
,
309 PLARGE_INTEGER SystemTime
)
313 TRACE("(%p, %p)\n", LocalTime
, SystemTime
);
315 bias
= TIME_GetBias();
316 SystemTime
->QuadPart
= LocalTime
->QuadPart
+ bias
* (LONGLONG
)TICKSPERSEC
;
317 return STATUS_SUCCESS
;
320 /******************************************************************************
321 * RtlSystemTimeToLocalTime [NTDLL.@]
323 * Convert a system time into a local time.
326 * SystemTime [I] System time to convert.
327 * LocalTime [O] Destination for the converted time.
330 * Success: STATUS_SUCCESS.
331 * Failure: An NTSTATUS error code indicating the problem.
333 NTSTATUS WINAPI
RtlSystemTimeToLocalTime( const LARGE_INTEGER
*SystemTime
,
334 PLARGE_INTEGER LocalTime
)
338 TRACE("(%p, %p)\n", SystemTime
, LocalTime
);
340 bias
= TIME_GetBias();
341 LocalTime
->QuadPart
= SystemTime
->QuadPart
- bias
* (LONGLONG
)TICKSPERSEC
;
342 return STATUS_SUCCESS
;
345 /******************************************************************************
346 * RtlTimeToSecondsSince1970 [NTDLL.@]
348 * Convert a time into a count of seconds since 1970.
351 * Time [I] Time to convert.
352 * Seconds [O] Destination for the converted time.
356 * Failure: FALSE, if the resulting value will not fit in a DWORD.
358 BOOLEAN WINAPI
RtlTimeToSecondsSince1970( const LARGE_INTEGER
*Time
, LPDWORD Seconds
)
360 ULONGLONG tmp
= Time
->QuadPart
/ TICKSPERSEC
- SECS_1601_TO_1970
;
361 if (tmp
> 0xffffffff) return FALSE
;
366 /******************************************************************************
367 * RtlTimeToSecondsSince1980 [NTDLL.@]
369 * Convert a time into a count of seconds since 1980.
372 * Time [I] Time to convert.
373 * Seconds [O] Destination for the converted time.
377 * Failure: FALSE, if the resulting value will not fit in a DWORD.
379 BOOLEAN WINAPI
RtlTimeToSecondsSince1980( const LARGE_INTEGER
*Time
, LPDWORD Seconds
)
381 ULONGLONG tmp
= Time
->QuadPart
/ TICKSPERSEC
- SECS_1601_TO_1980
;
382 if (tmp
> 0xffffffff) return FALSE
;
387 /******************************************************************************
388 * RtlSecondsSince1970ToTime [NTDLL.@]
390 * Convert a count of seconds since 1970 to a time.
393 * Seconds [I] Time to convert.
394 * Time [O] Destination for the converted time.
399 void WINAPI
RtlSecondsSince1970ToTime( DWORD Seconds
, LARGE_INTEGER
*Time
)
401 Time
->QuadPart
= Seconds
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1970
;
404 /******************************************************************************
405 * RtlSecondsSince1980ToTime [NTDLL.@]
407 * Convert a count of seconds since 1980 to a time.
410 * Seconds [I] Time to convert.
411 * Time [O] Destination for the converted time.
416 void WINAPI
RtlSecondsSince1980ToTime( DWORD Seconds
, LARGE_INTEGER
*Time
)
418 Time
->QuadPart
= Seconds
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1980
;
421 /******************************************************************************
422 * RtlTimeToElapsedTimeFields [NTDLL.@]
424 * Convert a time to a count of elapsed seconds.
427 * Time [I] Time to convert.
428 * TimeFields [O] Destination for the converted time.
433 void WINAPI
RtlTimeToElapsedTimeFields( const LARGE_INTEGER
*Time
, PTIME_FIELDS TimeFields
)
438 time
= Time
->QuadPart
/ TICKSPERSEC
;
439 TimeFields
->Milliseconds
= (Time
->QuadPart
% TICKSPERSEC
) / TICKSPERMSEC
;
441 /* time is now in seconds */
442 TimeFields
->Year
= 0;
443 TimeFields
->Month
= 0;
444 TimeFields
->Day
= time
/ SECSPERDAY
;
446 /* rem is now the remaining seconds in the last day */
447 rem
= time
% SECSPERDAY
;
448 TimeFields
->Second
= rem
% 60;
450 TimeFields
->Minute
= rem
% 60;
451 TimeFields
->Hour
= rem
/ 60;
454 /***********************************************************************
455 * NtQuerySystemTime [NTDLL.@]
456 * ZwQuerySystemTime [NTDLL.@]
458 * Get the current system time.
461 * time [O] Destination for the current system time.
464 * Success: STATUS_SUCCESS.
465 * Failure: An NTSTATUS error code indicating the problem.
467 NTSTATUS WINAPI
NtQuerySystemTime( LARGE_INTEGER
*time
)
469 #ifdef HAVE_CLOCK_GETTIME
471 static clockid_t clock_id
= CLOCK_MONOTONIC
; /* placeholder */
473 if (clock_id
== CLOCK_MONOTONIC
)
475 #ifdef CLOCK_REALTIME_COARSE
478 /* Use CLOCK_REALTIME_COARSE if it has 1 ms or better resolution */
479 if (!clock_getres( CLOCK_REALTIME_COARSE
, &res
) && res
.tv_sec
== 0 && res
.tv_nsec
<= 1000000)
480 clock_id
= CLOCK_REALTIME_COARSE
;
482 #endif /* CLOCK_REALTIME_COARSE */
483 clock_id
= CLOCK_REALTIME
;
486 if (!clock_gettime( clock_id
, &ts
))
488 time
->QuadPart
= ts
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1970
;
489 time
->QuadPart
+= (ts
.tv_nsec
+ 50) / 100;
492 #endif /* HAVE_CLOCK_GETTIME */
496 gettimeofday( &now
, 0 );
497 time
->QuadPart
= now
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1970
;
498 time
->QuadPart
+= now
.tv_usec
* 10;
500 return STATUS_SUCCESS
;
503 /***********************************************************************
504 * RtlGetSystemTimePrecise [NTDLL.@]
506 * Get a more accurate current system time.
509 * The current system time.
511 LONGLONG WINAPI
RtlGetSystemTimePrecise( void )
515 #ifdef HAVE_CLOCK_GETTIME
518 if (!clock_gettime( CLOCK_REALTIME
, &ts
))
520 time
= ts
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1970
;
521 time
+= (ts
.tv_nsec
+ 50) / 100;
528 gettimeofday( &now
, 0 );
529 time
= now
.tv_sec
* (ULONGLONG
)TICKSPERSEC
+ TICKS_1601_TO_1970
;
530 time
+= now
.tv_usec
* 10;
536 /******************************************************************************
537 * NtQueryPerformanceCounter [NTDLL.@]
539 NTSTATUS WINAPI
NtQueryPerformanceCounter( LARGE_INTEGER
*counter
, LARGE_INTEGER
*frequency
)
543 counter
->QuadPart
= monotonic_counter();
544 if (frequency
) frequency
->QuadPart
= TICKSPERSEC
;
548 return STATUS_ACCESS_VIOLATION
;
552 return STATUS_SUCCESS
;
556 /******************************************************************************
557 * NtGetTickCount (NTDLL.@)
558 * ZwGetTickCount (NTDLL.@)
560 ULONGLONG WINAPI DECLSPEC_HOTPATCH
get_tick_count64(void)
562 return monotonic_counter() / TICKSPERMSEC
;
565 /* calculate the mday of dst change date, so that for instance Sun 5 Oct 2007
566 * (last Sunday in October of 2007) becomes Sun Oct 28 2007
568 * Note: year, day and month must be in unix format.
570 static int weekday_to_mday(int year
, int day
, int mon
, int day_of_week
)
576 /* find first day in the month matching week day of the date */
577 memset(&date
, 0, sizeof(date
));
586 } while (date
.tm_wday
!= day_of_week
|| date
.tm_mon
!= mon
);
590 /* find number of week days in the month matching week day of the date */
591 wday
= 1; /* 1 - 1st, ...., 5 - last */
598 tm
= localtime(&tmp
);
599 if (tm
->tm_mon
!= mon
)
608 static BOOL
match_tz_date(const RTL_SYSTEM_TIME
*st
, const RTL_SYSTEM_TIME
*reg_st
)
612 if (st
->wMonth
!= reg_st
->wMonth
) return FALSE
;
614 if (!st
->wMonth
) return TRUE
; /* no transition dates */
617 if (!reg_st
->wYear
) /* date in a day-of-week format */
618 wDay
= weekday_to_mday(st
->wYear
- 1900, reg_st
->wDay
, reg_st
->wMonth
- 1, reg_st
->wDayOfWeek
);
620 if (st
->wDay
!= wDay
||
621 st
->wHour
!= reg_st
->wHour
||
622 st
->wMinute
!= reg_st
->wMinute
||
623 st
->wSecond
!= reg_st
->wSecond
||
624 st
->wMilliseconds
!= reg_st
->wMilliseconds
) return FALSE
;
629 static BOOL
match_tz_info(const RTL_DYNAMIC_TIME_ZONE_INFORMATION
*tzi
, const RTL_DYNAMIC_TIME_ZONE_INFORMATION
*reg_tzi
)
631 if (tzi
->Bias
== reg_tzi
->Bias
&&
632 match_tz_date(&tzi
->StandardDate
, ®_tzi
->StandardDate
) &&
633 match_tz_date(&tzi
->DaylightDate
, ®_tzi
->DaylightDate
))
639 static int compare_tz_key(const void *a
, const void *b
)
641 const struct tz_name_map
*map_a
, *map_b
;
642 map_a
= (const struct tz_name_map
*)a
;
643 map_b
= (const struct tz_name_map
*)b
;
644 return strcmpW(map_a
->key_name
, map_b
->key_name
);
647 static BOOL
match_tz_name(const char* tz_name
,
648 const RTL_DYNAMIC_TIME_ZONE_INFORMATION
*reg_tzi
)
650 static const struct tz_name_map mapping
[] = {
651 { {'K','o','r','e','a',' ','S','t','a','n','d','a','r','d',' ','T','i',
654 { {'T','o','k','y','o',' ','S','t','a','n','d','a','r','d',' ','T','i',
657 { {'Y','a','k','u','t','s','k',' ','S','t','a','n','d','a','r','d',' ',
659 "+09" }, /* YAKST was used until tzdata 2016f */
661 struct tz_name_map
*match
, key
;
663 if (reg_tzi
->DaylightDate
.wMonth
)
666 strcpyW(key
.key_name
, reg_tzi
->TimeZoneKeyName
);
667 match
= bsearch(&key
, mapping
, ARRAY_SIZE(mapping
), sizeof(mapping
[0]), compare_tz_key
);
671 return !strcmp(match
->short_name
, tz_name
);
674 static BOOL
reg_query_value(HKEY hkey
, LPCWSTR name
, DWORD type
, void *data
, DWORD count
)
676 UNICODE_STRING nameW
;
678 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buf
;
680 if (count
> sizeof(buf
) - sizeof(KEY_VALUE_PARTIAL_INFORMATION
))
683 RtlInitUnicodeString(&nameW
, name
);
685 if (NtQueryValueKey(hkey
, &nameW
, KeyValuePartialInformation
,
686 buf
, sizeof(buf
), &count
))
689 if (info
->Type
!= type
) return FALSE
;
691 memcpy(data
, info
->Data
, info
->DataLength
);
695 static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION
*tzi
, const char* tz_name
, int year
)
697 static const WCHAR Time_ZonesW
[] = { 'M','a','c','h','i','n','e','\\',
698 'S','o','f','t','w','a','r','e','\\',
699 'M','i','c','r','o','s','o','f','t','\\',
700 'W','i','n','d','o','w','s',' ','N','T','\\',
701 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
702 'T','i','m','e',' ','Z','o','n','e','s',0 };
703 static const WCHAR Dynamic_DstW
[] = { 'D','y','n','a','m','i','c',' ','D','S','T',0 };
704 static const WCHAR fmtW
[] = { '%','d',0 };
707 OBJECT_ATTRIBUTES attr
, attrDynamic
;
708 UNICODE_STRING nameW
, nameDynamicW
;
709 WCHAR buf
[128], yearW
[16];
711 sprintfW(yearW
, fmtW
, year
);
713 attrDynamic
.Length
= sizeof(attrDynamic
);
714 attrDynamic
.RootDirectory
= 0; /* will be replaced later */
715 attrDynamic
.ObjectName
= &nameDynamicW
;
716 attrDynamic
.Attributes
= 0;
717 attrDynamic
.SecurityDescriptor
= NULL
;
718 attrDynamic
.SecurityQualityOfService
= NULL
;
719 RtlInitUnicodeString(&nameDynamicW
, Dynamic_DstW
);
721 attr
.Length
= sizeof(attr
);
722 attr
.RootDirectory
= 0;
723 attr
.ObjectName
= &nameW
;
725 attr
.SecurityDescriptor
= NULL
;
726 attr
.SecurityQualityOfService
= NULL
;
727 RtlInitUnicodeString(&nameW
, Time_ZonesW
);
728 if (NtOpenKey(&hkey
, KEY_READ
, &attr
))
730 WARN("Unable to open the time zones key\n");
736 nameW
.Length
= sizeof(buf
);
737 nameW
.MaximumLength
= sizeof(buf
);
739 while (!RtlpNtEnumerateSubKey(hkey
, &nameW
, idx
++))
741 static const WCHAR stdW
[] = { 'S','t','d',0 };
742 static const WCHAR dltW
[] = { 'D','l','t',0 };
743 static const WCHAR mui_stdW
[] = { 'M','U','I','_','S','t','d',0 };
744 static const WCHAR mui_dltW
[] = { 'M','U','I','_','D','l','t',0 };
745 static const WCHAR tziW
[] = { 'T','Z','I',0 };
746 RTL_DYNAMIC_TIME_ZONE_INFORMATION reg_tzi
;
747 HANDLE hSubkey
, hSubkeyDynamicDST
;
748 BOOL is_dynamic
= FALSE
;
755 RTL_SYSTEM_TIME std_date
;
756 RTL_SYSTEM_TIME dlt_date
;
759 attr
.Length
= sizeof(attr
);
760 attr
.RootDirectory
= hkey
;
761 attr
.ObjectName
= &nameW
;
763 attr
.SecurityDescriptor
= NULL
;
764 attr
.SecurityQualityOfService
= NULL
;
765 if (NtOpenKey(&hSubkey
, KEY_READ
, &attr
))
767 WARN("Unable to open subkey %s\n", debugstr_wn(nameW
.Buffer
, nameW
.Length
/sizeof(WCHAR
)));
771 #define get_value(hkey, name, type, data, len) \
772 if (!reg_query_value(hkey, name, type, data, len)) \
774 WARN("can't read data from %s\n", debugstr_w(name)); \
779 if (!reg_query_value(hSubkey
, mui_stdW
, REG_SZ
, reg_tzi
.StandardName
, sizeof(reg_tzi
.StandardName
)))
780 get_value(hSubkey
, stdW
, REG_SZ
, reg_tzi
.StandardName
, sizeof(reg_tzi
.StandardName
));
781 if (!reg_query_value(hSubkey
, mui_dltW
, REG_SZ
, reg_tzi
.DaylightName
, sizeof(reg_tzi
.DaylightName
)))
782 get_value(hSubkey
, dltW
, REG_SZ
, reg_tzi
.DaylightName
, sizeof(reg_tzi
.DaylightName
));
783 memcpy(reg_tzi
.TimeZoneKeyName
, nameW
.Buffer
, nameW
.Length
);
784 reg_tzi
.TimeZoneKeyName
[nameW
.Length
/sizeof(WCHAR
)] = 0;
786 /* Check for Dynamic DST entry first */
787 attrDynamic
.RootDirectory
= hSubkey
;
788 if (!NtOpenKey(&hSubkeyDynamicDST
, KEY_READ
, &attrDynamic
))
790 is_dynamic
= reg_query_value(hSubkeyDynamicDST
, yearW
, REG_BINARY
, &tz_data
, sizeof(tz_data
));
791 NtClose(hSubkeyDynamicDST
);
795 get_value(hSubkey
, tziW
, REG_BINARY
, &tz_data
, sizeof(tz_data
));
799 reg_tzi
.Bias
= tz_data
.bias
;
800 reg_tzi
.StandardBias
= tz_data
.std_bias
;
801 reg_tzi
.DaylightBias
= tz_data
.dlt_bias
;
802 reg_tzi
.StandardDate
= tz_data
.std_date
;
803 reg_tzi
.DaylightDate
= tz_data
.dlt_date
;
805 TRACE("%s: bias %d\n", debugstr_wn(nameW
.Buffer
, nameW
.Length
/sizeof(WCHAR
)), reg_tzi
.Bias
);
806 TRACE("std (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
807 reg_tzi
.StandardDate
.wDay
, reg_tzi
.StandardDate
.wMonth
,
808 reg_tzi
.StandardDate
.wYear
, reg_tzi
.StandardDate
.wDayOfWeek
,
809 reg_tzi
.StandardDate
.wHour
, reg_tzi
.StandardDate
.wMinute
,
810 reg_tzi
.StandardDate
.wSecond
, reg_tzi
.StandardDate
.wMilliseconds
,
811 reg_tzi
.StandardBias
);
812 TRACE("dst (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
813 reg_tzi
.DaylightDate
.wDay
, reg_tzi
.DaylightDate
.wMonth
,
814 reg_tzi
.DaylightDate
.wYear
, reg_tzi
.DaylightDate
.wDayOfWeek
,
815 reg_tzi
.DaylightDate
.wHour
, reg_tzi
.DaylightDate
.wMinute
,
816 reg_tzi
.DaylightDate
.wSecond
, reg_tzi
.DaylightDate
.wMilliseconds
,
817 reg_tzi
.DaylightBias
);
821 if (match_tz_info(tzi
, ®_tzi
)
822 && match_tz_name(tz_name
, ®_tzi
))
830 nameW
.Length
= sizeof(buf
);
831 nameW
.MaximumLength
= sizeof(buf
);
836 FIXME("Can't find matching timezone information in the registry for "
837 "%s, bias %d, std (d/m/y): %u/%02u/%04u, dlt (d/m/y): %u/%02u/%04u\n",
839 tzi
->StandardDate
.wDay
, tzi
->StandardDate
.wMonth
, tzi
->StandardDate
.wYear
,
840 tzi
->DaylightDate
.wDay
, tzi
->DaylightDate
.wMonth
, tzi
->DaylightDate
.wYear
);
843 static time_t find_dst_change(unsigned long min
, unsigned long max
, int *is_dst
)
849 tm
= localtime(&start
);
850 *is_dst
= !tm
->tm_isdst
;
851 TRACE("starting date isdst %d, %s", !*is_dst
, ctime(&start
));
855 time_t pos
= (min
+ max
) / 2;
856 tm
= localtime(&pos
);
858 if (tm
->tm_isdst
!= *is_dst
)
866 static int init_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION
*tzi
)
868 static RTL_DYNAMIC_TIME_ZONE_INFORMATION cached_tzi
;
869 static int current_year
= -1, current_bias
= 65535;
871 char tz_name
[SHORT_TZ_NAME_MAX
];
872 time_t year_start
, year_end
, tmp
, dlt
= 0, std
= 0;
873 int is_dst
, current_is_dst
, bias
;
875 RtlEnterCriticalSection( &TIME_tz_section
);
877 year_start
= time(NULL
);
878 tm
= gmtime(&year_start
);
879 bias
= (LONG
)(mktime(tm
) - year_start
) / 60;
881 tm
= localtime(&year_start
);
882 current_is_dst
= tm
->tm_isdst
;
883 if (current_year
== tm
->tm_year
&& current_bias
== bias
)
886 RtlLeaveCriticalSection( &TIME_tz_section
);
887 return current_is_dst
;
890 memset(tzi
, 0, sizeof(*tzi
));
891 if (!strftime(tz_name
, sizeof(tz_name
), "%Z", tm
)) {
892 /* not enough room or another error */
896 TRACE("tz data will be valid through year %d, bias %d\n", tm
->tm_year
+ 1900, bias
);
897 current_year
= tm
->tm_year
;
904 tm
->tm_mon
= tm
->tm_hour
= tm
->tm_min
= tm
->tm_sec
= tm
->tm_wday
= tm
->tm_yday
= 0;
905 year_start
= mktime(tm
);
906 TRACE("year_start: %s", ctime(&year_start
));
908 tm
->tm_mday
= tm
->tm_wday
= tm
->tm_yday
= 0;
911 tm
->tm_min
= tm
->tm_sec
= 59;
912 year_end
= mktime(tm
);
913 TRACE("year_end: %s", ctime(&year_end
));
915 tmp
= find_dst_change(year_start
, year_end
, &is_dst
);
921 tmp
= find_dst_change(tmp
, year_end
, &is_dst
);
927 TRACE("std: %s", ctime(&std
));
928 TRACE("dlt: %s", ctime(&dlt
));
930 if (dlt
== std
|| !dlt
|| !std
)
931 TRACE("there is no daylight saving rules in this time zone\n");
934 tmp
= dlt
- tzi
->Bias
* 60;
936 TRACE("dlt gmtime: %s", asctime(tm
));
938 tzi
->DaylightBias
= -60;
939 tzi
->DaylightDate
.wYear
= tm
->tm_year
+ 1900;
940 tzi
->DaylightDate
.wMonth
= tm
->tm_mon
+ 1;
941 tzi
->DaylightDate
.wDayOfWeek
= tm
->tm_wday
;
942 tzi
->DaylightDate
.wDay
= tm
->tm_mday
;
943 tzi
->DaylightDate
.wHour
= tm
->tm_hour
;
944 tzi
->DaylightDate
.wMinute
= tm
->tm_min
;
945 tzi
->DaylightDate
.wSecond
= tm
->tm_sec
;
946 tzi
->DaylightDate
.wMilliseconds
= 0;
948 TRACE("daylight (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
949 tzi
->DaylightDate
.wDay
, tzi
->DaylightDate
.wMonth
,
950 tzi
->DaylightDate
.wYear
, tzi
->DaylightDate
.wDayOfWeek
,
951 tzi
->DaylightDate
.wHour
, tzi
->DaylightDate
.wMinute
,
952 tzi
->DaylightDate
.wSecond
, tzi
->DaylightDate
.wMilliseconds
,
955 tmp
= std
- tzi
->Bias
* 60 - tzi
->DaylightBias
* 60;
957 TRACE("std gmtime: %s", asctime(tm
));
959 tzi
->StandardBias
= 0;
960 tzi
->StandardDate
.wYear
= tm
->tm_year
+ 1900;
961 tzi
->StandardDate
.wMonth
= tm
->tm_mon
+ 1;
962 tzi
->StandardDate
.wDayOfWeek
= tm
->tm_wday
;
963 tzi
->StandardDate
.wDay
= tm
->tm_mday
;
964 tzi
->StandardDate
.wHour
= tm
->tm_hour
;
965 tzi
->StandardDate
.wMinute
= tm
->tm_min
;
966 tzi
->StandardDate
.wSecond
= tm
->tm_sec
;
967 tzi
->StandardDate
.wMilliseconds
= 0;
969 TRACE("standard (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
970 tzi
->StandardDate
.wDay
, tzi
->StandardDate
.wMonth
,
971 tzi
->StandardDate
.wYear
, tzi
->StandardDate
.wDayOfWeek
,
972 tzi
->StandardDate
.wHour
, tzi
->StandardDate
.wMinute
,
973 tzi
->StandardDate
.wSecond
, tzi
->StandardDate
.wMilliseconds
,
977 find_reg_tz_info(tzi
, tz_name
, current_year
+ 1900);
980 RtlLeaveCriticalSection( &TIME_tz_section
);
982 return current_is_dst
;
985 /***********************************************************************
986 * RtlQueryTimeZoneInformation [NTDLL.@]
988 * Get information about the current timezone.
991 * tzinfo [O] Destination for the retrieved timezone info.
994 * Success: STATUS_SUCCESS.
995 * Failure: An NTSTATUS error code indicating the problem.
997 NTSTATUS WINAPI
RtlQueryTimeZoneInformation(RTL_TIME_ZONE_INFORMATION
*ret
)
999 RTL_DYNAMIC_TIME_ZONE_INFORMATION tzinfo
;
1001 init_tz_info( &tzinfo
);
1002 memcpy( ret
, &tzinfo
, sizeof(*ret
) );
1003 return STATUS_SUCCESS
;
1006 /***********************************************************************
1007 * RtlQueryDynamicTimeZoneInformation [NTDLL.@]
1009 * Get information about the current timezone.
1012 * tzinfo [O] Destination for the retrieved timezone info.
1015 * Success: STATUS_SUCCESS.
1016 * Failure: An NTSTATUS error code indicating the problem.
1018 NTSTATUS WINAPI
RtlQueryDynamicTimeZoneInformation(RTL_DYNAMIC_TIME_ZONE_INFORMATION
*tzinfo
)
1020 init_tz_info( tzinfo
);
1022 return STATUS_SUCCESS
;
1025 /***********************************************************************
1026 * RtlSetTimeZoneInformation [NTDLL.@]
1028 * Set the current time zone information.
1031 * tzinfo [I] Timezone information to set.
1034 * Success: STATUS_SUCCESS.
1035 * Failure: An NTSTATUS error code indicating the problem.
1038 NTSTATUS WINAPI
RtlSetTimeZoneInformation( const RTL_TIME_ZONE_INFORMATION
*tzinfo
)
1040 return STATUS_PRIVILEGE_NOT_HELD
;
1043 /***********************************************************************
1044 * NtSetSystemTime [NTDLL.@]
1045 * ZwSetSystemTime [NTDLL.@]
1047 * Set the system time.
1050 * NewTime [I] The time to set.
1051 * OldTime [O] Optional destination for the previous system time.
1054 * Success: STATUS_SUCCESS.
1055 * Failure: An NTSTATUS error code indicating the problem.
1057 NTSTATUS WINAPI
NtSetSystemTime(const LARGE_INTEGER
*NewTime
, LARGE_INTEGER
*OldTime
)
1064 /* Return the old time if necessary */
1065 if (!OldTime
) OldTime
= &tm
;
1067 NtQuerySystemTime( OldTime
);
1068 if (!RtlTimeToSecondsSince1970( OldTime
, &oldsec
)) return STATUS_INVALID_PARAMETER
;
1069 if (!RtlTimeToSecondsSince1970( NewTime
, &sec
)) return STATUS_INVALID_PARAMETER
;
1071 /* fake success if time didn't change */
1073 return STATUS_SUCCESS
;
1075 /* set the new time */
1079 #ifdef HAVE_SETTIMEOFDAY
1081 if (!settimeofday(&tv
, NULL
)) /* 0 is OK, -1 is error */
1083 TRACE("OS time changed to %s\n", ctime(&tm_t
));
1084 return STATUS_SUCCESS
;
1086 ERR("Cannot set time to %s, time adjustment %ld: %s\n",
1087 ctime(&tm_t
), (long)(sec
-oldsec
), strerror(errno
));
1089 return STATUS_PRIVILEGE_NOT_HELD
;
1091 return STATUS_INVALID_PARAMETER
;
1094 FIXME("setting time to %s not implemented for missing settimeofday\n",
1096 return STATUS_NOT_IMPLEMENTED
;
1100 /***********************************************************************
1101 * RtlQueryUnbiasedInterruptTime [NTDLL.@]
1103 NTSTATUS WINAPI
RtlQueryUnbiasedInterruptTime(ULONGLONG
*time
)
1105 *time
= monotonic_counter();
1106 return STATUS_SUCCESS
;