mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-04-02 13:53:06 +02:00
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.
This commit is contained in:
parent
325d17c49f
commit
68978631e5
@ -142,9 +142,7 @@ typedef struct
|
|||||||
gint mday;
|
gint mday;
|
||||||
gint wday;
|
gint wday;
|
||||||
gint week;
|
gint week;
|
||||||
gint hour;
|
gint32 offset; /* hour*3600 + min*60 + sec; can be negative. */
|
||||||
gint min;
|
|
||||||
gint sec;
|
|
||||||
} TimeZoneDate;
|
} TimeZoneDate;
|
||||||
|
|
||||||
/* POSIX Timezone abbreviations are typically 3 or 4 characters, but
|
/* POSIX Timezone abbreviations are typically 3 or 4 characters, but
|
||||||
@ -289,10 +287,17 @@ g_time_zone_ref (GTimeZone *tz)
|
|||||||
* - h[h] is 0 to 24
|
* - h[h] is 0 to 24
|
||||||
* - mm is 00 to 59
|
* - mm is 00 to 59
|
||||||
* - ss 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
|
static gboolean
|
||||||
parse_time (const gchar *time_,
|
parse_time (const gchar *time_,
|
||||||
gint32 *offset)
|
gint32 *offset,
|
||||||
|
gboolean rfc8536)
|
||||||
{
|
{
|
||||||
if (*time_ < '0' || '9' < *time_)
|
if (*time_ < '0' || '9' < *time_)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -310,7 +315,20 @@ parse_time (const gchar *time_,
|
|||||||
*offset *= 10;
|
*offset *= 10;
|
||||||
*offset += 60 * 60 * (*time_++ - '0');
|
*offset += 60 * 60 * (*time_++ - '0');
|
||||||
|
|
||||||
if (*offset > 24 * 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;
|
return FALSE;
|
||||||
|
|
||||||
if (*time_ == '\0')
|
if (*time_ == '\0')
|
||||||
@ -319,6 +337,8 @@ parse_time (const gchar *time_,
|
|||||||
|
|
||||||
if (*time_ == ':')
|
if (*time_ == ':')
|
||||||
time_++;
|
time_++;
|
||||||
|
else if (rfc8536)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
if (*time_ < '0' || '5' < *time_)
|
if (*time_ < '0' || '5' < *time_)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -335,6 +355,8 @@ parse_time (const gchar *time_,
|
|||||||
|
|
||||||
if (*time_ == ':')
|
if (*time_ == ':')
|
||||||
time_++;
|
time_++;
|
||||||
|
else if (rfc8536)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
if (*time_ < '0' || '5' < *time_)
|
if (*time_ < '0' || '5' < *time_)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -351,28 +373,32 @@ parse_time (const gchar *time_,
|
|||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
parse_constant_offset (const gchar *name,
|
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;
|
*offset = 0;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*name >= '0' && '9' >= *name)
|
if (*name >= '0' && '9' >= *name)
|
||||||
return parse_time (name, offset);
|
return parse_time (name, offset, rfc8536);
|
||||||
|
|
||||||
switch (*name++)
|
switch (*name++)
|
||||||
{
|
{
|
||||||
case 'Z':
|
case 'Z':
|
||||||
*offset = 0;
|
*offset = 0;
|
||||||
return !*name;
|
/* Internet RFC 8536 section 3.3.1 requires a numeric zone. */
|
||||||
|
return !rfc8536 && !*name;
|
||||||
|
|
||||||
case '+':
|
case '+':
|
||||||
return parse_time (name, offset);
|
return parse_time (name, offset, rfc8536);
|
||||||
|
|
||||||
case '-':
|
case '-':
|
||||||
if (parse_time (name, offset))
|
if (parse_time (name, offset, rfc8536))
|
||||||
{
|
{
|
||||||
*offset = -*offset;
|
*offset = -*offset;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -391,7 +417,7 @@ zone_for_constant_offset (GTimeZone *gtz, const gchar *name)
|
|||||||
gint32 offset;
|
gint32 offset;
|
||||||
TransitionInfo info;
|
TransitionInfo info;
|
||||||
|
|
||||||
if (name == NULL || !parse_constant_offset (name, &offset))
|
if (name == NULL || !parse_constant_offset (name, &offset, FALSE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
info.gmt_offset = offset;
|
info.gmt_offset = offset;
|
||||||
@ -590,9 +616,8 @@ init_zone_from_iana_info (GTimeZone *gtz,
|
|||||||
static void
|
static void
|
||||||
copy_windows_systemtime (SYSTEMTIME *s_time, TimeZoneDate *tzdate)
|
copy_windows_systemtime (SYSTEMTIME *s_time, TimeZoneDate *tzdate)
|
||||||
{
|
{
|
||||||
tzdate->sec = s_time->wSecond;
|
tzdate->offset
|
||||||
tzdate->min = s_time->wMinute;
|
= s_time->wHour * 3600 + s_time->wMinute * 60 + s_time->wSecond;
|
||||||
tzdate->hour = s_time->wHour;
|
|
||||||
tzdate->mon = s_time->wMonth;
|
tzdate->mon = s_time->wMonth;
|
||||||
tzdate->year = s_time->wYear;
|
tzdate->year = s_time->wYear;
|
||||||
tzdate->wday = s_time->wDayOfWeek ? s_time->wDayOfWeek : 7;
|
tzdate->wday = s_time->wDayOfWeek ? s_time->wDayOfWeek : 7;
|
||||||
@ -979,7 +1004,7 @@ boundary_for_year (TimeZoneDate *boundary,
|
|||||||
g_date_clear (&date, 1);
|
g_date_clear (&date, 1);
|
||||||
g_date_set_dmy (&date, buffer.mday, buffer.mon, buffer.year);
|
g_date_set_dmy (&date, buffer.mday, buffer.mon, buffer.year);
|
||||||
return ((g_date_get_julian (&date) - unix_epoch_start) * seconds_per_day +
|
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
|
static void
|
||||||
@ -1289,25 +1314,10 @@ parse_tz_boundary (const gchar *identifier,
|
|||||||
/* Time */
|
/* Time */
|
||||||
|
|
||||||
if (*pos == '/')
|
if (*pos == '/')
|
||||||
{
|
return parse_constant_offset (pos + 1, &boundary->offset, TRUE);
|
||||||
gint32 offset;
|
|
||||||
|
|
||||||
if (!parse_time (++pos, &offset))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
boundary->hour = offset / 3600;
|
|
||||||
boundary->min = (offset / 60) % 60;
|
|
||||||
boundary->sec = offset % 3600;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
boundary->hour = 2;
|
boundary->offset = 2 * 60 * 60;
|
||||||
boundary->min = 0;
|
|
||||||
boundary->sec = 0;
|
|
||||||
|
|
||||||
return *pos == '\0';
|
return *pos == '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1341,7 +1351,7 @@ parse_offset (gchar **pos, gint32 *target)
|
|||||||
++(*pos);
|
++(*pos);
|
||||||
|
|
||||||
buffer = g_strndup (target_pos, *pos - 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);
|
g_free (buffer);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user