Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Step:15
glib2
glib2-add-support-for-slim-timezone-format.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File glib2-add-support-for-slim-timezone-format.patch of Package glib2
diff --unified --recursive --text glib-2.54.3.orig/glib/gtimezone.c glib-2.54.3.new/glib/gtimezone.c --- glib-2.54.3.orig/glib/gtimezone.c 2020-11-13 10:00:23.761986808 +0800 +++ glib-2.54.3.new/glib/gtimezone.c 2020-11-13 10:00:59.318654064 +0800 @@ -39,8 +39,10 @@ #include "gdate.h" #ifdef G_OS_WIN32 + #define STRICT #include <windows.h> +#include <wchar.h> #endif /** @@ -52,10 +54,17 @@ * #GTimeZone is a structure that represents a time zone, at no * particular point in time. It is refcounted and immutable. * + * Each time zone has an identifier (for example, ‘Europe/London’) which is + * platform dependent. See g_time_zone_new() for information on the identifier + * formats. The identifier of a time zone can be retrieved using + * g_time_zone_get_identifier(). + * * A time zone contains a number of intervals. Each interval has - * an abbreviation to describe it, an offet to UTC and a flag indicating - * if the daylight savings time is in effect during that interval. A - * time zone always has at least one interval -- interval 0. + * an abbreviation to describe it (for example, ‘PDT’), an offet to UTC and a + * flag indicating if the daylight savings time is in effect during that + * interval. A time zone always has at least one interval — interval 0. Note + * that interval abbreviations are not the same as time zone identifiers + * (apart from ‘UTC’), and cannot be passed to g_time_zone_new(). * * Every UTC time is contained within exactly one interval, but a given * local time may be contained within zero, one or two intervals (due to @@ -132,9 +141,7 @@ gint mday; gint wday; gint week; - gint hour; - gint min; - gint sec; + gint32 offset; /* hour*3600 + min*60 + sec; can be negative. */ } TimeZoneDate; /* POSIX Timezone abbreviations are typically 3 or 4 characters, but @@ -304,6 +311,10 @@ return canon; } +#ifdef G_OS_UNIX +static GTimeZone *parse_footertz (const gchar *, size_t); +#endif + /** * g_time_zone_unref: * @tz: a #GTimeZone @@ -341,7 +352,7 @@ if (tz->t_info != NULL) { - gint idx; + guint idx; for (idx = 0; idx < tz->t_info->len; idx++) { TransitionInfo *info = &g_array_index (tz->t_info, TransitionInfo, idx); @@ -385,13 +396,20 @@ /* fake zoneinfo creation (for RFC3339/ISO 8601 timezones) {{{1 */ /* * parses strings of the form h or hh[[:]mm[[[:]ss]]] where: - * - h[h] is 0 to 23 + * - h[h] is 0 to 24 * - mm is 00 to 59 * - ss is 00 to 59 + * If RFC8536, TIME_ is a transition time sans sign, + * so colons are required before mm and ss, and hh can be up to 167. + * See Internet RFC 8536 section 3.3.1: + * https://tools.ietf.org/html/rfc8536#section-3.3.1 + * and POSIX Base Definitions 8.3 TZ rule time: + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 */ static gboolean parse_time (const gchar *time_, - gint32 *offset) + gint32 *offset, + gboolean rfc8536) { if (*time_ < '0' || '9' < *time_) return FALSE; @@ -409,7 +427,20 @@ *offset *= 10; *offset += 60 * 60 * (*time_++ - '0'); - if (*offset > 23 * 60 * 60) + if (rfc8536) + { + /* Internet RFC 8536 section 3.3.1 and POSIX 8.3 TZ together say + that a transition time must be of the form [+-]hh[:mm[:ss]] where + the hours part can range from -167 to 167. */ + if ('0' <= *time_ && *time_ <= '9') + { + *offset *= 10; + *offset += 60 * 60 * (*time_++ - '0'); + } + if (*offset > 167 * 60 * 60) + return FALSE; + } + else if (*offset > 24 * 60 * 60) return FALSE; if (*time_ == '\0') @@ -418,6 +449,8 @@ if (*time_ == ':') time_++; + else if (rfc8536) + return FALSE; if (*time_ < '0' || '5' < *time_) return FALSE; @@ -434,6 +467,8 @@ if (*time_ == ':') time_++; + else if (rfc8536) + return FALSE; if (*time_ < '0' || '5' < *time_) return FALSE; @@ -450,32 +485,38 @@ static gboolean parse_constant_offset (const gchar *name, - gint32 *offset) + gint32 *offset, + gboolean rfc8536) { - if (g_strcmp0 (name, "UTC") == 0) + /* Internet RFC 8536 section 3.3.1 and POSIX 8.3 TZ together say + that a transition time must be numeric. */ + if (!rfc8536 && g_strcmp0 (name, "UTC") == 0) { *offset = 0; return TRUE; } if (*name >= '0' && '9' >= *name) - return parse_time (name, offset); + return parse_time (name, offset, rfc8536); switch (*name++) { case 'Z': *offset = 0; - return !*name; + /* Internet RFC 8536 section 3.3.1 requires a numeric zone. */ + return !rfc8536 && !*name; case '+': - return parse_time (name, offset); + return parse_time (name, offset, rfc8536); case '-': - if (parse_time (name, offset)) + if (parse_time (name, offset, rfc8536)) { *offset = -*offset; return TRUE; } + else + return FALSE; default: return FALSE; @@ -488,14 +529,14 @@ gint32 offset; TransitionInfo info; - if (name == NULL || !parse_constant_offset (name, &offset)) + if (name == NULL || !parse_constant_offset (name, &offset, FALSE)) return; info.gmt_offset = offset; info.is_dst = FALSE; info.abbrev = g_strdup (name); - + gtz->name = g_strdup (name); gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), 1); g_array_append_val (gtz->t_info, info); @@ -505,11 +546,18 @@ #ifdef G_OS_UNIX static GBytes* -zone_info_unix (const gchar *identifier) +zone_info_unix (const gchar *identifier, + gchar **out_identifier) { gchar *filename; GMappedFile *file = NULL; GBytes *zoneinfo = NULL; + gchar *resolved_identifier = NULL; + const gchar *tzdir; + + tzdir = getenv ("TZDIR"); + if (tzdir == NULL) + tzdir = "/usr/share/zoneinfo"; /* identifier can be a relative or absolute path name; if relative, it is interpreted starting from /usr/share/zoneinfo @@ -517,11 +565,7 @@ glibc allows both syntaxes, so we should too */ if (identifier != NULL) { - const gchar *tzdir; - - tzdir = getenv ("TZDIR"); - if (tzdir == NULL) - tzdir = "/usr/share/zoneinfo"; + resolved_identifier = g_strdup (identifier); if (*identifier == ':') identifier ++; @@ -532,7 +576,63 @@ filename = g_build_filename (tzdir, identifier, NULL); } else - filename = g_strdup ("/etc/localtime"); + { + gsize prefix_len = 0; + gchar *canonical_path = NULL; + GError *read_link_err = NULL; + + filename = g_strdup ("/etc/localtime"); + + /* Resolve the actual timezone pointed to by /etc/localtime. */ + resolved_identifier = g_file_read_link (filename, &read_link_err); + if (resolved_identifier == NULL) + { + gboolean not_a_symlink = g_error_matches (read_link_err, + G_FILE_ERROR, + G_FILE_ERROR_INVAL); + g_clear_error (&read_link_err); + + /* Fallback to the content of /var/db/zoneinfo or /etc/timezone + * if /etc/localtime is not a symlink. /var/db/zoneinfo is + * where 'tzsetup' program on FreeBSD and DragonflyBSD stores + * the timezone chosen by the user. /etc/timezone is where user + * choice is expressed on Gentoo OpenRC and others. */ + if (not_a_symlink && (g_file_get_contents ("/var/db/zoneinfo", + &resolved_identifier, + NULL, NULL) || + g_file_get_contents ("/etc/timezone", + &resolved_identifier, + NULL, NULL))) + g_strchomp (resolved_identifier); + else + { + /* Error */ + g_assert (resolved_identifier == NULL); + goto out; + } + } + else + { + /* Resolve relative path */ + canonical_path = g_canonicalize_filename (resolved_identifier, "/etc"); + g_free (resolved_identifier); + resolved_identifier = g_steal_pointer (&canonical_path); + } + + /* Strip the prefix and slashes if possible. */ + if (g_str_has_prefix (resolved_identifier, tzdir)) + { + prefix_len = strlen (tzdir); + while (*(resolved_identifier + prefix_len) == '/') + prefix_len++; + } + + if (prefix_len > 0) + memmove (resolved_identifier, resolved_identifier + prefix_len, + strlen (resolved_identifier) - prefix_len + 1 /* nul terminator */); + + g_free (canonical_path); + } file = g_mapped_file_new (filename, FALSE, NULL); if (file != NULL) @@ -543,12 +643,23 @@ g_mapped_file_ref (file)); g_mapped_file_unref (file); } + + g_assert (resolved_identifier != NULL); + +out: + if (out_identifier != NULL) + *out_identifier = g_steal_pointer (&resolved_identifier); + + g_free (resolved_identifier); g_free (filename); + return zoneinfo; } static void -init_zone_from_iana_info (GTimeZone *gtz, GBytes *zoneinfo) +init_zone_from_iana_info (GTimeZone *gtz, + GBytes *zoneinfo, + gchar *identifier /* (transfer full) */) { gsize size; guint index; @@ -556,12 +667,19 @@ guint8 *tz_transitions, *tz_type_index, *tz_ttinfo; guint8 *tz_abbrs; gsize timesize = sizeof (gint32); - const struct tzhead *header = g_bytes_get_data (zoneinfo, &size); + gconstpointer header_data = g_bytes_get_data (zoneinfo, &size); + const gchar *data = header_data; + const struct tzhead *header = header_data; + GTimeZone *footertz = NULL; + guint extra_time_count = 0, extra_type_count = 0; + gint64 last_explicit_transition_time; g_return_if_fail (size >= sizeof (struct tzhead) && memcmp (header, "TZif", 4) == 0); - if (header->tzh_version == '2') + /* FIXME: Handle invalid TZif files better (Issue#1088). */ + + if (header->tzh_version >= '2') { /* Skip ahead to the newer 64-bit data if it's available. */ header = (const struct tzhead *) @@ -577,15 +695,40 @@ time_count = guint32_from_be(header->tzh_timecnt); type_count = guint32_from_be(header->tzh_typecnt); + if (header->tzh_version >= '2') + { + const gchar *footer = (((const gchar *) (header + 1)) + + guint32_from_be(header->tzh_ttisgmtcnt) + + guint32_from_be(header->tzh_ttisstdcnt) + + 12 * guint32_from_be(header->tzh_leapcnt) + + 9 * time_count + + 6 * type_count + + guint32_from_be(header->tzh_charcnt)); + const gchar *footerlast; + size_t footerlen; + g_return_if_fail (footer <= data + size - 2 && footer[0] == '\n'); + footerlast = memchr (footer + 1, '\n', data + size - (footer + 1)); + g_return_if_fail (footerlast); + footerlen = footerlast + 1 - footer; + if (footerlen != 2) + { + footertz = parse_footertz (footer, footerlen); + g_return_if_fail (footertz); + extra_type_count = footertz->t_info->len; + extra_time_count = footertz->transitions->len; + } + } + tz_transitions = ((guint8 *) (header) + sizeof (*header)); tz_type_index = tz_transitions + timesize * time_count; tz_ttinfo = tz_type_index + time_count; tz_abbrs = tz_ttinfo + sizeof (struct ttinfo) * type_count; + gtz->name = g_steal_pointer (&identifier); gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), - type_count); + type_count + extra_type_count); gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition), - time_count); + time_count + extra_time_count); for (index = 0; index < type_count; index++) { @@ -600,15 +743,50 @@ for (index = 0; index < time_count; index++) { Transition trans; - if (header->tzh_version == '2') + if (header->tzh_version >= '2') trans.time = gint64_from_be (((gint64_be*)tz_transitions)[index]); else trans.time = gint32_from_be (((gint32_be*)tz_transitions)[index]); + last_explicit_transition_time = trans.time; trans.info_index = tz_type_index[index]; g_assert (trans.info_index >= 0); - g_assert (trans.info_index < gtz->t_info->len); + g_assert ((guint) trans.info_index < gtz->t_info->len); g_array_append_val (gtz->transitions, trans); } + + if (footertz) + { + /* Append footer time types. Don't bother to coalesce + duplicates with existing time types. */ + for (index = 0; index < extra_type_count; index++) + { + TransitionInfo t_info; + TransitionInfo *footer_t_info + = &g_array_index (footertz->t_info, TransitionInfo, index); + t_info.gmt_offset = footer_t_info->gmt_offset; + t_info.is_dst = footer_t_info->is_dst; + t_info.abbrev = g_steal_pointer (&footer_t_info->abbrev); + g_array_append_val (gtz->t_info, t_info); + } + + /* Append footer transitions that follow the last explicit + transition. */ + for (index = 0; index < extra_time_count; index++) + { + Transition *footer_transition + = &g_array_index (footertz->transitions, Transition, index); + if (time_count <= 0 + || last_explicit_transition_time < footer_transition->time) + { + Transition trans; + trans.time = footer_transition->time; + trans.info_index = type_count + footer_transition->info_index; + g_array_append_val (gtz->transitions, trans); + } + } + + g_time_zone_unref (footertz); + } } #elif defined (G_OS_WIN32) @@ -616,9 +794,8 @@ static void copy_windows_systemtime (SYSTEMTIME *s_time, TimeZoneDate *tzdate) { - tzdate->sec = s_time->wSecond; - tzdate->min = s_time->wMinute; - tzdate->hour = s_time->wHour; + tzdate->offset + = s_time->wHour * 3600 + s_time->wMinute * 60 + s_time->wSecond; tzdate->mon = s_time->wMonth; tzdate->year = s_time->wYear; tzdate->wday = s_time->wDayOfWeek ? s_time->wDayOfWeek : 7; @@ -633,10 +810,23 @@ } /* 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) { @@ -645,7 +835,6 @@ copy_windows_systemtime (&(tzi->DaylightDate), &(rule->dlt_start)); copy_windows_systemtime (&(tzi->StandardDate), &(rule->dlt_end)); - } else @@ -653,31 +842,41 @@ 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); } @@ -721,18 +920,33 @@ tzi->DaylightBias = reg->DaylightBias; } -static gint -rules_from_windows_time_zone (const gchar *identifier, TimeZoneRule **rules) +static guint +rules_from_windows_time_zone (const gchar *identifier, + gchar **out_identifier, + 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\\"; TIME_ZONE_INFORMATION tzi; DWORD size; - gint rules_num = 0; + guint rules_num = 0; RegTZI regtzi, regtzi_prev; + WCHAR winsyspath[MAX_PATH]; + gunichar2 *subkey_w, *subkey_dynamic_w; + + if (GetSystemDirectoryW (winsyspath, MAX_PATH) == 0) + return 0; + + g_assert (copy_identifier == FALSE || out_identifier != NULL); + g_assert (rules != NULL); + + if (copy_identifier) + *out_identifier = NULL; *rules = NULL; key_name = NULL; @@ -746,92 +960,134 @@ 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); - g_free (key_name); if (*rules) { @@ -841,10 +1097,17 @@ else (*rules)[rules_num - 1].start_year = (*rules)[rules_num - 2].start_year + 1; + if (copy_identifier) + *out_identifier = g_steal_pointer (&key_name); + else + g_free (key_name); + return rules_num; } - else - return 0; + + g_free (key_name); + + return 0; } #endif @@ -852,7 +1115,7 @@ static void find_relative_date (TimeZoneDate *buffer) { - gint wday; + guint wday; GDate date; g_date_clear (&date, 1); wday = buffer->wday; @@ -872,7 +1135,7 @@ else /* M.W.D */ { guint days; - guint days_in_month = g_date_days_in_month (buffer->mon, buffer->year); + guint days_in_month = g_date_get_days_in_month (buffer->mon, buffer->year); GDateWeekday first_wday; g_date_set_dmy (&date, 1, buffer->mon, buffer->year); @@ -919,7 +1182,7 @@ g_date_clear (&date, 1); g_date_set_dmy (&date, buffer.mday, buffer.mon, buffer.year); return ((g_date_get_julian (&date) - unix_epoch_start) * seconds_per_day + - buffer.hour * 3600 + buffer.min * 60 + buffer.sec - offset); + buffer.offset - offset); } static void @@ -945,7 +1208,8 @@ static void init_zone_from_rules (GTimeZone *gtz, TimeZoneRule *rules, - gint rules_num) + guint rules_num, + gchar *identifier /* (transfer full) */) { guint type_count = 0, trans_count = 0, info_index = 0; guint ri; /* rule index */ @@ -970,6 +1234,7 @@ type_count++; } + gtz->name = g_steal_pointer (&identifier); gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), type_count); gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition), trans_count); @@ -1094,7 +1359,7 @@ * - N is 0 to 365 * * time is either h or hh[[:]mm[[[:]ss]]] - * - h[h] is 0 to 23 + * - h[h] is 0 to 24 * - mm is 00 to 59 * - ss is 00 to 59 */ @@ -1141,14 +1406,21 @@ return TRUE; } -/* Different implementations of tzset interpret the Julian day field - differently. For example, Linux specifies that it should be 1-based - (1 Jan is JD 1) for both Jn and n formats, while zOS and BSD - specify that a Jn JD is 1-based while an n JD is 0-based. Rather - than trying to follow different specs, we will follow GDate's - practice thatIn order to keep it simple, we will follow Linux's - practice. */ - +/* + * This parses two slightly different ways of specifying + * the Julian day: + * + * - ignore_leap == TRUE + * + * Jn This specifies the Julian day with n between 1 and 365. Leap days + * are not counted. In this format, February 29 can't be represented; + * February 28 is day 59, and March 1 is always day 60. + * + * - ignore_leap == FALSE + * + * n This specifies the zero-based Julian day with n between 0 and 365. + * February 29 is counted in leap years. + */ static gboolean parse_julian_boundary (gchar** pos, TimeZoneDate *boundary, gboolean ignore_leap) @@ -1162,8 +1434,20 @@ day += *(*pos)++ - '0'; } - if (day < 1 || 365 < day) - return FALSE; + if (ignore_leap) + { + if (day < 1 || 365 < day) + return FALSE; + if (day >= 59) + day++; + } + else + { + if (day < 0 || 365 < day) + return FALSE; + /* GDate wants day in range 1->366 */ + day++; + } g_date_clear (&date, 1); g_date_set_julian (&date, day); @@ -1172,9 +1456,6 @@ boundary->mday = (int) g_date_get_day (&date); boundary->wday = 0; - if (!ignore_leap && day >= 59) - boundary->mday++; - return TRUE; } @@ -1196,13 +1477,13 @@ else if (*pos == 'J') { ++pos; - if (!parse_julian_boundary (&pos, boundary, FALSE)) + if (!parse_julian_boundary (&pos, boundary, TRUE)) return FALSE ; } /* Julian date which counts Feb 29 in leap years */ else if (*pos >= '0' && '9' >= *pos) { - if (!parse_julian_boundary (&pos, boundary, TRUE)) + if (!parse_julian_boundary (&pos, boundary, FALSE)) return FALSE; } else @@ -1211,30 +1492,15 @@ /* Time */ if (*pos == '/') - { - gint32 offset; - - if (!parse_time (++pos, &offset)) - return FALSE; - - boundary->hour = offset / 3600; - boundary->min = (offset / 60) % 60; - boundary->sec = offset % 3600; - - return TRUE; - } - + return parse_constant_offset (pos + 1, &boundary->offset, TRUE); else { - boundary->hour = 2; - boundary->min = 0; - boundary->sec = 0; - + boundary->offset = 2 * 60 * 60; return *pos == '\0'; } } -static gint +static guint create_ruleset_from_rule (TimeZoneRule **rules, TimeZoneRule *rule) { *rules = g_new0 (TimeZoneRule, 2); @@ -1263,7 +1529,7 @@ ++(*pos); buffer = g_strndup (target_pos, *pos - target_pos); - ret = parse_constant_offset (buffer, target); + ret = parse_constant_offset (buffer, target, FALSE); g_free (buffer); return ret; @@ -1288,21 +1554,34 @@ static gboolean set_tz_name (gchar **pos, gchar *buffer, guint size) { + gboolean quoted = **pos == '<'; gchar *name_pos = *pos; guint len; - /* Name is ASCII alpha (Is this necessarily true?) */ - while (g_ascii_isalpha (**pos)) - ++(*pos); + if (quoted) + { + name_pos++; + do + ++(*pos); + while (g_ascii_isalnum (**pos) || **pos == '-' || **pos == '+'); + if (**pos != '>') + return FALSE; + } + else + while (g_ascii_isalpha (**pos)) + ++(*pos); - /* Name should be three or more alphabetic characters */ + /* Name should be three or more characters */ + /* FIXME: Should return FALSE if the name is too long. + This should simplify code later in this function. */ if (*pos - name_pos < 3) return FALSE; - memset (buffer, 0, NAME_SIZE); + memset (buffer, 0, size); /* name_pos isn't 0-terminated, so we have to limit the length expressly */ len = *pos - name_pos > size - 1 ? size - 1 : *pos - name_pos; strncpy (buffer, name_pos, len); + *pos += quoted; return TRUE; } @@ -1326,13 +1605,20 @@ * Creates an array of TimeZoneRule from a TZ environment variable * type of identifier. Should free rules afterwards */ -static gint +static guint rules_from_identifier (const gchar *identifier, + gchar **out_identifier, TimeZoneRule **rules) { gchar *pos; TimeZoneRule tzr; + g_assert (out_identifier != NULL); + g_assert (rules != NULL); + + *out_identifier = NULL; + *rules = NULL; + if (!identifier) return 0; @@ -1344,7 +1630,10 @@ return 0; if (*pos == 0) - return create_ruleset_from_rule (rules, &tzr); + { + *out_identifier = g_strdup (identifier); + return create_ruleset_from_rule (rules, &tzr); + } /* Format 2 */ if (!(set_tz_name (&pos, tzr.dlt_name, NAME_SIZE))) @@ -1362,8 +1651,15 @@ /* Use US rules, Windows' default is Pacific Standard Time */ if ((rules_num = rules_from_windows_time_zone ("Pacific Standard Time", - 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; @@ -1384,9 +1680,40 @@ if (!parse_identifier_boundaries (&pos, &tzr)) return 0; + *out_identifier = g_strdup (identifier); return create_ruleset_from_rule (rules, &tzr); } +#ifdef G_OS_UNIX +static GTimeZone * +parse_footertz (const gchar *footer, size_t footerlen) +{ + gchar *tzstring = g_strndup (footer + 1, footerlen - 2); + GTimeZone *footertz = NULL; + + /* FIXME: it might make sense to modify rules_from_identifier to + allow NULL to be passed instead of &ident, saving the strdup/free + pair. The allocation for tzstring could also be avoided by + passing a gsize identifier_len argument to rules_from_identifier + and changing the code in that function to stop assuming that + identifier is nul-terminated. */ + gchar *ident; + TimeZoneRule *rules; + guint rules_num = rules_from_identifier (tzstring, &ident, &rules); + + g_free (ident); + g_free (tzstring); + if (rules_num > 1) + { + footertz = g_slice_new0 (GTimeZone); + init_zone_from_rules (footertz, rules, rules_num, NULL); + footertz->ref_count++; + } + g_free (rules); + return footertz; +} +#endif + /* Construction {{{1 */ /** * g_time_zone_new: @@ -1466,6 +1793,7 @@ GTimeZone *tz = NULL; TimeZoneRule *rules; gint rules_num; + gchar *resolved_identifier = NULL; G_LOCK (time_zones); if (time_zones == NULL) @@ -1483,42 +1811,42 @@ } tz = g_slice_new0 (GTimeZone); - tz->name = g_strdup (identifier); tz->ref_count = 0; zone_for_constant_offset (tz, identifier); if (tz->t_info == NULL && - (rules_num = rules_from_identifier (identifier, &rules))) + (rules_num = rules_from_identifier (identifier, &resolved_identifier, &rules))) { - init_zone_from_rules (tz, rules, rules_num); + init_zone_from_rules (tz, rules, rules_num, g_steal_pointer (&resolved_identifier)); g_free (rules); } if (tz->t_info == NULL) { #ifdef G_OS_UNIX - GBytes *zoneinfo = zone_info_unix (identifier); - if (!zoneinfo) - zone_for_constant_offset (tz, "UTC"); - else + GBytes *zoneinfo = zone_info_unix (identifier, &resolved_identifier); + if (zoneinfo != NULL) { - init_zone_from_iana_info (tz, zoneinfo); + init_zone_from_iana_info (tz, zoneinfo, g_steal_pointer (&resolved_identifier)); g_bytes_unref (zoneinfo); } #elif defined (G_OS_WIN32) - if ((rules_num = rules_from_windows_time_zone (identifier, &rules))) + if ((rules_num = rules_from_windows_time_zone (identifier, + &resolved_identifier, + &rules, + TRUE))) { - init_zone_from_rules (tz, rules, rules_num); + init_zone_from_rules (tz, rules, rules_num, g_steal_pointer (&resolved_identifier)); g_free (rules); } +#endif } +#if defined (G_OS_WIN32) if (tz->t_info == NULL) { - if (identifier) - zone_for_constant_offset (tz, "UTC"); - else + if (identifier == NULL) { TIME_ZONE_INFORMATION tzi; @@ -1526,21 +1854,31 @@ { 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); + init_zone_from_rules (tz, rules, 2, windows_default_tzname ()); + } g_free (rules); } } -#endif } +#endif + + g_free (resolved_identifier); + + /* Always fall back to UTC. */ + if (tz->t_info == NULL) + zone_for_constant_offset (tz, "UTC"); + + g_assert (tz->name != NULL); + g_assert (tz->t_info != NULL); if (tz->t_info != NULL) { @@ -1597,6 +1935,30 @@ return g_time_zone_new (getenv ("TZ")); } +/* We don't want a ABI change so add here as static. */ +static GTimeZone * +g_time_zone_new_offset (gint32 seconds) +{ + GTimeZone *tz = NULL; + gchar *identifier = NULL; + + /* Seemingly, we should be using @seconds directly to set the + * #TransitionInfo.gmt_offset to avoid all this string building and parsing. + * However, we always need to set the #GTimeZone.name to a constructed + * string anyway, so we might as well reuse its code. */ + identifier = g_strdup_printf ("%c%02u:%02u:%02u", + (seconds >= 0) ? '+' : '-', + (ABS (seconds) / 60) / 60, + (ABS (seconds) / 60) % 60, + ABS (seconds) % 60); + tz = g_time_zone_new (identifier); + g_free (identifier); + + g_assert (g_time_zone_get_offset (tz, 0) == seconds); + + return tz; +} + #define TRANSITION(n) g_array_index (tz->transitions, Transition, n) #define TRANSITION_INFO(n) g_array_index (tz->t_info, TransitionInfo, n) @@ -1647,7 +2009,10 @@ guint interval) { if (tz->transitions && interval < tz->transitions->len) - return (TRANSITION(interval)).time - 1; + { + gint64 lim = (TRANSITION(interval)).time; + return lim - (lim != G_MININT64); + } return G_MAXINT64; } @@ -1739,8 +2104,8 @@ GTimeType type, gint64 *time_) { - gint i; - guint intervals; + guint i, intervals; + gboolean interval_is_dst; if (tz->transitions == NULL) return 0; @@ -1782,16 +2147,21 @@ *time_ = interval_local_start (tz, i); } - else if (interval_isdst (tz, i) != type) - /* it's in this interval, but dst flag doesn't match. - * check neighbours for a better fit. */ + else { - if (i && *time_ <= interval_local_end (tz, i - 1)) - i--; - - else if (i < intervals && - *time_ >= interval_local_start (tz, i + 1)) - i++; + interval_is_dst = interval_isdst (tz, i); + if ((interval_is_dst && type != G_TIME_TYPE_DAYLIGHT) || + (!interval_is_dst && type == G_TIME_TYPE_DAYLIGHT)) + { + /* it's in this interval, but dst flag doesn't match. + * check neighbours for a better fit. */ + if (i && *time_ <= interval_local_end (tz, i - 1)) + i--; + + else if (i < intervals && + *time_ >= interval_local_start (tz, i + 1)) + i++; + } } } @@ -1804,7 +2174,7 @@ * @type: the #GTimeType of @time_ * @time_: a number of seconds since January 1, 1970 * - * Finds an the interval within @tz that corresponds to the given @time_. + * Finds an interval within @tz that corresponds to the given @time_. * The meaning of @time_ depends on @type. * * If @type is %G_TIME_TYPE_UNIVERSAL then this function will always @@ -1832,8 +2202,8 @@ GTimeType type, gint64 time_) { - gint i; - guint intervals; + guint i, intervals; + gboolean interval_is_dst; if (tz->transitions == NULL) return 0; @@ -1857,13 +2227,18 @@ return -1; } - else if (interval_isdst (tz, i) != type) + else { - if (i && time_ <= interval_local_end (tz, i - 1)) - i--; + interval_is_dst = interval_isdst (tz, i); + if ((interval_is_dst && type != G_TIME_TYPE_DAYLIGHT) || + (!interval_is_dst && type == G_TIME_TYPE_DAYLIGHT)) + { + if (i && time_ <= interval_local_end (tz, i - 1)) + i--; - else if (i < intervals && time_ >= interval_local_start (tz, i + 1)) - i++; + else if (i < intervals && time_ >= interval_local_start (tz, i + 1)) + i++; + } } return i; @@ -1946,5 +2321,14 @@ return interval_isdst (tz, (guint)interval); } +/* We don't want a ABI change so add here as static. */ +static const gchar * +g_time_zone_get_identifier (GTimeZone *tz) +{ + g_return_val_if_fail (tz != NULL, NULL); + + return tz->name; +} + /* Epilogue {{{1 */ /* vim:set foldmethod=marker: */ Only in glib-2.54.3.new/glib: gtimezone.c.orig
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor