mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 19:36:18 +01:00
Merge branch 'win32-enhance-gtimezone' into 'master'
Win32 enhance and fix gtimezone Closes #870 See merge request GNOME/glib!912
This commit is contained in:
commit
711b4b0578
195
glib/gtimezone.c
195
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_dynamic = g_strconcat (subkey, "\\Dynamic DST", NULL);
|
||||
subkey_w = g_utf8_to_utf16 (subkey, -1, NULL, NULL, NULL);
|
||||
if (subkey_w == NULL)
|
||||
goto utf16_conv_failed;
|
||||
|
||||
if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0,
|
||||
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 (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);
|
||||
|
||||
size = sizeof regtzi;
|
||||
if (RegQueryValueExA (key, s, NULL, NULL,
|
||||
(LPBYTE) ®tzi, &size) != ERROR_SUCCESS)
|
||||
if (!failed)
|
||||
{
|
||||
size = sizeof regtzi;
|
||||
if (RegQueryValueExW (key, s, NULL, NULL,
|
||||
(LPBYTE) ®tzi, &size) != ERROR_SUCCESS)
|
||||
failed = TRUE;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user