From 2bdf366e820066124545b54ef126fca7e9c4c597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Thu, 7 May 2026 07:21:24 -0600 Subject: [PATCH] fix: format_z loses sign for negative sub-hour timezone offsets int($o / 3600) truncates -1800 to 0, so sprintf("%+03d", 0) produces "+00" instead of "-00". Affects any negative offset under one hour (e.g. -0030, -0015, -0045). Fix: extract sign from original offset before taking abs(), then format with explicit sign character. Co-Authored-By: Claude Opus 4.6 --- lib/Date/Format/Generic.pm | 4 +++- t/format-z-sign.t | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 t/format-z-sign.t diff --git a/lib/Date/Format/Generic.pm b/lib/Date/Format/Generic.pm index b3b8bdf..1f7c496 100644 --- a/lib/Date/Format/Generic.pm +++ b/lib/Date/Format/Generic.pm @@ -207,7 +207,9 @@ sub format_Z { sub format_z { my $t = $_[0]->[9]; my $o = defined $tzname ? tz_offset($tzname, $t) : tz_offset(undef,$t); - sprintf("%+03d%02d", int($o / 3600), int(abs($o) % 3600) / 60); + my $sign = $o < 0 ? '-' : '+'; + my $abs = abs($o); + sprintf("%s%02d%02d", $sign, int($abs / 3600), int($abs % 3600) / 60); } sub format_c { &format_x . " " . &format_X } diff --git a/t/format-z-sign.t b/t/format-z-sign.t new file mode 100644 index 0000000..9c4b6ac --- /dev/null +++ b/t/format-z-sign.t @@ -0,0 +1,22 @@ +use strict; +use warnings; +use Test::More tests => 6; +use Date::Format qw(time2str); + +# Regression: format_z lost the sign for negative sub-hour timezone offsets +# because int(-1800/3600) truncates to 0, and sprintf("%+03d",...) renders +# 0 as "+00" instead of "-00". + +my $epoch = 946684800; # 2000-01-01 00:00:00 UTC + +# Negative sub-hour offsets (the bug) +is(time2str("%z", $epoch, "-0030"), "-0030", "%z preserves sign for -0030"); +is(time2str("%z", $epoch, "-0015"), "-0015", "%z preserves sign for -0015"); +is(time2str("%z", $epoch, "-0045"), "-0045", "%z preserves sign for -0045"); + +# Positive sub-hour offsets (control) +is(time2str("%z", $epoch, "+0030"), "+0030", "%z correct for +0030"); +is(time2str("%z", $epoch, "+0545"), "+0545", "%z correct for +0545 (Nepal)"); + +# Zero offset +is(time2str("%z", $epoch, "+0000"), "+0000", "%z correct for UTC");