From 7ab9359af08fff76b7a35bd14808a2e734a93f90 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Wed, 16 Mar 2022 12:47:03 +0000 Subject: [PATCH] gtimezone: Fix assertion failure when called with a huge offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This looks like a regression from commit 3356934db5, but prior to that commit there was always an assertion failure when calling `g_time_zone_new_offset()` with an offset which is too large (such as 44 hours), ever since the function was added in commit cf24867b93. It would be ideal if we could return a `NULL` timezone to indicate the error, but that’s not part of the API for `g_time_zone_new_offset()`, so we have to go with the dated and not-ideal approach of returning the UTC timezone and letting the caller figure it out. Another potential approach would be to reduce the `offset` modulo 24 hours. This makes the error less easily detectable than if returning UTC, though, and still returns an invalid result: `+44:00` is not the same timezone as `+20:00` (it’s one day further ahead). Add a unit test. Signed-off-by: Philip Withnall Fixes: #2620 --- glib/gtimezone.c | 16 +++++++++++--- glib/tests/gdatetime.c | 48 ++++++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/glib/gtimezone.c b/glib/gtimezone.c index a22f70eff..a37dbe2e1 100644 --- a/glib/gtimezone.c +++ b/glib/gtimezone.c @@ -2028,7 +2028,13 @@ g_time_zone_new_local (void) * This is equivalent to calling g_time_zone_new() with a string in the form * `[+|-]hh[:mm[:ss]]`. * - * Returns: (transfer full): a timezone at the given offset from UTC + * It is possible for this function to fail if @seconds is too big (greater than + * 24 hours), in which case this function will return the UTC timezone for + * backwards compatibility. To detect failures like this, use + * g_time_zone_new_identifier() directly. + * + * Returns: (transfer full): a timezone at the given offset from UTC, or UTC on + * failure * Since: 2.58 */ GTimeZone * @@ -2048,11 +2054,15 @@ g_time_zone_new_offset (gint32 seconds) (ABS (seconds) / 60) % 60, ABS (seconds) % 60); tz = g_time_zone_new_identifier (identifier); + + if (tz == NULL) + tz = g_time_zone_new_utc (); + else + g_assert (g_time_zone_get_offset (tz, 0) == seconds); + g_assert (tz != NULL); g_free (identifier); - g_assert (g_time_zone_get_offset (tz, 0) == seconds); - return tz; } diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c index d2d1ec1a6..2853b1174 100644 --- a/glib/tests/gdatetime.c +++ b/glib/tests/gdatetime.c @@ -2940,19 +2940,26 @@ test_identifier (void) static void test_new_offset (void) { - const gint32 vectors[] = + const struct { - -10000, - -3600, - -61, - -60, - -59, - 0, - 59, - 60, - 61, - 3600, - 10000, + gint32 offset; + gboolean expected_success; + } + vectors[] = + { + { -158400, FALSE }, + { -10000, TRUE }, + { -3600, TRUE }, + { -61, TRUE }, + { -60, TRUE }, + { -59, TRUE }, + { 0, TRUE }, + { 59, TRUE }, + { 60, TRUE }, + { 61, TRUE }, + { 3600, TRUE }, + { 10000, TRUE }, + { 158400, FALSE }, }; gsize i; @@ -2960,12 +2967,21 @@ test_new_offset (void) { GTimeZone *tz = NULL; - g_test_message ("Vector %" G_GSIZE_FORMAT ": %d", i, vectors[i]); + g_test_message ("Vector %" G_GSIZE_FORMAT ": %d", i, vectors[i].offset); - tz = g_time_zone_new_offset (vectors[i]); + tz = g_time_zone_new_offset (vectors[i].offset); g_assert_nonnull (tz); - g_assert_cmpstr (g_time_zone_get_identifier (tz), !=, "UTC"); - g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, vectors[i]); + + if (vectors[i].expected_success) + { + g_assert_cmpstr (g_time_zone_get_identifier (tz), !=, "UTC"); + g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, vectors[i].offset); + } + else + { + g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC"); + } + g_time_zone_unref (tz); } }