@@ -9,7 +9,7 @@ use strict;
99
1010use Config;
1111use POSIX;
12- use Test::More tests => 31 ;
12+ use Test::More tests => 40 ;
1313
1414# For the first go to UTC to avoid DST issues around the world when testing. SUS3 says that
1515# null should get you UTC, but some environments want the explicit names.
@@ -205,11 +205,14 @@ SKIP: {
205205 is(mktime(CORE::localtime ($time )), $time , " mktime()" );
206206 is(mktime(POSIX::localtime ($time )), $time , " mktime()" );
207207}
208+
209+ my $limited_strftime_formats = $^O eq " VMS"
210+ || $^O eq " MSWin32"
211+ || $^O eq " os390" ;
208212
209213SKIP: {
210- skip " '%s ' not implemented in strftime" , 1 if $^O eq " VMS"
211- || $^O eq " MSWin32"
212- || $^O eq " os390" ;
214+ skip " '%s ' not implemented in strftime" , 1 if $limited_strftime_formats ;
215+
213216 # Somewhat arbitrarily, put in 60 seconds of slack; if this fails, it
214217 # will likely be off by 1 hour
215218 ok(abs(POSIX::strftime(' %s' , localtime ) - time ) < 60,
@@ -226,3 +229,50 @@ SKIP: {
226229 is(strftime(undef , CORE::localtime ), ' ' , " strftime() works if format is undef" );
227230 like($warnings , qr / ^Use of uninitialized value in subroutine entry / , " strftime(undef, ...) produces expected warning" );
228231}
232+
233+ SKIP: { # GH #23878
234+ my $skip_count = 9;
235+ skip " No mktime()" , $skip_count if $Config {d_mktime } ne ' define' ;
236+ my $locale = " Europe/Paris" ;
237+ $ENV {TZ } = $locale ;
238+ my $t = 1761436800; # an hour before time should have changed
239+
240+ # The time in the first test case is such that UTC gives a different day.
241+ # If the locale above is unknown, libc is supposed to use UTC; so that's
242+ # how we check if the system knows about the rules for Paris; which on
243+ # some systems differ from plain CET-1CEST.
244+ skip " '$locale ' not understood" , $skip_count if
245+ POSIX::strftime(" %F %T%z " , localtime ($t - 1)) !~ / 2025-10-26/ ;
246+
247+ # On Winddows, the documentation says that it won't understand what our
248+ # $locale is set to, but instead of falling back to UTC, it uses the
249+ # system timezone, or U.S. Pacific time. The trick above therefore
250+ # doesn't work, so just skip this batch of tests.
251+ skip " '$locale ' not understood" , $skip_count if $^O eq " MSWin32" ;
252+
253+ my ($azone , $bzone ); # Timezone offset before and after dst change
254+
255+ # If this platform doesn't have this field, the offset will be zero
256+ if (! $Config {d_tm_tm_gmtoff }) {
257+ $azone = $bzone = " 0000" ;
258+ }
259+ else {
260+ $bzone = " 0200" ;
261+ $azone = " 0100" ;
262+ }
263+ my @data = (
264+ [ -1, " 2025-10-26 01:59:59+$bzone " , " -1 second" ],
265+ [ 0, " 2025-10-26 02:00:00+$bzone " , " +0 seconds" ],
266+ [ 1, " 2025-10-26 02:00:01+$bzone " , " +1 second" ],
267+ [ 3599, " 2025-10-26 02:59:59+$bzone " , " +59 minutes 59 seconds" ],
268+ [ 3600, " 2025-10-26 02:00:00+$azone " , " +1 hour" ],
269+ [ 3601, " 2025-10-26 02:00:01+$azone " , " +1 hour 1 second" ],
270+ [ 7199, " 2025-10-26 02:59:59+$azone " , " +2 hours minus 1 sec" ],
271+ [ 7200, " 2025-10-26 03:00:00+$azone " , " +2 hours" ],
272+ [ 7201, " 2025-10-26 03:00:01+$azone " , " +2 hours 1 second" ],
273+ );
274+ for (my $i = 0; $i < @data ; $i ++) {
275+ is(POSIX::strftime(" %F %T%z " , localtime $t + $data [$i ][0]),
276+ $data [$i ][1], $data [$i ][2]);
277+ }
278+ }
0 commit comments