From 1f36e6ac4217107b65a625bc73f84c74ea3eabde Mon Sep 17 00:00:00 2001 From: Vineeth Sai Date: Sat, 11 Jul 2026 10:41:10 -0700 Subject: [PATCH] Fix get_timezone_gmt hour for negative fractional offsets For a timezone whose UTC offset has a non-zero minute part and is negative (for example America/St_Johns at UTC-03:30), get_timezone_gmt rendered the hour one step too far from zero, producing 'GMT-04:30' instead of 'GMT-03:30'. divmod() on a negative number floors the quotient and returns a non-negative remainder, so divmod(-12600, 3600) is (-4, 1800): the code printed the floored hour -4 while taking the minutes from the positive remainder. Decompose the offset from its absolute value and track the sign separately. For every offset that already formatted correctly the output is unchanged, because '%s%02d' with the explicit sign matches '%+03d' whenever 0 <= hours < 100. --- babel/dates.py | 13 +++++++------ tests/test_dates.py | 7 +++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/babel/dates.py b/babel/dates.py index 69610a7f0..27b4eba08 100644 --- a/babel/dates.py +++ b/babel/dates.py @@ -461,18 +461,19 @@ def get_timezone_gmt( offset = datetime.tzinfo.utcoffset(datetime) seconds = offset.days * 24 * 60 * 60 + offset.seconds - hours, seconds = divmod(seconds, 3600) + sign = '-' if seconds < 0 else '+' + hours, seconds = divmod(abs(seconds), 3600) if return_z and hours == 0 and seconds == 0: return 'Z' elif seconds == 0 and width == 'iso8601_short': - return '%+03d' % hours + return '%s%02d' % (sign, hours) elif width == 'short' or width == 'iso8601_short': - pattern = '%+03d%02d' + pattern = '%s%02d%02d' elif width == 'iso8601': - pattern = '%+03d:%02d' + pattern = '%s%02d:%02d' else: - pattern = locale.zone_formats['gmt'] % '%+03d:%02d' - return pattern % (hours, seconds // 60) + pattern = locale.zone_formats['gmt'] % '%s%02d:%02d' + return pattern % (sign, hours, seconds // 60) def get_timezone_location( diff --git a/tests/test_dates.py b/tests/test_dates.py index 12bb23433..0ba18115a 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -455,6 +455,13 @@ def test_get_timezone_gmt(timezone_getter): assert dates.get_timezone_gmt(dt, 'short', locale='en') == '-0700' assert dates.get_timezone_gmt(dt, locale='en', width='iso8601_short') == '-07' assert dates.get_timezone_gmt(dt, 'long', locale='fr_FR') == 'UTC-07:00' + # A negative offset with a non-zero minute part must keep the correct hour + # (Newfoundland Standard Time is UTC-03:30, not UTC-04:30). + tz = timezone_getter('America/St_Johns') + dt = _localize(tz, datetime(2007, 1, 1, 12, 0)) + assert dates.get_timezone_gmt(dt, locale='en') == 'GMT-03:30' + assert dates.get_timezone_gmt(dt, 'short', locale='en') == '-0330' + assert dates.get_timezone_gmt(dt, locale='en', width='iso8601') == '-03:30' def test_get_timezone_location(timezone_getter):