mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-23 20:46:14 +01:00
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
This commit is contained in:
parent
68978631e5
commit
25d950b61f
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user