176 lines
6.4 KiB
Diff
176 lines
6.4 KiB
Diff
From 25d950b61f92f25cc9ab20d683aa4d6969f93098 Mon Sep 17 00:00:00 2001
|
|
From: Paul Eggert <eggert@cs.ucla.edu>
|
|
Date: Thu, 16 Jul 2020 12:41:49 -0700
|
|
Subject: [PATCH 0734/1095] 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
|
|
|
|
reason:support footers in TZif files
|
|
|
|
Conflict:NA
|
|
Reference:https://github.com/GNOME/glib/commit/25d950b61f92f25cc9ab20d683aa4d6969f93098
|
|
---
|
|
glib/gtimezone.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
|
1 file changed, 93 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/glib/gtimezone.c b/glib/gtimezone.c
|
|
index 80f3882..fcf1057 100644
|
|
--- a/glib/gtimezone.c
|
|
+++ b/glib/gtimezone.c
|
|
@@ -203,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
|
|
@@ -555,7 +559,12 @@ 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);
|
|
@@ -576,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;
|
|
@@ -583,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++)
|
|
{
|
|
@@ -604,11 +637,46 @@ init_zone_from_iana_info (GTimeZone *gtz,
|
|
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)
|
|
@@ -1504,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:
|
|
--
|
|
1.8.3.1
|
|
|