gtimezone: Fix assertion failure when called with a huge offset

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 <pwithnall@endlessos.org>

Fixes: #2620
This commit is contained in:
Philip Withnall 2022-03-16 12:47:03 +00:00
parent b86750cb63
commit 7ab9359af0
2 changed files with 45 additions and 19 deletions

View File

@ -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 * This is equivalent to calling g_time_zone_new() with a string in the form
* `[+|-]hh[:mm[:ss]]`. * `[+|-]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 * Since: 2.58
*/ */
GTimeZone * GTimeZone *
@ -2048,11 +2054,15 @@ g_time_zone_new_offset (gint32 seconds)
(ABS (seconds) / 60) % 60, (ABS (seconds) / 60) % 60,
ABS (seconds) % 60); ABS (seconds) % 60);
tz = g_time_zone_new_identifier (identifier); 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_assert (tz != NULL);
g_free (identifier); g_free (identifier);
g_assert (g_time_zone_get_offset (tz, 0) == seconds);
return tz; return tz;
} }

View File

@ -2940,19 +2940,26 @@ test_identifier (void)
static void static void
test_new_offset (void) test_new_offset (void)
{ {
const gint32 vectors[] = const struct
{ {
-10000, gint32 offset;
-3600, gboolean expected_success;
-61, }
-60, vectors[] =
-59, {
0, { -158400, FALSE },
59, { -10000, TRUE },
60, { -3600, TRUE },
61, { -61, TRUE },
3600, { -60, TRUE },
10000, { -59, TRUE },
{ 0, TRUE },
{ 59, TRUE },
{ 60, TRUE },
{ 61, TRUE },
{ 3600, TRUE },
{ 10000, TRUE },
{ 158400, FALSE },
}; };
gsize i; gsize i;
@ -2960,12 +2967,21 @@ test_new_offset (void)
{ {
GTimeZone *tz = NULL; 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_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); g_time_zone_unref (tz);
} }
} }