Skip to content

Commit 3dfffd4

Browse files
committed
Test fix for GH #23878
1 parent 4eadf9a commit 3dfffd4

File tree

4 files changed

+64
-39
lines changed

4 files changed

+64
-39
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.pod

Lines changed: 17 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,16 @@ 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 to indicate that daylight savings time is in effect.
1891+
1892+
=item zero to indicate that daylight savings time is not in effect.
1893+
1894+
=item negative to ask the function to determine if daylight savings time
1895+
is in effect.
18881896

18891897
If you want your code to be portable, your format (C<fmt>) argument
18901898
should use only the conversion specifiers defined by the ANSI C
@@ -1899,17 +1907,19 @@ The C<Z> specifier is notoriously unportable since the names of
18991907
timezones are non-standard. Sticking to the numeric specifiers is the
19001908
safest route.
19011909

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

19071913
The string for Tuesday, December 12, 1995 in the C<C> locale.
19081914

19091915
$str = POSIX::strftime( "%A, %B %d, %Y",
19101916
0, 0, 0, 12, 11, 95, 2 );
19111917
print "$str\n";
19121918

1919+
Note that older libc versions of this function had a hard time
1920+
determining if daylight savings time is in effect. That should no
1921+
longer be an issue.
1922+
19131923
=item C<strlen>
19141924

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

ext/POSIX/t/time.t

Lines changed: 22 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,24 @@ 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+
{ # GH #23878
231+
skip "No mktime()", 8 if $Config{d_mktime} ne 'define';
232+
$ENV{TZ}="Europe/Paris";
233+
my $b = 1761436800; # an hour before time should have changed
234+
my @data = (
235+
[ -1, "2025-10-26 01:59:59+0200", "-1 second" ],
236+
[ 0, "2025-10-26 02:00:00+0200", "+0 seconds" ],
237+
[ 1, "2025-10-26 02:00:01+0200", "+1 second" ],
238+
[ 3599, "2025-10-26 02:59:59+0200", "+59 minutes 59 seconds" ],
239+
[ 3600, "2025-10-26 02:00:00+0100", "+1 hour" ],
240+
[ 3601, "2025-10-26 02:00:01+0100", "+1 hour 1 second" ],
241+
[ 7199, "2025-10-26 02:59:59+0100", "+2 hours minus 1 sec" ],
242+
[ 7200, "2025-10-26 03:00:00+0100", "+2 hours" ],
243+
[ 7201, "2025-10-26 03:00:01+0100", "+2 hours 1 second" ],
244+
);
245+
for (my $i = 0; $i < @data; $i++) {
246+
is(POSIX::strftime("%F %T%z", localtime $b + $data[$i][0]),
247+
$data[$i][1], $data[$i][2]);
248+
}
249+
}

locale.c

Lines changed: 23 additions & 27 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

@@ -8383,30 +8375,34 @@ S_ints_to_tm(pTHX_ struct tm * mytm,
83838375
if (isdst == 0) {
83848376
mini_mktime(mytm);
83858377

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 {
8378+
# ifdef ALWAYS_RUN_MKTIME
83918379

8392-
# else
83938380
/* Here will have to run libc mktime() in order to get the values of
83948381
* 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. */
8382+
* mktime's side effect of looking for dst (because isdst==0), so we
8383+
* have to have a separate tm structure from which we copy just those
8384+
* fields into the structure we return. Initialize its values, which
8385+
* have now been normalized by mini_mktime. */
83998386
aux_tm = *mytm;
8400-
aux_tm.tm_isdst = isdst;
84018387
which_tm = &aux_tm;
84028388

84038389
# endif
84048390

8391+
}
8392+
8393+
# ifndef ALWAYS_RUN_MKTIME
8394+
8395+
else /* Don't run libc mktime if both: we ran mini_mktime above, and we
8396+
don't have to always run libc mktime */
8397+
8398+
# endif
8399+
8400+
{
84058401
/* Here, we need to run libc mktime(), either because we want to take
84068402
* 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
8403+
* that we need that mini_mktime() doesn't handle. */
8404+
8405+
/* Unlike mini_mktime(), it does consider the locale, so have to switch
84108406
* to the correct one. */
84118407
const char * orig_TIME_locale = toggle_locale_c(LC_TIME, locale);
84128408
MKTIME_LOCK;

0 commit comments

Comments
 (0)