[PATCH 0/8] MR10134: ntdll: Fix various issues with mapping Unix timezone to Windows timezone.
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ntdll/unix/system.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index d9b276acb46..3559ba6a212 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2925,6 +2925,20 @@ static time_t find_dst_change(time_t start, time_t end, int *is_dst) return min; } +static LONG64 get_current_tz_bias(void) +{ + ULONG high, low; + + do + { + high = user_shared_data->TimeZoneBias.High1Time; + low = user_shared_data->TimeZoneBias.LowPart; + } + while (high != user_shared_data->TimeZoneBias.High2Time); + + return ((LONG64)high << 32) | low; +} + static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) { static RTL_DYNAMIC_TIME_ZONE_INFORMATION cached_tzi; @@ -3227,27 +3241,10 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, case SystemTimeOfDayInformation: /* 3 */ { - static LONGLONG last_bias; - static time_t last_utc; - struct tm *tm; - time_t utc; SYSTEM_TIMEOFDAY_INFORMATION sti = {{{ 0 }}}; sti.BootTime.QuadPart = server_start_time; - - utc = time( NULL ); - pthread_mutex_lock( &timezone_mutex ); - if (utc != last_utc) - { - last_utc = utc; - tm = gmtime( &utc ); - last_bias = mktime( tm ) - utc; - tm = localtime( &utc ); - if (tm->tm_isdst) last_bias -= 3600; - last_bias *= TICKSPERSEC; - } - sti.TimeZoneBias.QuadPart = last_bias; - pthread_mutex_unlock( &timezone_mutex ); + sti.TimeZoneBias.QuadPart = get_current_tz_bias(); NtQuerySystemTime( &sti.SystemTime ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
From: Paul Gofman <pgofman@codeweavers.com> --- server/fd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/fd.c b/server/fd.c index f70bec354a3..2f21f833411 100644 --- a/server/fd.c +++ b/server/fd.c @@ -389,9 +389,8 @@ static void set_user_shared_data_time(void) { now = time( NULL ); tm = gmtime( &now ); + tm->tm_isdst = -1; timezone_bias = mktime( tm ) - now; - tm = localtime( &now ); - if (tm->tm_isdst) timezone_bias -= 3600; timezone_bias *= TICKS_PER_SEC; atomic_store_long(&user_shared_data->TimeZoneBias.High2Time, timezone_bias >> 32); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ntdll/unix/system.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 3559ba6a212..9194a8e2e3c 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2676,6 +2676,7 @@ static int weekday_to_mday(int year, int day, int mon, int day_of_week) do { date.tm_mday++; + date.tm_isdst = -1; tmp = mktime(&date); } while (date.tm_wday != day_of_week || date.tm_mon != mon); @@ -2688,6 +2689,7 @@ static int weekday_to_mday(int year, int day, int mon, int day_of_week) struct tm *tm; date.tm_mday += 7; + date.tm_isdst = -1; tmp = mktime(&date); tm = localtime(&tmp); if (tm->tm_mon != mon) @@ -2947,6 +2949,7 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) char tz_name[16]; time_t year_start, year_end, tmp, dlt = 0, std = 0; int is_dst, bias; + BOOL inverted_dst; mutex_lock( &timezone_mutex ); @@ -2954,6 +2957,11 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) tm = gmtime(&year_start); bias = (LONG)(mktime(tm) - year_start) / 60; + tm = gmtime(&year_start); + tm->tm_isdst = 1; + inverted_dst = (mktime(tm) - year_start) / 60 - bias > 0; + if (inverted_dst) bias += 60; + tm = localtime(&year_start); if (current_year == tm->tm_year && current_bias == bias) { @@ -2968,18 +2976,19 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) tz_name[0] = '\0'; } - TRACE("tz data will be valid through year %d, bias %d\n", tm->tm_year + 1900, bias); + TRACE("tz data will be valid through year %d, bias %d, inverted_dst %d\n", tm->tm_year + 1900, bias, inverted_dst); current_year = tm->tm_year; current_bias = bias; tzi->Bias = bias; - tm->tm_isdst = 0; + tm->tm_isdst = inverted_dst; tm->tm_mday = 1; tm->tm_mon = tm->tm_hour = tm->tm_min = tm->tm_sec = tm->tm_wday = tm->tm_yday = 0; year_start = mktime(tm); TRACE("year_start: %s", ctime(&year_start)); + tm->tm_isdst = inverted_dst; tm->tm_mday = tm->tm_wday = tm->tm_yday = 0; tm->tm_mon = 12; tm->tm_hour = 23; @@ -2988,12 +2997,14 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) TRACE("year_end: %s", ctime(&year_end)); tmp = find_dst_change(year_start, year_end, &is_dst); + if (inverted_dst) is_dst = !is_dst; if (is_dst) dlt = tmp; else std = tmp; tmp = find_dst_change(tmp, year_end, &is_dst); + if (inverted_dst) is_dst = !is_dst; if (is_dst) dlt = tmp; else -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ntdll/unix/system.c | 116 ++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 51 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 9194a8e2e3c..a31c467f71f 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2791,13 +2791,75 @@ static BOOL reg_query_value( HKEY key, LPCWSTR name, DWORD type, void *data, DWO return TRUE; } -static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* tz_name, int year) +static BOOL read_reg_tz_info( HANDLE key, UNICODE_STRING *zone_key_name, int year, RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) { static const WCHAR stdW[] = { 'S','t','d',0 }; static const WCHAR dltW[] = { 'D','l','t',0 }; static const WCHAR mui_stdW[] = { 'M','U','I','_','S','t','d',0 }; static const WCHAR mui_dltW[] = { 'M','U','I','_','D','l','t',0 }; static const WCHAR tziW[] = { 'T','Z','I',0 }; + static const WCHAR Dynamic_DstW[] = { 'D','y','n','a','m','i','c',' ','D','S','T',0 }; + HANDLE subkey, subkey_dyn; + OBJECT_ATTRIBUTES attr; + struct tz_reg_data + { + LONG bias; + LONG std_bias; + LONG dlt_bias; + RTL_SYSTEM_TIME std_date; + RTL_SYSTEM_TIME dlt_date; + } tz_data; + BOOL is_dynamic = FALSE; + UNICODE_STRING name; + BOOL ret = FALSE; + char buffer[16]; + WCHAR yearW[16]; + + InitializeObjectAttributes( &attr, zone_key_name, 0, key, NULL ); + if (NtOpenKey( &subkey, KEY_READ, &attr )) return FALSE; + + memset( tzi, 0, sizeof(*tzi) ); + memcpy(tzi->TimeZoneKeyName, zone_key_name->Buffer, zone_key_name->Length); + tzi->TimeZoneKeyName[zone_key_name->Length / sizeof(WCHAR)] = 0; + + if (!reg_query_value(subkey, mui_stdW, REG_SZ, tzi->StandardName, sizeof(tzi->StandardName)) && + !reg_query_value(subkey, stdW, REG_SZ, tzi->StandardName, sizeof(tzi->StandardName))) + goto done; + + if (!reg_query_value(subkey, mui_dltW, REG_SZ, tzi->DaylightName, sizeof(tzi->DaylightName)) && + !reg_query_value(subkey, dltW, REG_SZ, tzi->DaylightName, sizeof(tzi->DaylightName))) + goto done; + + /* Check for Dynamic DST entry first */ + name.Buffer = (WCHAR *)Dynamic_DstW; + name.Length = sizeof(Dynamic_DstW) - sizeof(WCHAR); + attr.RootDirectory = subkey; + attr.ObjectName = &name; + if (!NtOpenKey( &subkey_dyn, KEY_READ, &attr )) + { + snprintf( buffer, sizeof(buffer), "%u", year ); + ascii_to_unicode( yearW, buffer, strlen(buffer) + 1 ); + is_dynamic = reg_query_value( subkey_dyn, yearW, REG_BINARY, &tz_data, sizeof(tz_data) ); + NtClose( subkey_dyn ); + } + if (!is_dynamic && !reg_query_value( subkey, tziW, REG_BINARY, &tz_data, sizeof(tz_data) )) + goto done; + + tzi->Bias = tz_data.bias; + tzi->StandardBias = tz_data.std_bias; + tzi->DaylightBias = tz_data.dlt_bias; + tzi->StandardDate = tz_data.std_date; + tzi->DaylightDate = tz_data.dlt_date; + + ret = TRUE; + +done: + NtClose( subkey ); + return ret; +} + +static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* tz_name, int year) +{ static const WCHAR Time_ZonesW[] = { '\\','R','e','g','i','s','t','r','y','\\', 'M','a','c','h','i','n','e','\\', 'S','o','f','t','w','a','r','e','\\', @@ -2805,18 +2867,14 @@ static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* 'W','i','n','d','o','w','s',' ','N','T','\\', 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 'T','i','m','e',' ','Z','o','n','e','s',0 }; - static const WCHAR Dynamic_DstW[] = { 'D','y','n','a','m','i','c',' ','D','S','T',0 }; RTL_DYNAMIC_TIME_ZONE_INFORMATION reg_tzi; - HANDLE key, subkey, subkey_dyn = 0; + HANDLE key; ULONG idx, len; OBJECT_ATTRIBUTES attr; UNICODE_STRING nameW; - WCHAR yearW[16]; char buffer[128]; KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer; - snprintf( buffer, sizeof(buffer), "%u", year ); - ascii_to_unicode( yearW, buffer, strlen(buffer) + 1 ); init_unicode_string( &nameW, Time_ZonesW ); InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL ); if (NtOpenKey( &key, KEY_READ, &attr )) return; @@ -2824,50 +2882,9 @@ static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* idx = 0; while (!NtEnumerateKey( key, idx++, KeyBasicInformation, buffer, sizeof(buffer), &len )) { - struct tz_reg_data - { - LONG bias; - LONG std_bias; - LONG dlt_bias; - RTL_SYSTEM_TIME std_date; - RTL_SYSTEM_TIME dlt_date; - } tz_data; - BOOL is_dynamic = FALSE; - nameW.Buffer = info->Name; nameW.Length = info->NameLength; - attr.RootDirectory = key; - if (NtOpenKey( &subkey, KEY_READ, &attr )) continue; - - memset( ®_tzi, 0, sizeof(reg_tzi) ); - memcpy(reg_tzi.TimeZoneKeyName, nameW.Buffer, nameW.Length); - reg_tzi.TimeZoneKeyName[nameW.Length/sizeof(WCHAR)] = 0; - - if (!reg_query_value(subkey, mui_stdW, REG_SZ, reg_tzi.StandardName, sizeof(reg_tzi.StandardName)) && - !reg_query_value(subkey, stdW, REG_SZ, reg_tzi.StandardName, sizeof(reg_tzi.StandardName))) - goto next; - - if (!reg_query_value(subkey, mui_dltW, REG_SZ, reg_tzi.DaylightName, sizeof(reg_tzi.DaylightName)) && - !reg_query_value(subkey, dltW, REG_SZ, reg_tzi.DaylightName, sizeof(reg_tzi.DaylightName))) - goto next; - - /* Check for Dynamic DST entry first */ - nameW.Buffer = (WCHAR *)Dynamic_DstW; - nameW.Length = sizeof(Dynamic_DstW) - sizeof(WCHAR); - attr.RootDirectory = subkey; - if (!NtOpenKey( &subkey_dyn, KEY_READ, &attr )) - { - is_dynamic = reg_query_value( subkey_dyn, yearW, REG_BINARY, &tz_data, sizeof(tz_data) ); - NtClose( subkey_dyn ); - } - if (!is_dynamic && !reg_query_value( subkey, tziW, REG_BINARY, &tz_data, sizeof(tz_data) )) - goto next; - - reg_tzi.Bias = tz_data.bias; - reg_tzi.StandardBias = tz_data.std_bias; - reg_tzi.DaylightBias = tz_data.dlt_bias; - reg_tzi.StandardDate = tz_data.std_date; - reg_tzi.DaylightDate = tz_data.dlt_date; + if (!read_reg_tz_info( key, &nameW, year, ®_tzi )) continue; TRACE("%s: bias %d\n", debugstr_us(&nameW), reg_tzi.Bias); TRACE("std (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n", @@ -2886,12 +2903,9 @@ static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* if (match_tz_info( tzi, ®_tzi ) && match_tz_name( tz_name, ®_tzi )) { *tzi = reg_tzi; - NtClose( subkey ); NtClose( key ); return; } - next: - NtClose( subkey ); } NtClose( key ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/kernelbase/kernelbase.rgs | 454 +++++++++++++++++++++++++++++++++ dlls/ntdll/unix/system.c | 97 ++++++- tools/make_unicode | 24 +- 3 files changed, 564 insertions(+), 11 deletions(-) diff --git a/dlls/kernelbase/kernelbase.rgs b/dlls/kernelbase/kernelbase.rgs index ac1820d5c79..0a01ee5580f 100644 --- a/dlls/kernelbase/kernelbase.rgs +++ b/dlls/kernelbase/kernelbase.rgs @@ -3255,6 +3255,460 @@ HKLM } } } + NoRemove Wine + { + 'Time Zones' + { + 'TZ Mapping' + { + val 'Africa/Abidjan' = s 'Greenwich Standard Time' + val 'Africa/Accra' = s 'Greenwich Standard Time' + val 'Africa/Addis_Ababa' = s 'E. Africa Standard Time' + val 'Africa/Algiers' = s 'W. Central Africa Standard Time' + val 'Africa/Asmera' = s 'E. Africa Standard Time' + val 'Africa/Bamako' = s 'Greenwich Standard Time' + val 'Africa/Bangui' = s 'W. Central Africa Standard Time' + val 'Africa/Banjul' = s 'Greenwich Standard Time' + val 'Africa/Bissau' = s 'Greenwich Standard Time' + val 'Africa/Blantyre' = s 'South Africa Standard Time' + val 'Africa/Brazzaville' = s 'W. Central Africa Standard Time' + val 'Africa/Bujumbura' = s 'South Africa Standard Time' + val 'Africa/Cairo' = s 'Egypt Standard Time' + val 'Africa/Casablanca' = s 'Morocco Standard Time' + val 'Africa/Ceuta' = s 'Romance Standard Time' + val 'Africa/Conakry' = s 'Greenwich Standard Time' + val 'Africa/Dakar' = s 'Greenwich Standard Time' + val 'Africa/Dar_es_Salaam' = s 'E. Africa Standard Time' + val 'Africa/Djibouti' = s 'E. Africa Standard Time' + val 'Africa/Douala' = s 'W. Central Africa Standard Time' + val 'Africa/El_Aaiun' = s 'Morocco Standard Time' + val 'Africa/Freetown' = s 'Greenwich Standard Time' + val 'Africa/Gaborone' = s 'South Africa Standard Time' + val 'Africa/Harare' = s 'South Africa Standard Time' + val 'Africa/Johannesburg' = s 'South Africa Standard Time' + val 'Africa/Juba' = s 'South Sudan Standard Time' + val 'Africa/Kampala' = s 'E. Africa Standard Time' + val 'Africa/Khartoum' = s 'Sudan Standard Time' + val 'Africa/Kigali' = s 'South Africa Standard Time' + val 'Africa/Kinshasa' = s 'W. Central Africa Standard Time' + val 'Africa/Lagos' = s 'W. Central Africa Standard Time' + val 'Africa/Libreville' = s 'W. Central Africa Standard Time' + val 'Africa/Lome' = s 'Greenwich Standard Time' + val 'Africa/Luanda' = s 'W. Central Africa Standard Time' + val 'Africa/Lubumbashi' = s 'South Africa Standard Time' + val 'Africa/Lusaka' = s 'South Africa Standard Time' + val 'Africa/Malabo' = s 'W. Central Africa Standard Time' + val 'Africa/Maputo' = s 'South Africa Standard Time' + val 'Africa/Maseru' = s 'South Africa Standard Time' + val 'Africa/Mbabane' = s 'South Africa Standard Time' + val 'Africa/Mogadishu' = s 'E. Africa Standard Time' + val 'Africa/Monrovia' = s 'Greenwich Standard Time' + val 'Africa/Nairobi' = s 'E. Africa Standard Time' + val 'Africa/Ndjamena' = s 'W. Central Africa Standard Time' + val 'Africa/Niamey' = s 'W. Central Africa Standard Time' + val 'Africa/Nouakchott' = s 'Greenwich Standard Time' + val 'Africa/Ouagadougou' = s 'Greenwich Standard Time' + val 'Africa/Porto-Novo' = s 'W. Central Africa Standard Time' + val 'Africa/Sao_Tome' = s 'Sao Tome Standard Time' + val 'Africa/Tripoli' = s 'Libya Standard Time' + val 'Africa/Tunis' = s 'W. Central Africa Standard Time' + val 'Africa/Windhoek' = s 'Namibia Standard Time' + val 'America/Adak' = s 'Aleutian Standard Time' + val 'America/Anchorage' = s 'Alaskan Standard Time' + val 'America/Anguilla' = s 'SA Western Standard Time' + val 'America/Antigua' = s 'SA Western Standard Time' + val 'America/Araguaina' = s 'Tocantins Standard Time' + val 'America/Argentina/La_Rioja' = s 'Argentina Standard Time' + val 'America/Argentina/Rio_Gallegos' = s 'Argentina Standard Time' + val 'America/Argentina/Salta' = s 'Argentina Standard Time' + val 'America/Argentina/San_Juan' = s 'Argentina Standard Time' + val 'America/Argentina/San_Luis' = s 'Argentina Standard Time' + val 'America/Argentina/Tucuman' = s 'Argentina Standard Time' + val 'America/Argentina/Ushuaia' = s 'Argentina Standard Time' + val 'America/Aruba' = s 'SA Western Standard Time' + val 'America/Asuncion' = s 'Paraguay Standard Time' + val 'America/Bahia' = s 'Bahia Standard Time' + val 'America/Bahia_Banderas' = s 'Central Standard Time (Mexico)' + val 'America/Barbados' = s 'SA Western Standard Time' + val 'America/Belem' = s 'SA Eastern Standard Time' + val 'America/Belize' = s 'Central America Standard Time' + val 'America/Blanc-Sablon' = s 'SA Western Standard Time' + val 'America/Boa_Vista' = s 'SA Western Standard Time' + val 'America/Bogota' = s 'SA Pacific Standard Time' + val 'America/Boise' = s 'Mountain Standard Time' + val 'America/Buenos_Aires' = s 'Argentina Standard Time' + val 'America/Cambridge_Bay' = s 'Mountain Standard Time' + val 'America/Campo_Grande' = s 'Central Brazilian Standard Time' + val 'America/Cancun' = s 'Eastern Standard Time (Mexico)' + val 'America/Caracas' = s 'Venezuela Standard Time' + val 'America/Catamarca' = s 'Argentina Standard Time' + val 'America/Cayenne' = s 'SA Eastern Standard Time' + val 'America/Cayman' = s 'SA Pacific Standard Time' + val 'America/Chicago' = s 'Central Standard Time' + val 'America/Chihuahua' = s 'Central Standard Time (Mexico)' + val 'America/Ciudad_Juarez' = s 'Mountain Standard Time' + val 'America/Coral_Harbour' = s 'SA Pacific Standard Time' + val 'America/Cordoba' = s 'Argentina Standard Time' + val 'America/Costa_Rica' = s 'Central America Standard Time' + val 'America/Coyhaique' = s 'Magallanes Standard Time' + val 'America/Creston' = s 'US Mountain Standard Time' + val 'America/Cuiaba' = s 'Central Brazilian Standard Time' + val 'America/Curacao' = s 'SA Western Standard Time' + val 'America/Danmarkshavn' = s 'Greenwich Standard Time' + val 'America/Dawson' = s 'Yukon Standard Time' + val 'America/Dawson_Creek' = s 'US Mountain Standard Time' + val 'America/Denver' = s 'Mountain Standard Time' + val 'America/Detroit' = s 'Eastern Standard Time' + val 'America/Dominica' = s 'SA Western Standard Time' + val 'America/Edmonton' = s 'Mountain Standard Time' + val 'America/Eirunepe' = s 'SA Pacific Standard Time' + val 'America/El_Salvador' = s 'Central America Standard Time' + val 'America/Fort_Nelson' = s 'US Mountain Standard Time' + val 'America/Fortaleza' = s 'SA Eastern Standard Time' + val 'America/Glace_Bay' = s 'Atlantic Standard Time' + val 'America/Godthab' = s 'Greenland Standard Time' + val 'America/Goose_Bay' = s 'Atlantic Standard Time' + val 'America/Grand_Turk' = s 'Turks And Caicos Standard Time' + val 'America/Grenada' = s 'SA Western Standard Time' + val 'America/Guadeloupe' = s 'SA Western Standard Time' + val 'America/Guatemala' = s 'Central America Standard Time' + val 'America/Guayaquil' = s 'SA Pacific Standard Time' + val 'America/Guyana' = s 'SA Western Standard Time' + val 'America/Halifax' = s 'Atlantic Standard Time' + val 'America/Havana' = s 'Cuba Standard Time' + val 'America/Hermosillo' = s 'US Mountain Standard Time' + val 'America/Indiana/Knox' = s 'Central Standard Time' + val 'America/Indiana/Marengo' = s 'US Eastern Standard Time' + val 'America/Indiana/Petersburg' = s 'Eastern Standard Time' + val 'America/Indiana/Tell_City' = s 'Central Standard Time' + val 'America/Indiana/Vevay' = s 'US Eastern Standard Time' + val 'America/Indiana/Vincennes' = s 'Eastern Standard Time' + val 'America/Indiana/Winamac' = s 'Eastern Standard Time' + val 'America/Indianapolis' = s 'US Eastern Standard Time' + val 'America/Inuvik' = s 'Mountain Standard Time' + val 'America/Iqaluit' = s 'Eastern Standard Time' + val 'America/Jamaica' = s 'SA Pacific Standard Time' + val 'America/Jujuy' = s 'Argentina Standard Time' + val 'America/Juneau' = s 'Alaskan Standard Time' + val 'America/Kentucky/Monticello' = s 'Eastern Standard Time' + val 'America/Kralendijk' = s 'SA Western Standard Time' + val 'America/La_Paz' = s 'SA Western Standard Time' + val 'America/Lima' = s 'SA Pacific Standard Time' + val 'America/Los_Angeles' = s 'Pacific Standard Time' + val 'America/Louisville' = s 'Eastern Standard Time' + val 'America/Lower_Princes' = s 'SA Western Standard Time' + val 'America/Maceio' = s 'SA Eastern Standard Time' + val 'America/Managua' = s 'Central America Standard Time' + val 'America/Manaus' = s 'SA Western Standard Time' + val 'America/Marigot' = s 'SA Western Standard Time' + val 'America/Martinique' = s 'SA Western Standard Time' + val 'America/Matamoros' = s 'Central Standard Time' + val 'America/Mazatlan' = s 'Mountain Standard Time (Mexico)' + val 'America/Mendoza' = s 'Argentina Standard Time' + val 'America/Menominee' = s 'Central Standard Time' + val 'America/Merida' = s 'Central Standard Time (Mexico)' + val 'America/Metlakatla' = s 'Alaskan Standard Time' + val 'America/Mexico_City' = s 'Central Standard Time (Mexico)' + val 'America/Miquelon' = s 'Saint Pierre Standard Time' + val 'America/Moncton' = s 'Atlantic Standard Time' + val 'America/Monterrey' = s 'Central Standard Time (Mexico)' + val 'America/Montevideo' = s 'Montevideo Standard Time' + val 'America/Montserrat' = s 'SA Western Standard Time' + val 'America/Nassau' = s 'Eastern Standard Time' + val 'America/New_York' = s 'Eastern Standard Time' + val 'America/Nome' = s 'Alaskan Standard Time' + val 'America/Noronha' = s 'UTC-02' + val 'America/North_Dakota/Beulah' = s 'Central Standard Time' + val 'America/North_Dakota/Center' = s 'Central Standard Time' + val 'America/North_Dakota/New_Salem' = s 'Central Standard Time' + val 'America/Ojinaga' = s 'Central Standard Time' + val 'America/Panama' = s 'SA Pacific Standard Time' + val 'America/Paramaribo' = s 'SA Eastern Standard Time' + val 'America/Phoenix' = s 'US Mountain Standard Time' + val 'America/Port-au-Prince' = s 'Haiti Standard Time' + val 'America/Port_of_Spain' = s 'SA Western Standard Time' + val 'America/Porto_Velho' = s 'SA Western Standard Time' + val 'America/Puerto_Rico' = s 'SA Western Standard Time' + val 'America/Punta_Arenas' = s 'Magallanes Standard Time' + val 'America/Rankin_Inlet' = s 'Central Standard Time' + val 'America/Recife' = s 'SA Eastern Standard Time' + val 'America/Regina' = s 'Canada Central Standard Time' + val 'America/Resolute' = s 'Central Standard Time' + val 'America/Rio_Branco' = s 'SA Pacific Standard Time' + val 'America/Santarem' = s 'SA Eastern Standard Time' + val 'America/Santiago' = s 'Pacific SA Standard Time' + val 'America/Santo_Domingo' = s 'SA Western Standard Time' + val 'America/Sao_Paulo' = s 'E. South America Standard Time' + val 'America/Scoresbysund' = s 'Azores Standard Time' + val 'America/Sitka' = s 'Alaskan Standard Time' + val 'America/St_Barthelemy' = s 'SA Western Standard Time' + val 'America/St_Johns' = s 'Newfoundland Standard Time' + val 'America/St_Kitts' = s 'SA Western Standard Time' + val 'America/St_Lucia' = s 'SA Western Standard Time' + val 'America/St_Thomas' = s 'SA Western Standard Time' + val 'America/St_Vincent' = s 'SA Western Standard Time' + val 'America/Swift_Current' = s 'Canada Central Standard Time' + val 'America/Tegucigalpa' = s 'Central America Standard Time' + val 'America/Thule' = s 'Atlantic Standard Time' + val 'America/Tijuana' = s 'Pacific Standard Time (Mexico)' + val 'America/Toronto' = s 'Eastern Standard Time' + val 'America/Tortola' = s 'SA Western Standard Time' + val 'America/Vancouver' = s 'Pacific Standard Time' + val 'America/Whitehorse' = s 'Yukon Standard Time' + val 'America/Winnipeg' = s 'Central Standard Time' + val 'America/Yakutat' = s 'Alaskan Standard Time' + val 'Antarctica/Casey' = s 'Central Pacific Standard Time' + val 'Antarctica/Davis' = s 'SE Asia Standard Time' + val 'Antarctica/DumontDUrville' = s 'West Pacific Standard Time' + val 'Antarctica/Macquarie' = s 'Tasmania Standard Time' + val 'Antarctica/Mawson' = s 'West Asia Standard Time' + val 'Antarctica/McMurdo' = s 'New Zealand Standard Time' + val 'Antarctica/Palmer' = s 'SA Eastern Standard Time' + val 'Antarctica/Rothera' = s 'SA Eastern Standard Time' + val 'Antarctica/Syowa' = s 'E. Africa Standard Time' + val 'Antarctica/Vostok' = s 'Central Asia Standard Time' + val 'Arctic/Longyearbyen' = s 'W. Europe Standard Time' + val 'Asia/Aden' = s 'Arab Standard Time' + val 'Asia/Almaty' = s 'West Asia Standard Time' + val 'Asia/Amman' = s 'Jordan Standard Time' + val 'Asia/Anadyr' = s 'Russia Time Zone 11' + val 'Asia/Aqtau' = s 'West Asia Standard Time' + val 'Asia/Aqtobe' = s 'West Asia Standard Time' + val 'Asia/Ashgabat' = s 'West Asia Standard Time' + val 'Asia/Atyrau' = s 'West Asia Standard Time' + val 'Asia/Baghdad' = s 'Arabic Standard Time' + val 'Asia/Bahrain' = s 'Arab Standard Time' + val 'Asia/Baku' = s 'Azerbaijan Standard Time' + val 'Asia/Bangkok' = s 'SE Asia Standard Time' + val 'Asia/Barnaul' = s 'Altai Standard Time' + val 'Asia/Beirut' = s 'Middle East Standard Time' + val 'Asia/Bishkek' = s 'Central Asia Standard Time' + val 'Asia/Brunei' = s 'Singapore Standard Time' + val 'Asia/Calcutta' = s 'India Standard Time' + val 'Asia/Chita' = s 'Transbaikal Standard Time' + val 'Asia/Colombo' = s 'Sri Lanka Standard Time' + val 'Asia/Damascus' = s 'Syria Standard Time' + val 'Asia/Dhaka' = s 'Bangladesh Standard Time' + val 'Asia/Dili' = s 'Tokyo Standard Time' + val 'Asia/Dubai' = s 'Arabian Standard Time' + val 'Asia/Dushanbe' = s 'West Asia Standard Time' + val 'Asia/Famagusta' = s 'GTB Standard Time' + val 'Asia/Gaza' = s 'West Bank Standard Time' + val 'Asia/Hebron' = s 'West Bank Standard Time' + val 'Asia/Hong_Kong' = s 'China Standard Time' + val 'Asia/Hovd' = s 'W. Mongolia Standard Time' + val 'Asia/Irkutsk' = s 'North Asia East Standard Time' + val 'Asia/Jakarta' = s 'SE Asia Standard Time' + val 'Asia/Jayapura' = s 'Tokyo Standard Time' + val 'Asia/Jerusalem' = s 'Israel Standard Time' + val 'Asia/Kabul' = s 'Afghanistan Standard Time' + val 'Asia/Kamchatka' = s 'Russia Time Zone 11' + val 'Asia/Karachi' = s 'Pakistan Standard Time' + val 'Asia/Katmandu' = s 'Nepal Standard Time' + val 'Asia/Khandyga' = s 'Yakutsk Standard Time' + val 'Asia/Krasnoyarsk' = s 'North Asia Standard Time' + val 'Asia/Kuala_Lumpur' = s 'Singapore Standard Time' + val 'Asia/Kuching' = s 'Singapore Standard Time' + val 'Asia/Kuwait' = s 'Arab Standard Time' + val 'Asia/Macau' = s 'China Standard Time' + val 'Asia/Magadan' = s 'Magadan Standard Time' + val 'Asia/Makassar' = s 'Singapore Standard Time' + val 'Asia/Manila' = s 'Singapore Standard Time' + val 'Asia/Muscat' = s 'Arabian Standard Time' + val 'Asia/Nicosia' = s 'GTB Standard Time' + val 'Asia/Novokuznetsk' = s 'North Asia Standard Time' + val 'Asia/Novosibirsk' = s 'N. Central Asia Standard Time' + val 'Asia/Omsk' = s 'Omsk Standard Time' + val 'Asia/Oral' = s 'West Asia Standard Time' + val 'Asia/Phnom_Penh' = s 'SE Asia Standard Time' + val 'Asia/Pontianak' = s 'SE Asia Standard Time' + val 'Asia/Pyongyang' = s 'North Korea Standard Time' + val 'Asia/Qatar' = s 'Arab Standard Time' + val 'Asia/Qostanay' = s 'West Asia Standard Time' + val 'Asia/Qyzylorda' = s 'Qyzylorda Standard Time' + val 'Asia/Rangoon' = s 'Myanmar Standard Time' + val 'Asia/Riyadh' = s 'Arab Standard Time' + val 'Asia/Saigon' = s 'SE Asia Standard Time' + val 'Asia/Sakhalin' = s 'Sakhalin Standard Time' + val 'Asia/Samarkand' = s 'West Asia Standard Time' + val 'Asia/Seoul' = s 'Korea Standard Time' + val 'Asia/Shanghai' = s 'China Standard Time' + val 'Asia/Singapore' = s 'Singapore Standard Time' + val 'Asia/Srednekolymsk' = s 'Russia Time Zone 10' + val 'Asia/Taipei' = s 'Taipei Standard Time' + val 'Asia/Tashkent' = s 'West Asia Standard Time' + val 'Asia/Tbilisi' = s 'Georgian Standard Time' + val 'Asia/Tehran' = s 'Iran Standard Time' + val 'Asia/Thimphu' = s 'Bangladesh Standard Time' + val 'Asia/Tokyo' = s 'Tokyo Standard Time' + val 'Asia/Tomsk' = s 'Tomsk Standard Time' + val 'Asia/Ulaanbaatar' = s 'Ulaanbaatar Standard Time' + val 'Asia/Urumqi' = s 'Central Asia Standard Time' + val 'Asia/Ust-Nera' = s 'Vladivostok Standard Time' + val 'Asia/Vientiane' = s 'SE Asia Standard Time' + val 'Asia/Vladivostok' = s 'Vladivostok Standard Time' + val 'Asia/Yakutsk' = s 'Yakutsk Standard Time' + val 'Asia/Yekaterinburg' = s 'Ekaterinburg Standard Time' + val 'Asia/Yerevan' = s 'Caucasus Standard Time' + val 'Atlantic/Azores' = s 'Azores Standard Time' + val 'Atlantic/Bermuda' = s 'Atlantic Standard Time' + val 'Atlantic/Canary' = s 'GMT Standard Time' + val 'Atlantic/Cape_Verde' = s 'Cape Verde Standard Time' + val 'Atlantic/Faeroe' = s 'GMT Standard Time' + val 'Atlantic/Madeira' = s 'GMT Standard Time' + val 'Atlantic/Reykjavik' = s 'Greenwich Standard Time' + val 'Atlantic/South_Georgia' = s 'UTC-02' + val 'Atlantic/St_Helena' = s 'Greenwich Standard Time' + val 'Atlantic/Stanley' = s 'SA Eastern Standard Time' + val 'Australia/Adelaide' = s 'Cen. Australia Standard Time' + val 'Australia/Brisbane' = s 'E. Australia Standard Time' + val 'Australia/Broken_Hill' = s 'Cen. Australia Standard Time' + val 'Australia/Darwin' = s 'AUS Central Standard Time' + val 'Australia/Eucla' = s 'Aus Central W. Standard Time' + val 'Australia/Hobart' = s 'Tasmania Standard Time' + val 'Australia/Lindeman' = s 'E. Australia Standard Time' + val 'Australia/Lord_Howe' = s 'Lord Howe Standard Time' + val 'Australia/Melbourne' = s 'AUS Eastern Standard Time' + val 'Australia/Perth' = s 'W. Australia Standard Time' + val 'Australia/Sydney' = s 'AUS Eastern Standard Time' + val 'Etc/GMT' = s 'UTC' + val 'Etc/GMT+1' = s 'Cape Verde Standard Time' + val 'Etc/GMT+10' = s 'Hawaiian Standard Time' + val 'Etc/GMT+11' = s 'UTC-11' + val 'Etc/GMT+12' = s 'Dateline Standard Time' + val 'Etc/GMT+2' = s 'UTC-02' + val 'Etc/GMT+3' = s 'SA Eastern Standard Time' + val 'Etc/GMT+4' = s 'SA Western Standard Time' + val 'Etc/GMT+5' = s 'SA Pacific Standard Time' + val 'Etc/GMT+6' = s 'Central America Standard Time' + val 'Etc/GMT+7' = s 'US Mountain Standard Time' + val 'Etc/GMT+8' = s 'UTC-08' + val 'Etc/GMT+9' = s 'UTC-09' + val 'Etc/GMT-1' = s 'W. Central Africa Standard Time' + val 'Etc/GMT-10' = s 'West Pacific Standard Time' + val 'Etc/GMT-11' = s 'Central Pacific Standard Time' + val 'Etc/GMT-12' = s 'UTC+12' + val 'Etc/GMT-13' = s 'UTC+13' + val 'Etc/GMT-14' = s 'Line Islands Standard Time' + val 'Etc/GMT-2' = s 'South Africa Standard Time' + val 'Etc/GMT-3' = s 'E. Africa Standard Time' + val 'Etc/GMT-4' = s 'Arabian Standard Time' + val 'Etc/GMT-5' = s 'West Asia Standard Time' + val 'Etc/GMT-6' = s 'Central Asia Standard Time' + val 'Etc/GMT-7' = s 'SE Asia Standard Time' + val 'Etc/GMT-8' = s 'Singapore Standard Time' + val 'Etc/GMT-9' = s 'Tokyo Standard Time' + val 'Etc/UTC' = s 'UTC' + val 'Europe/Amsterdam' = s 'W. Europe Standard Time' + val 'Europe/Andorra' = s 'W. Europe Standard Time' + val 'Europe/Astrakhan' = s 'Astrakhan Standard Time' + val 'Europe/Athens' = s 'GTB Standard Time' + val 'Europe/Belgrade' = s 'Central Europe Standard Time' + val 'Europe/Berlin' = s 'W. Europe Standard Time' + val 'Europe/Bratislava' = s 'Central Europe Standard Time' + val 'Europe/Brussels' = s 'Romance Standard Time' + val 'Europe/Bucharest' = s 'GTB Standard Time' + val 'Europe/Budapest' = s 'Central Europe Standard Time' + val 'Europe/Busingen' = s 'W. Europe Standard Time' + val 'Europe/Chisinau' = s 'E. Europe Standard Time' + val 'Europe/Copenhagen' = s 'Romance Standard Time' + val 'Europe/Dublin' = s 'GMT Standard Time' + val 'Europe/Gibraltar' = s 'W. Europe Standard Time' + val 'Europe/Guernsey' = s 'GMT Standard Time' + val 'Europe/Helsinki' = s 'FLE Standard Time' + val 'Europe/Isle_of_Man' = s 'GMT Standard Time' + val 'Europe/Istanbul' = s 'Turkey Standard Time' + val 'Europe/Jersey' = s 'GMT Standard Time' + val 'Europe/Kaliningrad' = s 'Kaliningrad Standard Time' + val 'Europe/Kiev' = s 'FLE Standard Time' + val 'Europe/Kirov' = s 'Russian Standard Time' + val 'Europe/Lisbon' = s 'GMT Standard Time' + val 'Europe/Ljubljana' = s 'Central Europe Standard Time' + val 'Europe/London' = s 'GMT Standard Time' + val 'Europe/Luxembourg' = s 'W. Europe Standard Time' + val 'Europe/Madrid' = s 'Romance Standard Time' + val 'Europe/Malta' = s 'W. Europe Standard Time' + val 'Europe/Mariehamn' = s 'FLE Standard Time' + val 'Europe/Minsk' = s 'Belarus Standard Time' + val 'Europe/Monaco' = s 'W. Europe Standard Time' + val 'Europe/Moscow' = s 'Russian Standard Time' + val 'Europe/Oslo' = s 'W. Europe Standard Time' + val 'Europe/Paris' = s 'Romance Standard Time' + val 'Europe/Podgorica' = s 'Central Europe Standard Time' + val 'Europe/Prague' = s 'Central Europe Standard Time' + val 'Europe/Riga' = s 'FLE Standard Time' + val 'Europe/Rome' = s 'W. Europe Standard Time' + val 'Europe/Samara' = s 'Russia Time Zone 3' + val 'Europe/San_Marino' = s 'W. Europe Standard Time' + val 'Europe/Sarajevo' = s 'Central European Standard Time' + val 'Europe/Saratov' = s 'Saratov Standard Time' + val 'Europe/Simferopol' = s 'Russian Standard Time' + val 'Europe/Skopje' = s 'Central European Standard Time' + val 'Europe/Sofia' = s 'FLE Standard Time' + val 'Europe/Stockholm' = s 'W. Europe Standard Time' + val 'Europe/Tallinn' = s 'FLE Standard Time' + val 'Europe/Tirane' = s 'Central Europe Standard Time' + val 'Europe/Ulyanovsk' = s 'Astrakhan Standard Time' + val 'Europe/Vaduz' = s 'W. Europe Standard Time' + val 'Europe/Vatican' = s 'W. Europe Standard Time' + val 'Europe/Vienna' = s 'W. Europe Standard Time' + val 'Europe/Vilnius' = s 'FLE Standard Time' + val 'Europe/Volgograd' = s 'Volgograd Standard Time' + val 'Europe/Warsaw' = s 'Central European Standard Time' + val 'Europe/Zagreb' = s 'Central European Standard Time' + val 'Europe/Zurich' = s 'W. Europe Standard Time' + val 'Indian/Antananarivo' = s 'E. Africa Standard Time' + val 'Indian/Chagos' = s 'Central Asia Standard Time' + val 'Indian/Christmas' = s 'SE Asia Standard Time' + val 'Indian/Cocos' = s 'Myanmar Standard Time' + val 'Indian/Comoro' = s 'E. Africa Standard Time' + val 'Indian/Kerguelen' = s 'West Asia Standard Time' + val 'Indian/Mahe' = s 'Mauritius Standard Time' + val 'Indian/Maldives' = s 'West Asia Standard Time' + val 'Indian/Mauritius' = s 'Mauritius Standard Time' + val 'Indian/Mayotte' = s 'E. Africa Standard Time' + val 'Indian/Reunion' = s 'Mauritius Standard Time' + val 'Pacific/Apia' = s 'Samoa Standard Time' + val 'Pacific/Auckland' = s 'New Zealand Standard Time' + val 'Pacific/Bougainville' = s 'Bougainville Standard Time' + val 'Pacific/Chatham' = s 'Chatham Islands Standard Time' + val 'Pacific/Easter' = s 'Easter Island Standard Time' + val 'Pacific/Efate' = s 'Central Pacific Standard Time' + val 'Pacific/Enderbury' = s 'UTC+13' + val 'Pacific/Fakaofo' = s 'UTC+13' + val 'Pacific/Fiji' = s 'Fiji Standard Time' + val 'Pacific/Funafuti' = s 'UTC+12' + val 'Pacific/Galapagos' = s 'Central America Standard Time' + val 'Pacific/Gambier' = s 'UTC-09' + val 'Pacific/Guadalcanal' = s 'Central Pacific Standard Time' + val 'Pacific/Guam' = s 'West Pacific Standard Time' + val 'Pacific/Honolulu' = s 'Hawaiian Standard Time' + val 'Pacific/Kiritimati' = s 'Line Islands Standard Time' + val 'Pacific/Kosrae' = s 'Central Pacific Standard Time' + val 'Pacific/Kwajalein' = s 'UTC+12' + val 'Pacific/Majuro' = s 'UTC+12' + val 'Pacific/Marquesas' = s 'Marquesas Standard Time' + val 'Pacific/Midway' = s 'UTC-11' + val 'Pacific/Nauru' = s 'UTC+12' + val 'Pacific/Niue' = s 'UTC-11' + val 'Pacific/Norfolk' = s 'Norfolk Standard Time' + val 'Pacific/Noumea' = s 'Central Pacific Standard Time' + val 'Pacific/Pago_Pago' = s 'UTC-11' + val 'Pacific/Palau' = s 'Tokyo Standard Time' + val 'Pacific/Pitcairn' = s 'UTC-08' + val 'Pacific/Ponape' = s 'Central Pacific Standard Time' + val 'Pacific/Port_Moresby' = s 'West Pacific Standard Time' + val 'Pacific/Rarotonga' = s 'Hawaiian Standard Time' + val 'Pacific/Saipan' = s 'West Pacific Standard Time' + val 'Pacific/Tahiti' = s 'Hawaiian Standard Time' + val 'Pacific/Tarawa' = s 'UTC+12' + val 'Pacific/Tongatapu' = s 'Tonga Standard Time' + val 'Pacific/Truk' = s 'West Pacific Standard Time' + val 'Pacific/Wake' = s 'UTC+12' + val 'Pacific/Wallis' = s 'UTC+12' + } + } + } } NoRemove SYSTEM { diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index a31c467f71f..7dc5f72bcbb 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -264,6 +264,15 @@ static ULONG_PTR system_cpu_mask; static pthread_mutex_t timezone_mutex = PTHREAD_MUTEX_INITIALIZER; +static const char default_tzinfo_dir[] = "/usr/share/zoneinfo"; +static const WCHAR Time_ZonesW[] = { '\\','R','e','g','i','s','t','r','y','\\', + 'M','a','c','h','i','n','e','\\', + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s',' ','N','T','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'T','i','m','e',' ','Z','o','n','e','s',0 }; + /******************************************************************************* * Architecture specific feature detection for CPUs * @@ -2860,13 +2869,6 @@ done: static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* tz_name, int year) { - static const WCHAR Time_ZonesW[] = { '\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'M','i','c','r','o','s','o','f','t','\\', - 'W','i','n','d','o','w','s',' ','N','T','\\', - 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', - 'T','i','m','e',' ','Z','o','n','e','s',0 }; RTL_DYNAMIC_TIME_ZONE_INFORMATION reg_tzi; HANDLE key; ULONG idx, len; @@ -2941,6 +2943,76 @@ static time_t find_dst_change(time_t start, time_t end, int *is_dst) return min; } +static BOOL get_tz_info_from_zoneinfo_name( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char *name, int year ) +{ + static const WCHAR wine_tz_map[] = { '\\','R','e','g','i','s','t','r','y','\\', + 'M','a','c','h','i','n','e','\\', + 'S','o','f','t','w','a','r','e','\\', + 'W','i','n','e','\\', + 'T','i','m','e',' ','Z','o','n','e','s','\\', + 'T','Z',' ','M','a','p','p','i','n','g', + 0 }; + + const char *tzinfo_dir = getenv( "TZDIR" ); + UNICODE_STRING key_name, win_name; + WCHAR nameW[64], win_nameW[64]; + OBJECT_ATTRIBUTES attr; + char buf[MAX_PATH]; + HANDLE key; + BOOL ret; + FILE *f; + + TRACE( "name %s.\n", debugstr_a( name )); + + if (strlen(name) >= ARRAY_SIZE(nameW)) return FALSE; + + if (!tzinfo_dir) tzinfo_dir = default_tzinfo_dir; + snprintf( buf, sizeof(buf), "%s/%s", tzinfo_dir, name ); + + if (!(f = fopen( buf, "r" ))) + { + WARN( "Could not open %s.\n", debugstr_a( buf )); + return FALSE; + } + fclose( f ); + + init_unicode_string( &key_name, wine_tz_map ); + InitializeObjectAttributes( &attr, &key_name, 0, NULL, NULL ); + if (NtOpenKey( &key, KEY_READ, &attr )) return FALSE; + + ascii_to_unicode( nameW, name, strlen( name ) + 1 ); + ret = reg_query_value( key, nameW, REG_SZ, win_nameW, sizeof(win_nameW) ); + NtClose( key ); + if (!ret) return FALSE; + TRACE( "got %s for %s.\n", debugstr_w(win_nameW), debugstr_a( name ) ); + + init_unicode_string( &key_name, Time_ZonesW ); + if (NtOpenKey( &key, KEY_READ, &attr )) return FALSE; + init_unicode_string( &win_name, win_nameW ); + ret = read_reg_tz_info( key, &win_name, year, tzi ); + NtClose( key ); + return ret; +} + +static BOOL get_system_config_tz_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, int year ) +{ + char path[PATH_MAX]; + const char *str; + int len; + + if ((str = getenv( "TZ" ))) + { + if (*str == ':') ++str; + return get_tz_info_from_zoneinfo_name( tzi, str, year ); + } + + if (!realpath( "/etc/localtime", path )) return FALSE; + len = sizeof( default_tzinfo_dir ) - 1; + if (strncmp( path, default_tzinfo_dir, len )) return FALSE; + if (path[len] != '/') return FALSE; + return get_tz_info_from_zoneinfo_name( tzi, path + len + 1, year ); +} + static LONG64 get_current_tz_bias(void) { ULONG high, low; @@ -2959,6 +3031,7 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) { static RTL_DYNAMIC_TIME_ZONE_INFORMATION cached_tzi; static int current_year = -1, current_bias = 65535; + RTL_DYNAMIC_TIME_ZONE_INFORMATION reg_tzi; struct tm *tm; char tz_name[16]; time_t year_start, year_end, tmp, dlt = 0, std = 0; @@ -3074,6 +3147,16 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) tzi->StandardBias); } + if (get_system_config_tz_info( ®_tzi, current_year + 1900 )) + { + if (match_tz_info( tzi, ®_tzi ) && match_tz_name( tz_name, ®_tzi )) + { + cached_tzi = *tzi = reg_tzi; + mutex_unlock( &timezone_mutex ); + return; + } + WARN( "System config TZ info didn't match guessed parameters, falling back to search.\n" ); + } find_reg_tz_info(tzi, tz_name, current_year + 1900); cached_tzi = *tzi; mutex_unlock( &timezone_mutex ); diff --git a/tools/make_unicode b/tools/make_unicode index f7ccbd8131d..3cb4ab80c4a 100755 --- a/tools/make_unicode +++ b/tools/make_unicode @@ -71,6 +71,7 @@ my $MAX_CHAR = 0x10ffff; my $nlskey = "-SYSTEM\\-CurrentControlSet\\-Control\\-Nls"; my $zonekey = "-Software\\-Microsoft\\-Windows NT\\-CurrentVersion\\Time Zones"; +my $winezonekey = "-Software\\-Wine\\Time Zones"; my @allfiles = ( @@ -5501,6 +5502,7 @@ sub load_windows_zones() { my $current_name; my %names; + my %mapping; my $base = "cldr-release-$CLDRVERSION"; my $INPUT = open_data_file( "cldr", "$base/common/supplemental/windowsZones.xml" ); while (<$INPUT>) @@ -5513,9 +5515,16 @@ sub load_windows_zones() { $names{$1} = [ $current_name, $2 ]; } + if (/<mapZone other="(.*)" territory=(.*) type="(.*)"\/>/) + { + foreach my $name (split ' ', $3) + { + $mapping{$name} = $1; + } + } } close $INPUT; - return %names; + return (\%names, \%mapping); } @@ -5638,7 +5647,7 @@ sub dump_timezones($@) my $FIRST_YEAR = 2000; my $LAST_YEAR = 2030; - my %names = load_windows_zones(); + my ($names, $mapping) = load_windows_zones(); my %zones; my %rules; my %links; @@ -5694,9 +5703,10 @@ sub dump_timezones($@) close $FILE; } - foreach my $name (sort { uc($a) cmp uc($b) } keys %names) + foreach my $name (sort { uc($a) cmp uc($b) } keys %{$names}) { - my ($display, $zone) = @{$names{$name}}; + my ($display, $zone) = @{%{$names}{$name}}; + $zone = $links{$zone} if defined $links{$zone}; # build list of transitions @@ -5865,6 +5875,12 @@ sub dump_timezones($@) add_registry_dword_value( $zonekey, "$name\\Dynamic DST", "LastEntry", $last_year ); } + foreach my $map (sort { uc($a) cmp uc($b) } keys %{$mapping}) + { + my $name = %{$mapping}{$map}; + add_registry_string_value( $winezonekey, "TZ Mapping", $map, $name ); + } + print OUTPUT "}\n"; close OUTPUT; save_file($filename); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
From: Paul Gofman <pgofman@codeweavers.com> Fixes matching timezones Asia/Dili, Asia/Jayapura, Etc/GMT-9, Pacific/Palau. --- dlls/ntdll/unix/system.c | 60 ++++------------------------------------ 1 file changed, 6 insertions(+), 54 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 7dc5f72bcbb..d8781ff4e27 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2740,48 +2740,6 @@ static BOOL match_tz_info( const RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, match_tz_date(&tzi->DaylightDate, ®_tzi->DaylightDate)); } -static BOOL match_past_tz_bias( time_t past_time, LONG past_bias ) -{ - LONG bias; - struct tm *tm; - if (!past_time) return TRUE; - - tm = gmtime( &past_time ); - bias = (LONG)(mktime(tm) - past_time) / 60; - return bias == past_bias; -} - -static BOOL match_tz_name( const char *tz_name, const RTL_DYNAMIC_TIME_ZONE_INFORMATION *reg_tzi ) -{ - static const struct { - WCHAR key_name[32]; - const char *short_name; - time_t past_time; - LONG past_bias; - } - mapping[] = - { - { {'N','o','r','t','h',' ','K','o','r','e','a',' ','S','t','a','n','d','a','r','d',' ','T','i','m','e',0 }, - "KST", 1451606400 /* 2016-01-01 00:00:00 UTC */, -510 }, - { {'K','o','r','e','a',' ','S','t','a','n','d','a','r','d',' ','T','i','m','e',0 }, - "KST", 1451606400 /* 2016-01-01 00:00:00 UTC */, -540 }, - { {'T','o','k','y','o',' ','S','t','a','n','d','a','r','d',' ','T','i','m','e',0 }, - "JST" }, - { {'Y','a','k','u','t','s','k',' ','S','t','a','n','d','a','r','d',' ','T','i','m','e',0 }, - "+09" }, /* YAKST was used until tzdata 2016f */ - }; - unsigned int i; - - if (reg_tzi->DaylightDate.wMonth) return TRUE; - for (i = 0; i < ARRAY_SIZE(mapping); i++) - { - if (!wcscmp( mapping[i].key_name, reg_tzi->TimeZoneKeyName )) - return !strcmp( mapping[i].short_name, tz_name ) - && match_past_tz_bias( mapping[i].past_time, mapping[i].past_bias ); - } - return TRUE; -} - static BOOL reg_query_value( HKEY key, LPCWSTR name, DWORD type, void *data, DWORD count ) { char buf[256]; @@ -2867,7 +2825,7 @@ done: return ret; } -static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* tz_name, int year) +static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, int year) { RTL_DYNAMIC_TIME_ZONE_INFORMATION reg_tzi; HANDLE key; @@ -2902,7 +2860,7 @@ static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* reg_tzi.DaylightDate.wSecond, reg_tzi.DaylightDate.wMilliseconds, reg_tzi.DaylightBias); - if (match_tz_info( tzi, ®_tzi ) && match_tz_name( tz_name, ®_tzi )) + if (match_tz_info( tzi, ®_tzi )) { *tzi = reg_tzi; NtClose( key ); @@ -2914,8 +2872,8 @@ static void find_reg_tz_info(RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi, const char* if (idx == 1) return; /* registry info not initialized yet */ FIXME("Can't find matching timezone information in the registry for " - "%s, bias %d, std (d/m/y): %u/%02u/%04u, dlt (d/m/y): %u/%02u/%04u\n", - tz_name, tzi->Bias, + "bias %d, std (d/m/y): %u/%02u/%04u, dlt (d/m/y): %u/%02u/%04u\n", + tzi->Bias, tzi->StandardDate.wDay, tzi->StandardDate.wMonth, tzi->StandardDate.wYear, tzi->DaylightDate.wDay, tzi->DaylightDate.wMonth, tzi->DaylightDate.wYear); } @@ -3033,7 +2991,6 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) static int current_year = -1, current_bias = 65535; RTL_DYNAMIC_TIME_ZONE_INFORMATION reg_tzi; struct tm *tm; - char tz_name[16]; time_t year_start, year_end, tmp, dlt = 0, std = 0; int is_dst, bias; BOOL inverted_dst; @@ -3058,11 +3015,6 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) } memset(tzi, 0, sizeof(*tzi)); - if (!strftime(tz_name, sizeof(tz_name), "%Z", tm)) { - /* not enough room or another error */ - tz_name[0] = '\0'; - } - TRACE("tz data will be valid through year %d, bias %d, inverted_dst %d\n", tm->tm_year + 1900, bias, inverted_dst); current_year = tm->tm_year; current_bias = bias; @@ -3149,7 +3101,7 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) if (get_system_config_tz_info( ®_tzi, current_year + 1900 )) { - if (match_tz_info( tzi, ®_tzi ) && match_tz_name( tz_name, ®_tzi )) + if (match_tz_info( tzi, ®_tzi )) { cached_tzi = *tzi = reg_tzi; mutex_unlock( &timezone_mutex ); @@ -3157,7 +3109,7 @@ static void get_timezone_info( RTL_DYNAMIC_TIME_ZONE_INFORMATION *tzi ) } WARN( "System config TZ info didn't match guessed parameters, falling back to search.\n" ); } - find_reg_tz_info(tzi, tz_name, current_year + 1900); + find_reg_tz_info(tzi, current_year + 1900); cached_tzi = *tzi; mutex_unlock( &timezone_mutex ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
From: Paul Gofman <pgofman@codeweavers.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50532 --- dlls/ntdll/unix/system.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index d8781ff4e27..8b8543dc3b2 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2883,14 +2883,25 @@ static time_t find_dst_change(time_t start, time_t end, int *is_dst) struct tm *tm; ULONGLONG min = (sizeof(time_t) == sizeof(int)) ? (ULONG)start : start; ULONGLONG max = (sizeof(time_t) == sizeof(int)) ? (ULONG)end : end; + time_t pos; tm = localtime(&start); *is_dst = !tm->tm_isdst; TRACE("starting date isdst %d, %s", !*is_dst, ctime(&start)); + for (pos = min; pos <= max; pos += 30 * 24 * 3600) + { + tm = localtime(&pos); + if (tm->tm_isdst == *is_dst) + { + max = pos; + break; + } + } + while (min <= max) { - time_t pos = (min + max) / 2; + pos = (min + max) / 2; tm = localtime(&pos); if (tm->tm_isdst != *is_dst) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
From: Paul Gofman <pgofman@codeweavers.com> Fixes Australia/Lord_Howe timezone match. --- dlls/ntdll/unix/system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 8b8543dc3b2..fd4ff3002ec 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2727,7 +2727,7 @@ static BOOL match_tz_date( const RTL_SYSTEM_TIME *st, const RTL_SYSTEM_TIME *reg return (st->wDay == wDay && st->wHour == reg_st->wHour && - st->wMinute == reg_st->wMinute && + (st->wMinute == reg_st->wMinute || (st->wMinute == 30 && !reg_st->wMinute)) && st->wSecond == reg_st->wSecond && st->wMilliseconds == reg_st->wMilliseconds); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10134
Correct timezone info and name is important for some games which, e. g., expect the user region guessed from timezone information match user region. 1. Some few time zones, e. g., Europe/Dublin, are defined with "inverse DST" on Unix. Dublin standard time is IST (Ireland Standard Time, not to confuse, e. g, with Indian Standard Time) which is in effect on summer, while daylight shift is defined as 1 hour back in autumn. libc time functions return tm.tm_isdst as 0 now (in winter) and 1 in summer. That breaks various things: - GetLocalTime() is plainly wrong, the current timezone offset calculation doesn't mind such case. That is fixed by "ntdll: Get current timezone bias from user shared data.", "server: Avoid relying on tm->tm_isdst in set_user_shared_data_time().". - Timezone matching in get_timezone_info() goes wrong in various ways for such a case. For a start, it ends up with wrong timezone bias and a year of only 1 hour length now. "ntdll: Fix get_timezone_info() for timezones defined with inverse DST on Unix." fixes all of that and lets the algorithm correctly find GMT timezone for Dublin (instead of 'W. Central Africa Standard Time' as it does now); 2. There are cases when all the current zones have all the current year parameters matching, so the current algorithm picks the first one. That result in the timezone name and details being from absolutely different region sometimes. Similar issues were partially addressed in the past disambiguating by past date conversion (e. g., https://bugs.winehq.org/show_bug.cgi?id=50213 , commit 22612bcff5ecc7ece4acc4325aa8b19e6129b2cf ). But that covers only some part of the cases, also not quite correctly (the existing disambiguation breaks zone matching for a few existing zones). E. g, right now Mexico City (set as America/Mexico_City Unix localtime) matches to Canada Central Time Zone (can in theory be disambiguated using histroy info), but there are also cases when there are different zones with fully identical parameters without DST which can't be disambiguated this way ar all. So it seems to me that for correct results it is both unavoidable and much simpler to have an explicit mapping f rom Unix timezone and Windows timezone. So, even for the cases where it is possible, we don't have to do some very ad-hoc and unobvious disambiguation checks and have the match just generated from the Unicode data we are already parsing in make_unicode. I made that in "ntdll: Try to determine system time zone to Windows zone by name.". At this point I made a thorough test of whether the matching is now correct across all the timezones and the changes don't break anything. I am attaching test C program which prints current locale info, bash script which runs this for every Unix timezone as 'TZ=<tzname> wine a.exe' and greps output for expected Windows timezone (the input to bash script with all the zone names is also attached). [test_tz2.sh](/uploads/150ac3566177e84db0f575457ef0a08c/test_tz2.sh) [tzt.c](/uploads/5368b882a04ee9181733a9e03f5f10ad/tzt.c) [test.txt](/uploads/57ab6f8e37609d9adf0cac16521c4574/test.txt) I also ran this for time(NULL) in the beginning at get_timezone_info() offset 3 month, 6 month, and 9month to make sure that the code is still correct through the timezones when the current time's DST state changes. That revealed some additional issues: - Match was failing for Asia/Dili, Asia/Jayapura, Etc/GMT-9, Pacific/Palau, due to check_tz_name() check which is not quite correct. I dropped that check in "ntdll: Drop additional TZ matching with match_tz_name().", because now the part which matches Unix timezone name is supposed to disambiguate the timezones anyway; - Africa/Casablanca is special, at least this year. It changed to STD on Feb15 for some reason and going to DLR month later, on Mar22. The present find_dst_change() binary search algorithm doesn't work well for Casablanca this year, it skips the dates (it is in general mentioned in https://bugs.winehq.org/show_bug.cgi?id=50532 but at the present moment Casablanca looks like the only case which hits this issue). I made the search a bit more reliable in "ntdll: Better search for DST change in find_dst_change()."; - Australia/Lord_Howe : performs shift to STD time at 2:30, which is reflected in Unix timezone, while Windows timezone info has 2:00 (both on up to date Windows 11 and in our data). I relaxed the match in "ntdll: Accept 30min shift in match_tz_date()." because I suspect that Windows just never puts half hours there but it is hard to be sure because that looks like the only case at the current moment. Finally, there are 3 remaining failures in my test (while the failure happens when the actual timezone name didn't match our mapping); - America/Scoresbysund: seems to be an error in Unicode winsowsZones.xml: matches that to "Azores Standard Time" but that is actually "Greenland Standard Time" (Azores Standard Time is plainly differs in current time), but fallback algorithm finds the correct zone, so this is not an actual issue currently; - Antarctica/Casey: similarly (but with wrong matching result), should be "W. Australia Standard Time", but is listed as "Central Pacific Standard Time". The algorithm finds "China Standard Time" which is wrong (both before and after my changes), but it looks like it is impossible to distinguish other than by name: there is no DST in neither zone, nor static nor dynamic; - Antarctica/Vostok: similar, mapped in Unicode data to "Central Asia Standard Time" which is incompatible zone. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10134#note_129931
I realized that current timezone bias estimation I changed in the MR is now wrong at the times within an hour of DST change time, I am going to fix it. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10134#note_129964
participants (2)
-
Paul Gofman -
Paul Gofman (@gofman)