Skip to content

Commit 26269a0

Browse files
committed
Test fix for GH #23878
1 parent c7edb9f commit 26269a0

File tree

6 files changed

+206
-112
lines changed

6 files changed

+206
-112
lines changed

ext/POSIX/POSIX.xs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3589,7 +3589,7 @@ difftime(time1, time2)
35893589
# sv_setpv(TARG, ...) could be used rather than
35903590
# ST(0) = sv_2mortal(newSVpv(...))
35913591
void
3592-
strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = 0)
3592+
strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
35933593
SV * fmt
35943594
int sec
35953595
int min
@@ -3605,10 +3605,8 @@ strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = 0)
36053605
PERL_UNUSED_ARG(wday);
36063606
PERL_UNUSED_ARG(yday);
36073607

3608-
/* -isdst triggers backwards compatibility mode for non-zero
3609-
* 'isdst' */
36103608
SV *sv = sv_strftime_ints(fmt, sec, min, hour, mday, mon, year,
3611-
-abs(isdst));
3609+
isdst);
36123610
if (sv) {
36133611
sv = sv_2mortal(sv);
36143612
}

ext/POSIX/lib/POSIX.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use warnings;
44

55
our ($AUTOLOAD, %SIGRT);
66

7-
our $VERSION = '2.24';
7+
our $VERSION = '2.25';
88

99
require XSLoader;
1010

ext/POSIX/lib/POSIX.pod

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,49 +1866,17 @@ Identical to the string form of C<$!>, see L<perlvar/$ERRNO>.
18661866
=item C<strftime>
18671867

18681868
Convert date and time information to string based on the current
1869-
underlying locale of the program (except for any daylight savings time).
1870-
Returns the string.
1869+
underlying locale of the program.
1870+
Returns the string in a mortalized SV; set to an empty string on error.
18711871

1872-
Synopsis:
1873-
1874-
strftime(fmt, sec, min, hour, mday, mon, year,
1875-
wday = -1, yday = -1, isdst = 0)
1876-
1877-
The month (C<mon>) begins at zero,
1878-
I<e.g.>, January is 0, not 1. The
1879-
year (C<year>) is given in years since 1900, I<e.g.>, the year 1995 is 95; the
1880-
year 2001 is 101. Consult your system's C<strftime()> manpage for details
1881-
about these and the other arguments.
1872+
my $sv = strftime(fmt, sec, min, hour, mday, mon, year,
1873+
wday = -1, yday = -1, isdst = -1)
18821874

18831875
The C<wday> and C<yday> parameters are both ignored. Their values are
18841876
always determinable from the other parameters.
18851877

1886-
C<isdst> should be C<1> or C<0>, depending on whether or not daylight
1887-
savings time is in effect for the given time or not.
1888-
1889-
If you want your code to be portable, your format (C<fmt>) argument
1890-
should use only the conversion specifiers defined by the ANSI C
1891-
standard (C99, to play safe). These are C<aAbBcdHIjmMpSUwWxXyYZ%>.
1892-
But even then, the B<results> of some of the conversion specifiers are
1893-
non-portable. For example, the specifiers C<aAbBcpZ> change according
1894-
to the locale settings of the user, and both how to set locales (the
1895-
locale names) and what output to expect are non-standard.
1896-
The specifier C<c> changes according to the timezone settings of the
1897-
user and the timezone computation rules of the operating system.
1898-
The C<Z> specifier is notoriously unportable since the names of
1899-
timezones are non-standard. Sticking to the numeric specifiers is the
1900-
safest route.
1901-
1902-
The arguments, except for C<isdst>, are made consistent as though by
1903-
calling C<mktime()> before calling your system's C<strftime()> function.
1904-
To get correct results, you must set C<isdst> to be the proper value.
1905-
When omitted, the function assumes daylight savings is not in effect.
1906-
1907-
The string for Tuesday, December 12, 1995 in the C<C> locale.
1908-
1909-
$str = POSIX::strftime( "%A, %B %d, %Y",
1910-
0, 0, 0, 12, 11, 95, 2 );
1911-
print "$str\n";
1878+
More details on the behavior and the specification of the other
1879+
parameters are described in L<perlapi/sv_strftime_ints>.
19121880

19131881
=item C<strlen>
19141882

ext/POSIX/t/posix.t

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,12 @@ print POSIX::strftime("ok $test # %H:%M, on %m/%d/%y\n", localtime());
273273
# input fields to strftime().
274274
sub try_strftime {
275275
my $expect = shift;
276-
my $got = POSIX::strftime("%a %b %d %H:%M:%S %Y %j", @_);
276+
my @input = @_;
277+
278+
# Add zeros to missing parameters. The final 0 is for isdst, and the zero
279+
# forces use of mini_mktime (unless the code changes).
280+
push @input, 0 while @input < 9;
281+
my $got = POSIX::strftime("%a %b %d %H:%M:%S %Y %j", @input);
277282
is($got, $expect, "validating mini_mktime() and strftime(): $expect");
278283
}
279284

ext/POSIX/t/time.t

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use strict;
99

1010
use Config;
1111
use 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

209213
SKIP: {
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

Comments
 (0)