mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-06-02 02:40:07 +02:00
gtimezone: fix parsing of Julian day in POSIX TZ format
The timezone(3) man page on Fedora 31 describes the start/end field in the POSIX TZ format as follows: [quote] The start field specifies when daylight saving time goes into effect and the end field specifies when the change is made back to standard time. These fields may have the fol‐ lowing formats: 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. n This specifies the zero-based Julian day with n between 0 and 365. February 29 is counted in leap years. Mm.w.d This specifies day d (0 <= d <= 6) of week w (1 <= w <= 5) of month m (1 <= m <= 12). Week 1 is the first week in which day d occurs and week 5 is the last week in which day d occurs. Day 0 is a Sunday. [/quote] The GTimeZone code does not correctly parse the 'n' syntax, treating it as having the range 1-365, the same as the 'Jn' syntax. This is semantically broken as it makes it impossible to represent the 366th day, which is the purpose of the 'n' syntax. There is a code comment saying this was done because the Linux semantics are different from zOS and BSD. This is not correct, as GLibC does indeed use the same 0-365 range as other operating systems. It is believed that the original author was mislead by a bug in old versions of the Linux libc timezone(3) man pages which was fixed in commit 5a554f8e525faa98354c1b95bfe4aca7125a3657 Author: Peter Schiffer <pschiffe@redhat.com> Date: Sat Mar 24 16:08:10 2012 +1300 tzset.3: Correct description for Julian 'n' date format The Julian 'n' date format counts atrting from 0, not 1. Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com> Fixes: #1999 Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
6cd263f655
commit
3c0685ec4a
@ -1203,14 +1203,21 @@ parse_mwd_boundary (gchar **pos, TimeZoneDate *boundary)
|
||||
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)
|
||||
@ -1224,8 +1231,20 @@ parse_julian_boundary (gchar** pos, TimeZoneDate *boundary,
|
||||
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);
|
||||
@ -1234,9 +1253,6 @@ parse_julian_boundary (gchar** pos, TimeZoneDate *boundary,
|
||||
boundary->mday = (int) g_date_get_day (&date);
|
||||
boundary->wday = 0;
|
||||
|
||||
if (ignore_leap && day >= 59)
|
||||
boundary->mday++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -2432,8 +2432,8 @@ test_posix_parse (void)
|
||||
g_date_time_unref (gdt2);
|
||||
g_time_zone_unref (tz);
|
||||
|
||||
tz = g_time_zone_new ("NZST-12:00:00NZDT-13:00:00,280,77");
|
||||
g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "NZST-12:00:00NZDT-13:00:00,280,77");
|
||||
tz = g_time_zone_new ("NZST-12:00:00NZDT-13:00:00,279,76");
|
||||
g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "NZST-12:00:00NZDT-13:00:00,279,76");
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "NZST");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, 12 * 3600);
|
||||
g_assert (!g_time_zone_is_dst (tz, 0));
|
||||
@ -2533,6 +2533,48 @@ test_posix_parse (void)
|
||||
g_date_time_unref (gdt1);
|
||||
g_date_time_unref (gdt2);
|
||||
g_time_zone_unref (tz);
|
||||
|
||||
tz = g_time_zone_new ("VIR-00:30");
|
||||
g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-00:30");
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (30 * 60));
|
||||
g_assert_false (g_time_zone_is_dst (tz, 0));
|
||||
|
||||
tz = g_time_zone_new ("VIR-00:30VID,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-00:30VID,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (30 * 60));
|
||||
g_assert_false (g_time_zone_is_dst (tz, 0));
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (90 * 60));
|
||||
g_assert_true (g_time_zone_is_dst (tz, 1));
|
||||
|
||||
tz = g_time_zone_new ("VIR-02:30VID,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-02:30VID,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (150 * 60));
|
||||
g_assert_false (g_time_zone_is_dst (tz, 0));
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (210 * 60));
|
||||
g_assert_true (g_time_zone_is_dst (tz, 1));
|
||||
|
||||
tz = g_time_zone_new ("VIR-02:30VID-04:30,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-02:30VID-04:30,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (150 * 60));
|
||||
g_assert_false (g_time_zone_is_dst (tz, 0));
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (270 * 60));
|
||||
g_assert_true (g_time_zone_is_dst (tz, 1));
|
||||
|
||||
tz = g_time_zone_new ("VIR-12:00VID-13:00,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-12:00VID-13:00,0/00:00:00,365/23:59:59");
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (720 * 60));
|
||||
g_assert_false (g_time_zone_is_dst (tz, 0));
|
||||
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
|
||||
g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (780 * 60));
|
||||
g_assert_true (g_time_zone_is_dst (tz, 1));
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
x
Reference in New Issue
Block a user