|
@@ -0,0 +1,458 @@
|
|
|
+Backport a number of patches from upstream to fix reading of the new 'slim'
|
|
|
+encoding for tzdata files.
|
|
|
+
|
|
|
+Upstream-Status: Backport
|
|
|
+Signed-off-by: Ross Burton <ross.burton@arm.com>
|
|
|
+
|
|
|
+commit 18cbd5e5a4812e9bd0b06a058322d2b44ed2ad92
|
|
|
+Author: Paul Eggert <eggert@cs.ucla.edu>
|
|
|
+Date: Thu Jul 16 12:41:49 2020 -0700
|
|
|
+
|
|
|
+ Clarify memset in set_tz_name
|
|
|
+
|
|
|
+ * glib/gtimezone.c (set_tz_name): Use size, not NAME_SIZE,
|
|
|
+ to clear the buffer. Suggested by Philip Withnall in:
|
|
|
+ https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1533#note_867859
|
|
|
+
|
|
|
+commit 1ab3f927d6d09a8cf3349a3545f5351446f43d47
|
|
|
+Author: Paul Eggert <eggert@cs.ucla.edu>
|
|
|
+Date: Thu Jul 16 12:41:49 2020 -0700
|
|
|
+
|
|
|
+ gtimezone: support footers in TZif files
|
|
|
+
|
|
|
+ Since tzcode95f (1995), TZif files have had a trailing
|
|
|
+ TZ string, used for timestamps after the last transition.
|
|
|
+ This string is specified in Internet RFC 8536 section 3.3.
|
|
|
+ init_zone_from_iana_info has ignored this string, causing it
|
|
|
+ to mishandle timestamps past the year 2038. With zic's new -b
|
|
|
+ slim flag, init_zone_from_iana_info would even mishandle current
|
|
|
+ timestamps. Fix this by parsing the trailing TZ string and adding
|
|
|
+ its transitions.
|
|
|
+
|
|
|
+ Closes #2129
|
|
|
+
|
|
|
+commit e8b763e35235a2c6b4bdd48a5099c00f72741059
|
|
|
+Author: Paul Eggert <eggert@cs.ucla.edu>
|
|
|
+Date: Thu Jul 16 12:41:49 2020 -0700
|
|
|
+
|
|
|
+ gtimezone: add support for RFC 8536 time zone transitions
|
|
|
+
|
|
|
+ Time zone transition times can range from -167:59:59 through
|
|
|
+ +167:59:59, according to Internet RFC 8536 section 3.3.1;
|
|
|
+ this is an extension to POSIX. It is needed for proper
|
|
|
+ support of TZif version 3 files.
|
|
|
+
|
|
|
+commit 1c65dd48b8ebd31af8bc9b2263f83c0c411f7519
|
|
|
+Author: Paul Eggert <eggert@cs.ucla.edu>
|
|
|
+Date: Thu Jul 16 12:41:49 2020 -0700
|
|
|
+
|
|
|
+ gtimezone: allow hh to be 24, as per POSIX
|
|
|
+
|
|
|
+ POSIX allows hh to be 24; see
|
|
|
+ https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
|
|
+
|
|
|
+commit 368b65cb4cb17e29a4f55654149f554a14f48bc6
|
|
|
+Author: Paul Eggert <eggert@cs.ucla.edu>
|
|
|
+Date: Thu Jul 16 12:41:49 2020 -0700
|
|
|
+
|
|
|
+ gtimezone: support POSIX 1003.1-2001 quoted TZ abbreviations
|
|
|
+
|
|
|
+ TZ strings like '<-03>3' were introduced in POSIX 1003.1-2001 and
|
|
|
+ are currently specified in:
|
|
|
+ https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
|
|
+
|
|
|
+commit fd528aaab6bb077c6d217e62f2228ec9fe3ed760
|
|
|
+Author: Paul Eggert <eggert@cs.ucla.edu>
|
|
|
+Date: Thu Jul 16 12:41:49 2020 -0700
|
|
|
+
|
|
|
+ gtimezone: get 64-bit data from version-3 TZif files
|
|
|
+
|
|
|
+ Version 3 was introduced in tzdb 2013e (2013).
|
|
|
+ See Internet RFC 8536 section 3.1 under "ver(sion)".
|
|
|
+
|
|
|
+diff --git a/glib/gtimezone.c b/glib/gtimezone.c
|
|
|
+index 5a835dea9..f9eee1967 100644
|
|
|
+--- a/glib/gtimezone.c
|
|
|
++++ b/glib/gtimezone.c
|
|
|
+@@ -142,9 +142,7 @@ typedef struct
|
|
|
+ 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
|
|
|
+@@ -205,6 +203,10 @@ static GTimeZone *tz_local = NULL;
|
|
|
+ there's no point in getting carried
|
|
|
+ away. */
|
|
|
+
|
|
|
++#ifdef G_OS_UNIX
|
|
|
++static GTimeZone *parse_footertz (const gchar *, size_t);
|
|
|
++#endif
|
|
|
++
|
|
|
+ /**
|
|
|
+ * g_time_zone_unref:
|
|
|
+ * @tz: a #GTimeZone
|
|
|
+@@ -286,13 +288,20 @@ g_time_zone_ref (GTimeZone *tz)
|
|
|
+ /* 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;
|
|
|
+@@ -310,7 +319,20 @@ parse_time (const gchar *time_,
|
|
|
+ *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')
|
|
|
+@@ -319,6 +341,8 @@ parse_time (const gchar *time_,
|
|
|
+
|
|
|
+ if (*time_ == ':')
|
|
|
+ time_++;
|
|
|
++ else if (rfc8536)
|
|
|
++ return FALSE;
|
|
|
+
|
|
|
+ if (*time_ < '0' || '5' < *time_)
|
|
|
+ return FALSE;
|
|
|
+@@ -335,6 +359,8 @@ parse_time (const gchar *time_,
|
|
|
+
|
|
|
+ if (*time_ == ':')
|
|
|
+ time_++;
|
|
|
++ else if (rfc8536)
|
|
|
++ return FALSE;
|
|
|
+
|
|
|
+ if (*time_ < '0' || '5' < *time_)
|
|
|
+ return FALSE;
|
|
|
+@@ -351,28 +377,32 @@ parse_time (const gchar *time_,
|
|
|
+
|
|
|
+ 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;
|
|
|
+@@ -391,7 +421,7 @@ zone_for_constant_offset (GTimeZone *gtz, const gchar *name)
|
|
|
+ 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;
|
|
|
+@@ -529,12 +559,17 @@ init_zone_from_iana_info (GTimeZone *gtz,
|
|
|
+ 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')
|
|
|
++ if (header->tzh_version >= '2')
|
|
|
+ {
|
|
|
+ /* Skip ahead to the newer 64-bit data if it's available. */
|
|
|
+ header = (const struct tzhead *)
|
|
|
+@@ -550,6 +585,30 @@ init_zone_from_iana_info (GTimeZone *gtz,
|
|
|
+ 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;
|
|
|
+@@ -557,9 +616,9 @@ init_zone_from_iana_info (GTimeZone *gtz,
|
|
|
+
|
|
|
+ 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++)
|
|
|
+ {
|
|
|
+@@ -574,15 +633,50 @@ init_zone_from_iana_info (GTimeZone *gtz,
|
|
|
+ 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 ((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)
|
|
|
+@@ -590,9 +684,8 @@ init_zone_from_iana_info (GTimeZone *gtz,
|
|
|
+ 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;
|
|
|
+@@ -979,7 +1072,7 @@ boundary_for_year (TimeZoneDate *boundary,
|
|
|
+ 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
|
|
|
+@@ -1156,7 +1249,7 @@ init_zone_from_rules (GTimeZone *gtz,
|
|
|
+ * - 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
|
|
|
+ */
|
|
|
+@@ -1289,25 +1382,10 @@ parse_tz_boundary (const gchar *identifier,
|
|
|
+ /* 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';
|
|
|
+ }
|
|
|
+ }
|
|
|
+@@ -1341,7 +1419,7 @@ parse_offset (gchar **pos, gint32 *target)
|
|
|
+ ++(*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;
|
|
|
+@@ -1366,21 +1444,32 @@ parse_identifier_boundary (gchar **pos, TimeZoneDate *target)
|
|
|
+ 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 */
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1483,6 +1572,28 @@ rules_from_identifier (const gchar *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;
|
|
|
++ 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:
|