diff options
author | Philip Withnall <philip@tecnocode.co.uk> | 2019-07-05 11:08:32 +0000 |
---|---|---|
committer | Philip Withnall <philip@tecnocode.co.uk> | 2019-07-05 11:08:32 +0000 |
commit | 711b4b05781a4459962375ac14be26164e7adfd3 (patch) | |
tree | 2550a39eab0c9786c7ad9df97e5a63b39f535708 | |
parent | dc774db6088d12cafa1b46ab8fef588487498463 (diff) | |
parent | 5d54727180b46765c30a51e7ebbb2a743f4120c2 (diff) | |
download | glib-711b4b05781a4459962375ac14be26164e7adfd3.tar.gz |
Merge branch 'win32-enhance-gtimezone' into 'master'
Win32 enhance and fix gtimezone
Closes #870
See merge request GNOME/glib!912
-rw-r--r-- | glib/gtimezone.c | 193 | ||||
-rw-r--r-- | glib/tests/gdatetime.c | 37 |
2 files changed, 174 insertions, 56 deletions
diff --git a/glib/gtimezone.c b/glib/gtimezone.c index 970b4b1a5..26a563403 100644 --- a/glib/gtimezone.c +++ b/glib/gtimezone.c @@ -39,8 +39,10 @@ #include "gdate.h" #ifdef G_OS_WIN32 + #define STRICT #include <windows.h> +#include <wchar.h> #endif /** @@ -602,10 +604,23 @@ copy_windows_systemtime (SYSTEMTIME *s_time, TimeZoneDate *tzdate) } /* UTC = local time + bias while local time = UTC + offset */ -static void +static gboolean rule_from_windows_time_zone_info (TimeZoneRule *rule, TIME_ZONE_INFORMATION *tzi) { + gchar *std_name, *dlt_name; + + std_name = g_utf16_to_utf8 ((gunichar2 *)tzi->StandardName, -1, NULL, NULL, NULL); + if (std_name == NULL) + return FALSE; + + dlt_name = g_utf16_to_utf8 ((gunichar2 *)tzi->DaylightName, -1, NULL, NULL, NULL); + if (dlt_name == NULL) + { + g_free (std_name); + return FALSE; + } + /* Set offset */ if (tzi->StandardDate.wMonth) { @@ -614,7 +629,6 @@ rule_from_windows_time_zone_info (TimeZoneRule *rule, copy_windows_systemtime (&(tzi->DaylightDate), &(rule->dlt_start)); copy_windows_systemtime (&(tzi->StandardDate), &(rule->dlt_end)); - } else @@ -622,31 +636,41 @@ rule_from_windows_time_zone_info (TimeZoneRule *rule, rule->std_offset = -tzi->Bias * 60; rule->dlt_start.mon = 0; } - strncpy (rule->std_name, (gchar*)tzi->StandardName, NAME_SIZE - 1); - strncpy (rule->dlt_name, (gchar*)tzi->DaylightName, NAME_SIZE - 1); + strncpy (rule->std_name, std_name, NAME_SIZE - 1); + strncpy (rule->dlt_name, dlt_name, NAME_SIZE - 1); + + g_free (std_name); + g_free (dlt_name); + + return TRUE; } static gchar* windows_default_tzname (void) { - const gchar *subkey = - "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation"; + const gunichar2 *subkey = + L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation"; HKEY key; gchar *key_name = NULL; - if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0, + gunichar2 *key_name_w = NULL; + if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { DWORD size = 0; - if (RegQueryValueExA (key, "TimeZoneKeyName", NULL, NULL, + if (RegQueryValueExW (key, L"TimeZoneKeyName", NULL, NULL, NULL, &size) == ERROR_SUCCESS) { - key_name = g_malloc ((gint)size); - if (RegQueryValueExA (key, "TimeZoneKeyName", NULL, NULL, - (LPBYTE)key_name, &size) != ERROR_SUCCESS) + key_name_w = g_malloc ((gint)size); + + if (key_name_w == NULL || + RegQueryValueExW (key, L"TimeZoneKeyName", NULL, NULL, + (LPBYTE)key_name_w, &size) != ERROR_SUCCESS) { - g_free (key_name); + g_free (key_name_w); key_name = NULL; } + else + key_name = g_utf16_to_utf8 (key_name_w, -1, NULL, NULL, NULL); } RegCloseKey (key); } @@ -693,10 +717,12 @@ register_tzi_to_tzi (RegTZI *reg, TIME_ZONE_INFORMATION *tzi) static guint rules_from_windows_time_zone (const gchar *identifier, gchar **out_identifier, - TimeZoneRule **rules) + TimeZoneRule **rules, + gboolean copy_identifier) { HKEY key; - gchar *subkey, *subkey_dynamic; + gchar *subkey = NULL; + gchar *subkey_dynamic = NULL; gchar *key_name = NULL; const gchar *reg_key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"; @@ -704,11 +730,18 @@ rules_from_windows_time_zone (const gchar *identifier, DWORD size; guint rules_num = 0; RegTZI regtzi, regtzi_prev; + WCHAR winsyspath[MAX_PATH]; + gunichar2 *subkey_w, *subkey_dynamic_w; - g_assert (out_identifier != NULL); + if (GetSystemDirectoryW (winsyspath, MAX_PATH) == 0) + return 0; + + g_assert (copy_identifier == FALSE || out_identifier != NULL); g_assert (rules != NULL); - *out_identifier = NULL; + if (copy_identifier) + *out_identifier = NULL; + *rules = NULL; key_name = NULL; @@ -721,90 +754,133 @@ rules_from_windows_time_zone (const gchar *identifier, return 0; subkey = g_strconcat (reg_key, key_name, NULL); + subkey_w = g_utf8_to_utf16 (subkey, -1, NULL, NULL, NULL); + if (subkey_w == NULL) + goto utf16_conv_failed; + subkey_dynamic = g_strconcat (subkey, "\\Dynamic DST", NULL); + subkey_dynamic_w = g_utf8_to_utf16 (subkey_dynamic, -1, NULL, NULL, NULL); + if (subkey_dynamic_w == NULL) + goto utf16_conv_failed; - if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0, + if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_w, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) - return 0; + goto utf16_conv_failed; + size = sizeof tzi.StandardName; - if (RegQueryValueExA (key, "Std", NULL, NULL, - (LPBYTE)&(tzi.StandardName), &size) != ERROR_SUCCESS) - goto failed; + + /* use RegLoadMUIStringW() to query MUI_Std from the registry if possible, otherwise + fallback to querying Std */ + if (RegLoadMUIStringW (key, L"MUI_Std", tzi.StandardName, + size, &size, 0, winsyspath) != ERROR_SUCCESS) + { + size = sizeof tzi.StandardName; + if (RegQueryValueExW (key, L"Std", NULL, NULL, + (LPBYTE)&(tzi.StandardName), &size) != ERROR_SUCCESS) + goto registry_failed; + } size = sizeof tzi.DaylightName; - if (RegQueryValueExA (key, "Dlt", NULL, NULL, - (LPBYTE)&(tzi.DaylightName), &size) != ERROR_SUCCESS) - goto failed; + /* use RegLoadMUIStringW() to query MUI_Dlt from the registry if possible, otherwise + fallback to querying Dlt */ + if (RegLoadMUIStringW (key, L"MUI_Dlt", tzi.DaylightName, + size, &size, 0, winsyspath) != ERROR_SUCCESS) + { + size = sizeof tzi.DaylightName; + if (RegQueryValueExW (key, L"Dlt", NULL, NULL, + (LPBYTE)&(tzi.DaylightName), &size) != ERROR_SUCCESS) + goto registry_failed; + } RegCloseKey (key); - if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey_dynamic, 0, + if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_dynamic_w, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { DWORD first, last; int year, i; - gchar *s; + wchar_t s[12]; size = sizeof first; - if (RegQueryValueExA (key, "FirstEntry", NULL, NULL, + if (RegQueryValueExW (key, L"FirstEntry", NULL, NULL, (LPBYTE) &first, &size) != ERROR_SUCCESS) - goto failed; + goto registry_failed; size = sizeof last; - if (RegQueryValueExA (key, "LastEntry", NULL, NULL, + if (RegQueryValueExW (key, L"LastEntry", NULL, NULL, (LPBYTE) &last, &size) != ERROR_SUCCESS) - goto failed; + goto registry_failed; rules_num = last - first + 2; *rules = g_new0 (TimeZoneRule, rules_num); - for (year = first, i = 0; year <= last; year++) + for (year = first, i = 0; *rules != NULL && year <= last; year++) { - s = g_strdup_printf ("%d", year); + gboolean failed = FALSE; + swprintf_s (s, 11, L"%d", year); + + if (!failed) + { + size = sizeof regtzi; + if (RegQueryValueExW (key, s, NULL, NULL, + (LPBYTE) ®tzi, &size) != ERROR_SUCCESS) + failed = TRUE; + } - size = sizeof regtzi; - if (RegQueryValueExA (key, s, NULL, NULL, - (LPBYTE) ®tzi, &size) != ERROR_SUCCESS) + if (failed) { g_free (*rules); *rules = NULL; break; } - g_free (s); - if (year > first && memcmp (®tzi_prev, ®tzi, sizeof regtzi) == 0) continue; else memcpy (®tzi_prev, ®tzi, sizeof regtzi); register_tzi_to_tzi (®tzi, &tzi); - rule_from_windows_time_zone_info (&(*rules)[i], &tzi); + + if (!rule_from_windows_time_zone_info (&(*rules)[i], &tzi)) + { + g_free (*rules); + *rules = NULL; + break; + } + (*rules)[i++].start_year = year; } rules_num = i + 1; -failed: +registry_failed: RegCloseKey (key); } - else if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0, + else if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_w, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { size = sizeof regtzi; - if (RegQueryValueExA (key, "TZI", NULL, NULL, + if (RegQueryValueExW (key, L"TZI", NULL, NULL, (LPBYTE) ®tzi, &size) == ERROR_SUCCESS) { rules_num = 2; *rules = g_new0 (TimeZoneRule, 2); register_tzi_to_tzi (®tzi, &tzi); - rule_from_windows_time_zone_info (&(*rules)[0], &tzi); + + if (!rule_from_windows_time_zone_info (&(*rules)[0], &tzi)) + { + g_free (*rules); + *rules = NULL; + } } RegCloseKey (key); } +utf16_conv_failed: + g_free (subkey_dynamic_w); g_free (subkey_dynamic); + g_free (subkey_w); g_free (subkey); if (*rules) @@ -815,7 +891,10 @@ failed: else (*rules)[rules_num - 1].start_year = (*rules)[rules_num - 2].start_year + 1; - *out_identifier = g_steal_pointer (&key_name); + if (copy_identifier) + *out_identifier = g_steal_pointer (&key_name); + else + g_free (key_name); return rules_num; } @@ -1352,9 +1431,15 @@ rules_from_identifier (const gchar *identifier, /* Use US rules, Windows' default is Pacific Standard Time */ if ((rules_num = rules_from_windows_time_zone ("Pacific Standard Time", - out_identifier, - rules))) + NULL, + rules, + FALSE))) { + /* We don't want to hardcode our identifier here as + * "Pacific Standard Time", use what was passed in + */ + *out_identifier = g_strdup (identifier); + for (i = 0; i < rules_num - 1; i++) { (*rules)[i].std_offset = - tzr.std_offset; @@ -1499,7 +1584,8 @@ g_time_zone_new (const gchar *identifier) #elif defined (G_OS_WIN32) if ((rules_num = rules_from_windows_time_zone (identifier, &resolved_identifier, - &rules))) + &rules, + TRUE))) { init_zone_from_rules (tz, rules, rules_num, g_steal_pointer (&resolved_identifier)); g_free (rules); @@ -1518,15 +1604,16 @@ g_time_zone_new (const gchar *identifier) { rules = g_new0 (TimeZoneRule, 2); - rule_from_windows_time_zone_info (&rules[0], &tzi); - - memset (rules[0].std_name, 0, NAME_SIZE); - memset (rules[0].dlt_name, 0, NAME_SIZE); + if (rule_from_windows_time_zone_info (&rules[0], &tzi)) + { + memset (rules[0].std_name, 0, NAME_SIZE); + memset (rules[0].dlt_name, 0, NAME_SIZE); - rules[0].start_year = MIN_TZYEAR; - rules[1].start_year = MAX_TZYEAR; + rules[0].start_year = MIN_TZYEAR; + rules[1].start_year = MAX_TZYEAR; - init_zone_from_rules (tz, rules, 2, windows_default_tzname ()); + init_zone_from_rules (tz, rules, 2, windows_default_tzname ()); + } g_free (rules); } diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c index 6d89ea47a..32dd69283 100644 --- a/glib/tests/gdatetime.c +++ b/glib/tests/gdatetime.c @@ -25,6 +25,11 @@ #include <glib/gstdio.h> #include <locale.h> +#ifdef G_OS_WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + #define ASSERT_DATE(dt,y,m,d) G_STMT_START { \ g_assert_nonnull ((dt)); \ g_assert_cmpint ((y), ==, g_date_time_get_year ((dt))); \ @@ -1029,6 +1034,13 @@ test_GDateTime_new_full (void) GTimeZone *tz, *dt_tz; GDateTime *dt; +#ifdef G_OS_WIN32 + LCID currLcid = GetThreadLocale (); + LANGID currLangId = LANGIDFROMLCID (currLcid); + LANGID en = MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US); + SetThreadUILanguage (en); +#endif + dt = g_date_time_new_utc (2009, 12, 11, 12, 11, 10); g_assert_cmpint (2009, ==, g_date_time_get_year (dt)); g_assert_cmpint (12, ==, g_date_time_get_month (dt)); @@ -1063,6 +1075,7 @@ test_GDateTime_new_full (void) g_date_time_get_timezone_abbreviation (dt)); g_assert_cmpstr ("Pacific Standard Time", ==, g_time_zone_get_identifier (dt_tz)); + SetThreadUILanguage (currLangId); #endif g_assert (!g_date_time_is_daylight_savings (dt)); g_date_time_unref (dt); @@ -1333,6 +1346,11 @@ test_GDateTime_printf (void) struct tm tt; time_t t; +#ifdef G_OS_WIN32 + gchar *current_tz = NULL; + DYNAMIC_TIME_ZONE_INFORMATION dtz_info; +#endif + #define TEST_PRINTF(f,o) G_STMT_START { \ GDateTime *__dt = g_date_time_new_local (2009, 10, 24, 0, 0, 0);\ gchar *__p = g_date_time_format (__dt, (f)); \ @@ -1430,7 +1448,14 @@ GDateTime *__dt = g_date_time_new_local (2009, 10, 24, 0, 0, 0);\ #ifdef G_OS_UNIX TEST_PRINTF ("%Z", dst); #elif defined G_OS_WIN32 - TEST_PRINTF ("%Z", "Pacific Standard Time"); + g_assert (GetDynamicTimeZoneInformation (&dtz_info) != TIME_ZONE_ID_INVALID); + if (wcscmp (dtz_info.StandardName, L"") != 0) + current_tz = g_utf16_to_utf8 (dtz_info.StandardName, -1, NULL, NULL, NULL); + else + current_tz = g_utf16_to_utf8 (dtz_info.DaylightName, -1, NULL, NULL, NULL); + + TEST_PRINTF ("%Z", current_tz); + g_free (current_tz); #endif if (old_lc_messages != NULL) @@ -2391,6 +2416,12 @@ test_identifier (void) GTimeZone *tz; gchar *old_tz = g_strdup (g_getenv ("TZ")); +#ifdef G_OS_WIN32 + const char *recife_tz = "SA Eastern Standard Time"; +#else + const char *recife_tz = "America/Recife"; +#endif + tz = g_time_zone_new ("UTC"); g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC"); g_time_zone_unref (tz); @@ -2420,10 +2451,10 @@ test_identifier (void) g_time_zone_unref (tz); /* Local timezone tests. */ - if (g_setenv ("TZ", "America/Recife", TRUE)) + if (g_setenv ("TZ", recife_tz, TRUE)) { tz = g_time_zone_new_local (); - g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "America/Recife"); + g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, recife_tz); g_time_zone_unref (tz); } |