@@ -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,16 @@ SKIP: {
205205 is(mktime(CORE::localtime ($time )), $time , " mktime()" );
206206 is(mktime(POSIX::localtime ($time )), $time , " mktime()" );
207207}
208+
209+ # What these are hasn't been fully investigated; None have %s. At least MS
210+ # isn't documented to have %l, %E, %O either.
211+ my $limited_strftime_formats = $^O eq " VMS"
212+ || $^O eq " MSWin32"
213+ || $^O eq " os390" ;
208214
209215SKIP: {
210- skip " '%s ' not implemented in strftime" , 1 if $^O eq " VMS"
211- || $^O eq " MSWin32"
212- || $^O eq " os390" ;
216+ skip " '%s ' not implemented in strftime" , 1 if $limited_strftime_formats ;
217+
213218 # Somewhat arbitrarily, put in 60 seconds of slack; if this fails, it
214219 # will likely be off by 1 hour
215220 ok(abs(POSIX::strftime(' %s' , localtime ) - time ) < 60,
@@ -226,3 +231,50 @@ SKIP: {
226231 is(strftime(undef , CORE::localtime ), ' ' , " strftime() works if format is undef" );
227232 like($warnings , qr / ^Use of uninitialized value in subroutine entry / , " strftime(undef, ...) produces expected warning" );
228233}
234+
235+ SKIP: { # GH #23878
236+ my $skip_count = 9;
237+ skip " No mktime()" , $skip_count if $Config {d_mktime } ne ' define' ;
238+ my $locale = " Europe/Paris" ;
239+ $ENV {TZ } = $locale ;
240+ my $t = 1761436800; # an hour before time should have changed
241+
242+ # The time in the first test case is such that UTC gives a different day.
243+ # If the locale above is unknown, libc is supposed to use UTC; so that's
244+ # how we check if the system knows about the rules for Paris; which on
245+ # some systems differ from plain CET-1CEST.
246+ skip " '$locale ' not understood" , $skip_count if
247+ POSIX::strftime(" %F %T%z " , localtime ($t - 1)) !~ / 2025-10-26/ ;
248+
249+ # On Winddows, the documentation says that it won't understand what our
250+ # $locale is set to, but instead of falling back to UTC, it uses the
251+ # system timezone, or U.S. Pacific time. The trick above therefore
252+ # doesn't work, so just skip this batch of tests.
253+ skip " '$locale ' not understood" , $skip_count if $^O eq " MSWin32" ;
254+
255+ my ($azone , $bzone ); # Timezone offset before and after dst change
256+
257+ # If this platform doesn't have this field, the offset will be zero
258+ if (0 && ! $Config {d_tm_tm_gmtoff }) {
259+ $azone = $bzone = " 0000" ;
260+ }
261+ else {
262+ $bzone = " 0200" ;
263+ $azone = " 0100" ;
264+ }
265+ my @data = (
266+ [ -1, " 2025-10-26 01:59:59+$bzone " , " -1 second" ],
267+ [ 0, " 2025-10-26 02:00:00+$bzone " , " +0 seconds" ],
268+ [ 1, " 2025-10-26 02:00:01+$bzone " , " +1 second" ],
269+ [ 3599, " 2025-10-26 02:59:59+$bzone " , " +59 minutes 59 seconds" ],
270+ [ 3600, " 2025-10-26 02:00:00+$azone " , " +1 hour" ],
271+ [ 3601, " 2025-10-26 02:00:01+$azone " , " +1 hour 1 second" ],
272+ [ 7199, " 2025-10-26 02:59:59+$azone " , " +2 hours minus 1 sec" ],
273+ [ 7200, " 2025-10-26 03:00:00+$azone " , " +2 hours" ],
274+ [ 7201, " 2025-10-26 03:00:01+$azone " , " +2 hours 1 second" ],
275+ );
276+ for (my $i = 0; $i < @data ; $i ++) {
277+ is(POSIX::strftime(" %F %T%z " , localtime $t + $data [$i ][0]),
278+ $data [$i ][1], $data [$i ][2]);
279+ }
280+ }
0 commit comments