From 072679cae9cf3a6bc40d3b5f3793f64a2851256a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Tue, 28 Apr 2026 06:16:22 -0600 Subject: [PATCH] fix: strftime() sets epoch index for %G and %s format codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strftime() never assigned $me->[9] (the epoch timestamp), so format codes that read it — %G (GPS week) and %s (epoch seconds) — produced wrong results. %G returned -522 instead of the correct GPS week number because the uninitialized value was treated as zero. Set $me->[9] in both the timezone and non-timezone code paths, matching what time2str() already does. Co-Authored-By: Claude Opus 4.6 --- lib/Date/Format/Generic.pm | 4 +++- t/strftime-epoch.t | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 t/strftime-epoch.t diff --git a/lib/Date/Format/Generic.pm b/lib/Date/Format/Generic.pm index c719ee9..d740214 100644 --- a/lib/Date/Format/Generic.pm +++ b/lib/Date/Format/Generic.pm @@ -56,11 +56,13 @@ sub strftime $epoch = timelocal(@{$time}[0..5]); @$me = gmtime($epoch + tz_offset($tzname)); + $me->[9] = $epoch; } else { @$me = @$time; - undef $epoch; + $epoch = timelocal(@$me[0..5]); + $me->[9] = $epoch; } _subs($me,$fmt); diff --git a/t/strftime-epoch.t b/t/strftime-epoch.t new file mode 100644 index 0000000..cb61ba6 --- /dev/null +++ b/t/strftime-epoch.t @@ -0,0 +1,48 @@ +use strict; +use warnings; +use Test::More; +use Date::Format qw(time2str strftime); +use Time::Local qw(timegm); + +# strftime() was missing $me->[9] (epoch timestamp), causing format codes +# that depend on it (%G, %s) to produce wrong results. + +my $t = timegm(0, 0, 12, 28, 3, 124); # 2024-04-28 12:00:00 UTC +my @gmt = gmtime($t); + +# %G (GPS week) - was returning -522 instead of 2312 +is(time2str("%G", $t, "UTC"), "2312", + "time2str %G gives correct GPS week"); +is(strftime("%G", @gmt, "UTC"), "2312", + "strftime %G with timezone gives correct GPS week"); + +# %s (epoch seconds) - strftime with localtime should match time2str +my @lt = localtime($t); +is(time2str("%s", $t), strftime("%s", @lt), + "strftime %s (localtime, no tz) matches time2str %s"); + +# GPS epoch boundary via strftime +my $gps_epoch = 315964800; +my @gps_gmt = gmtime($gps_epoch); +is(strftime("%G", @gps_gmt, "UTC"), "0", + "strftime %G at GPS epoch is week 0"); + +my @week1_gmt = gmtime($gps_epoch + 7 * 86400); +is(strftime("%G", @week1_gmt, "UTC"), "1", + "strftime %G one week after GPS epoch is week 1"); + +# Verify strftime and time2str agree on %G for several dates +for my $pair ( + [timegm(0, 0, 0, 6, 0, 80), "0", "GPS epoch"], + [timegm(0, 0, 0, 13, 0, 80), "1", "start of week 1"], + [936709362, "1026", "Sep 7, 1999"], +) { + my ($epoch, $expected, $label) = @$pair; + my @g = gmtime($epoch); + is(strftime("%G", @g, "UTC"), $expected, + "strftime %G: $label"); + is(time2str("%G", $epoch, "UTC"), $expected, + "time2str %G: $label (cross-check)"); +} + +done_testing;