Skip to content

Commit ee55296

Browse files
committed
Test fix for GH #23878
1 parent b2ea40d commit ee55296

File tree

6 files changed

+82
-45
lines changed

6 files changed

+82
-45
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: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,7 @@ Returns the string.
18721872
Synopsis:
18731873

18741874
strftime(fmt, sec, min, hour, mday, mon, year,
1875-
wday = -1, yday = -1, isdst = 0)
1875+
wday = -1, yday = -1, isdst = -1)
18761876

18771877
The month (C<mon>) begins at zero,
18781878
I<e.g.>, January is 0, not 1. The
@@ -1883,8 +1883,24 @@ about these and the other arguments.
18831883
The C<wday> and C<yday> parameters are both ignored. Their values are
18841884
always determinable from the other parameters.
18851885

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.
1886+
C<isdst> should be
1887+
1888+
=over
1889+
1890+
=item positive
1891+
1892+
to indicate that daylight savings time is in effect. Some systems treat
1893+
this as a hint rather than a mandate.
1894+
1895+
=item zero
1896+
1897+
to indicate that daylight savings time is not in effect.
1898+
1899+
=item negative
1900+
1901+
to ask the function to determine if daylight savings time is in effect.
1902+
1903+
=back
18881904

18891905
If you want your code to be portable, your format (C<fmt>) argument
18901906
should use only the conversion specifiers defined by the ANSI C
@@ -1899,17 +1915,20 @@ The C<Z> specifier is notoriously unportable since the names of
18991915
timezones are non-standard. Sticking to the numeric specifiers is the
19001916
safest route.
19011917

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.
1918+
1919+
The arguments are made consistent as though by calling C<mktime()>
1920+
before calling your system's C<strftime()> function.
19061921

19071922
The string for Tuesday, December 12, 1995 in the C<C> locale.
19081923

19091924
$str = POSIX::strftime( "%A, %B %d, %Y",
19101925
0, 0, 0, 12, 11, 95, 2 );
19111926
print "$str\n";
19121927

1928+
Note that older libc versions of this function had a hard time
1929+
determining if daylight savings time is in effect. That should no
1930+
longer be an issue.
1931+
19131932
=item C<strlen>
19141933

19151934
Not implemented. C<strlen()> is C-specific, use C<length()> instead, see L<perlfunc/length>.

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: 25 additions & 1 deletion
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.
@@ -226,3 +226,27 @@ SKIP: {
226226
is(strftime(undef, CORE::localtime), '', "strftime() works if format is undef");
227227
like($warnings, qr/^Use of uninitialized value in subroutine entry /, "strftime(undef, ...) produces expected warning");
228228
}
229+
230+
SKIP: { # GH #23878
231+
skip "No mktime()", 9 if $Config{d_mktime} ne 'define';
232+
$ENV{TZ}="Europe/Paris";
233+
POSIX::tzset();
234+
skip "Doesn't recognize Europe/Paris timezone", 9
235+
unless grep { m/CET/ } POSIX::tzname;
236+
my $b = 1761436800; # an hour before time should have changed
237+
my @data = (
238+
[ -1, "2025-10-26 01:59:59+0200", "-1 second" ],
239+
[ 0, "2025-10-26 02:00:00+0200", "+0 seconds" ],
240+
[ 1, "2025-10-26 02:00:01+0200", "+1 second" ],
241+
[ 3599, "2025-10-26 02:59:59+0200", "+59 minutes 59 seconds" ],
242+
[ 3600, "2025-10-26 02:00:00+0100", "+1 hour" ],
243+
[ 3601, "2025-10-26 02:00:01+0100", "+1 hour 1 second" ],
244+
[ 7199, "2025-10-26 02:59:59+0100", "+2 hours minus 1 sec" ],
245+
[ 7200, "2025-10-26 03:00:00+0100", "+2 hours" ],
246+
[ 7201, "2025-10-26 03:00:01+0100", "+2 hours 1 second" ],
247+
);
248+
for (my $i = 0; $i < @data; $i++) {
249+
is(POSIX::strftime("%F %T%z", localtime $b + $data[$i][0]),
250+
$data[$i][1], $data[$i][2]);
251+
}
252+
}

locale.c

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8190,17 +8190,16 @@ The value of C<isdst> is used as follows:
81908190
81918191
=item 0
81928192
8193-
No daylight savings time is in effect
8193+
The caller knows that daylight savings time is not in effect
81948194
81958195
=item E<gt>0
81968196
8197-
Check if daylight savings time is in effect, and adjust the results
8198-
accordingly.
8197+
The caller knows that daylight savings time is in effect, though the
8198+
underlying libc function may choose to override this.
81998199
82008200
=item E<lt>0
82018201
8202-
This value is reserved for internal use by the L<POSIX> module for backwards
8203-
compatibility purposes.
8202+
The function is to calculate itself if daylight savings time is in effect.
82048203
82058204
=back
82068205
@@ -8269,15 +8268,8 @@ Perl_sv_strftime_ints(pTHX_ SV * fmt, int sec, int min, int hour,
82698268
const char * locale = "C";
82708269
#endif
82718270

8272-
/* A negative 'isdst' triggers backwards compatibility mode for
8273-
* POSIX::strftime(), in which 0 is always passed to ints_to_tm() so that
8274-
* the possibility of daylight savings time is never considered, But, a 1
8275-
* is eventually passed to libc strftime() so that it returns the results
8276-
* it always has for a non-zero 'isdst'. See GH #22351 */
82778271
struct tm mytm;
8278-
ints_to_tm(&mytm, locale, sec, min, hour, mday, mon, year,
8279-
MAX(0, isdst));
8280-
mytm.tm_isdst = MIN(1, abs(isdst));
8272+
ints_to_tm(&mytm, locale, sec, min, hour, mday, mon, year, isdst);
82818273
return sv_strftime_common(fmt, locale, &mytm);
82828274
}
82838275

@@ -8372,9 +8364,6 @@ S_ints_to_tm(pTHX_ struct tm * mytm,
83728364
/* On platforms that have either of these two fields, we have to run the
83738365
* libc mktime() in order to set them, as mini_mktime() doesn't deal with
83748366
* them. [perl #18238] */
8375-
# if defined(HAS_TM_TM_GMTOFF) || defined(HAS_TM_TM_ZONE)
8376-
# define ALWAYS_RUN_MKTIME
8377-
# endif
83788367

83798368
/* When isdst is 0, it means to consider daylight savings time as never
83808369
* being in effect. Many libc implementations of mktime() do not allow
@@ -8383,30 +8372,33 @@ S_ints_to_tm(pTHX_ struct tm * mytm,
83838372
if (isdst == 0) {
83848373
mini_mktime(mytm);
83858374

8386-
# ifndef ALWAYS_RUN_MKTIME
8387-
8388-
/* When we don't always need libc mktime(), we call it only when we didn't
8389-
* call mini_mktime() */
8390-
} else {
8375+
# if defined(HAS_TM_TM_GMTOFF) || defined(HAS_TM_TM_ZONE)
83918376

8392-
# else
83938377
/* Here will have to run libc mktime() in order to get the values of
83948378
* some fields that mini_mktime doesn't populate. We don't want
8395-
* mktime's side effect of looking for dst, so we have to have a
8396-
* separate tm structure from which we copy just those fields into the
8397-
* returned structure. Initialize its values. mytm should now be a
8398-
* normalized version of the input. */
8379+
* mktime's side effect of looking for dst (because isdst==0), so we
8380+
* have to have a separate tm structure from which we copy just those
8381+
* fields into the structure we return. Initialize its values, which
8382+
* have now been normalized by mini_mktime. */
83998383
aux_tm = *mytm;
8400-
aux_tm.tm_isdst = isdst;
84018384
which_tm = &aux_tm;
8385+
}
8386+
8387+
# else
8388+
8389+
}
8390+
else /* Don't run libc mktime if both: we ran mini_mktime above, and we
8391+
don't have to always run libc mktime */
84028392

84038393
# endif
84048394

8395+
{
84058396
/* Here, we need to run libc mktime(), either because we want to take
84068397
* dst into consideration, or because it calculates one or two fields
8407-
* that we need that mini_mktime() doesn't handle.
8408-
*
8409-
* Unlike mini_mktime(), it does consider the locale, so have to switch
8398+
* that we need that mini_mktime() doesn't handle. */
8399+
which_tm->tm_isdst = isdst;
8400+
8401+
/* Unlike mini_mktime(), it does consider the locale, so have to switch
84108402
* to the correct one. */
84118403
const char * orig_TIME_locale = toggle_locale_c(LC_TIME, locale);
84128404
MKTIME_LOCK;
@@ -8436,7 +8428,6 @@ S_ints_to_tm(pTHX_ struct tm * mytm,
84368428
}
84378429

84388430
# endif
8439-
# undef ALWAYS_RUN_MKTIME
84408431
#endif
84418432

84428433
return;

0 commit comments

Comments
 (0)