Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

timezone and gmt offset as output by POSIX::strftime() are sometimes wrong #6059

Closed
p5pRT opened this issue Nov 5, 2002 · 10 comments
Closed

Comments

@p5pRT
Copy link

p5pRT commented Nov 5, 2002

Migrated from rt.perl.org#18238 (status was 'resolved')

Searchable as RT18238$

@p5pRT
Copy link
Author

p5pRT commented Nov 5, 2002

From atatat@atatdot.net

while perl does reorganize the members of the "struct tm" that gets
passed to strftime() so that they're roughly internally coherent, the
dance that is does with init_tm() and mini_mktime() leave the timezone
strings and gmt offset wrong for times outside of the current daylight
setting (eg, for a time six months from now).

noting that perl doesn't exactly perform the same way as the
strftime() function from the c library, i won't include any of the
test output i did that involved struct tm members being out of the
stated domain (perl's behavior there is different from libc's, yet i
suppose that that's perfectly acceptable since i can't complain that
"malformed input" should give the same output).

correct output (from date)​:

% sh -c 'for t in 1000 900 800 700 600; do \
  date -r $(($t*1000000)) +"%c %z %Z"; done'
Sat Sep 8 21​:46​:40 2001 -0400 EDT
Thu Jul 9 12​:00​:00 1998 -0400 EDT
Tue May 9 02​:13​:20 1995 -0400 EDT
Sat Mar 7 15​:26​:40 1992 -0500 EST
Thu Jan 5 05​:40​:00 1989 -0500 EST

incorrect output (from perl)​:

% perl -MPOSIX -e 'foreach $t (1000,900,800,700,600) { \
  @​tm=localtime($t*1000000);print(strftime("%c %z %Z%n",@​tm));}'
Sat Sep 8 21​:46​:40 2001 -0400 EDT
Thu Jul 9 12​:00​:00 1998 -0400 EDT
Tue May 9 02​:13​:20 1995 -0400 EDT
Sat Mar 7 15​:26​:40 1992 -0400 EDT <- wrong tz, gmtoff
Thu Jan 5 05​:40​:00 1989 -0400 EDT <- wrong tz, gmtoff

corrected output (from perl)​:

Sat Sep 8 21​:46​:40 2001 -0400 EDT
Thu Jul 9 12​:00​:00 1998 -0400 EDT
Tue May 9 02​:13​:20 1995 -0400 EDT
Sat Mar 7 15​:26​:40 1992 -0500 EST <- now correct
Thu Jan 5 05​:40​:00 1989 -0500 EST <- now correct

the patch below makes it work, but you might not like it. :)

-------8<-------8<-------8<-------8<-------8<-------8<-------

Inline Patch
--- ext/POSIX/POSIX.xs.orig	Fri Apr  6 00:38:46 2001
+++ ext/POSIX/POSIX.xs	Mon Oct 28 12:12:02 2002
@@ -3858,6 +3858,16 @@
 	    mytm.tm_yday = yday;
 	    mytm.tm_isdst = isdst;
 	    mini_mktime(&mytm);
+	    /* use libc to normalize the thing -- @@@ */
+	    {
+		    struct tm mytm2;
+		    time_t t;
+		    mytm2 = mytm;
+		    t = mktime(&mytm2);
+		    mytm2 = *localtime(&t);
+		    mytm.tm_gmtoff = mytm2.tm_gmtoff;
+		    mytm.tm_zone = mytm2.tm_zone;
+	    }
 	    len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
 	    /*
 	    ** The following is needed to handle to the situation where 
-------8<-------8<-------8<-------8<-------8<-------8<-------
Perl Info

Flags:
    category=core
    severity=high

Site configuration information for perl v5.6.1:

Configured by andrew at Sun Mar  3 03:02:51 EST 2002.

Summary of my perl5 (revision 5.0 version 6 subversion 1) configuration:
  Platform:
    osname=netbsd, osvers=1.5za, archname=i386-netbsd
    uname='netbsd this 1.5za netbsd 1.5za (that) #79: sun dec 30 17:10:07 est 2001 andrew@this:usrsrcsysarchi386compilethat i386 '
    config_args='-sde -Dprefix=/usr/pkg -Dscriptdir=/usr/pkg/bin -Darchname=i386-netbsd -Doptimize=-O2 -Dcc=cc -Uusemymalloc -Uinstallusrbinperl -Dlibswanted=m crypt -Duseshrplib'
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef
    useperlio=undef d_sfio=undef uselargefiles=define usesocks=undef
    use64bitint=undef use64bitall=undef uselongdouble=undef
  Compiler:
    cc='cc', ccflags ='-fno-strict-aliasing -I/usr/pkg/include',
    optimize='-O2',
    cppflags='-fno-strict-aliasing -I/usr/pkg/include'
    ccversion='', gccversion='2.95.3 20010315 (release) (NetBSD nb1)', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=4, usemymalloc=n, prototype=define
  Linker and Libraries:
    ld='cc', ldflags ='-Wl,-R/usr/pkg/lib  -L/usr/pkg/lib'
    libpth=/usr/pkg/lib /usr/lib
    libs=-lm -lcrypt
    perllibs=-lm -lcrypt
    libc=/usr/lib/libc.so, so=so, useshrplib=true, libperl=libperl.so
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-whole-archive -lgcc -Wl,-no-whole-archive 			-Wl,-E -Wl,-R/usr/pkg/lib  -Wl,-R/usr/pkg/lib/perl5/5.6.1/i386-netbsd/CORE'
    cccdlflags='-DPIC -fPIC ', lddlflags='--whole-archive -shared  -Wl,-R/usr/pkg/lib -L/usr/pkg/lib'

Locally applied patches:
    


@INC for perl v5.6.1:
    /usr/pkg/lib/perl5/site_perl/5.6.1/i386-netbsd
    /usr/pkg/lib/perl5/site_perl/5.6.1
    /usr/pkg/lib/perl5/site_perl
    /usr/pkg/lib/perl5/5.6.1/i386-netbsd
    /usr/pkg/lib/perl5/5.6.1
    .


Environment for perl v5.6.1:
    HOME=/home/andrew
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/andrew/bin:/usr/pkg/bin:/usr/pkg/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/X11R6/bin:/usr/lib:/usr/games
    PERL_BADLANG (unset)
    SHELL=/usr/pkg/bin/tcsh

@p5pRT
Copy link
Author

p5pRT commented Nov 8, 2002

From @eserte

"atatat@​atatdot.net (via RT)" <perlbug@​perl.org> writes​:

# New Ticket Created by atatat@​atatdot.net
# Please include the string​: [perl #18238]
# in the subject line of all future correspondence about this issue.
# <URL​: http​://rt.perl.org/rt2/Ticket/Display.html?id=18238 >

This is a bug report for perl from atatat@​atatdot.net.
generated with the help of perlbug 1.33 running under perl v5.6.1.

-----------------------------------------------------------------
[Please enter your report here]

while perl does reorganize the members of the "struct tm" that gets
passed to strftime() so that they're roughly internally coherent, the
dance that is does with init_tm() and mini_mktime() leave the timezone
strings and gmt offset wrong for times outside of the current daylight
setting (eg, for a time six months from now).

noting that perl doesn't exactly perform the same way as the
strftime() function from the c library, i won't include any of the
test output i did that involved struct tm members being out of the
stated domain (perl's behavior there is different from libc's, yet i
suppose that that's perfectly acceptable since i can't complain that
"malformed input" should give the same output).

correct output (from date)​:

% sh -c 'for t in 1000 900 800 700 600; do \
date -r $(($t*1000000)) +"%c %z %Z"; done'
Sat Sep 8 21​:46​:40 2001 -0400 EDT
Thu Jul 9 12​:00​:00 1998 -0400 EDT
Tue May 9 02​:13​:20 1995 -0400 EDT
Sat Mar 7 15​:26​:40 1992 -0500 EST
Thu Jan 5 05​:40​:00 1989 -0500 EST

incorrect output (from perl)​:

% perl -MPOSIX -e 'foreach $t (1000,900,800,700,600) { \
@​tm=localtime($t*1000000);print(strftime("%c %z %Z%n",@​tm));}'
Sat Sep 8 21​:46​:40 2001 -0400 EDT
Thu Jul 9 12​:00​:00 1998 -0400 EDT
Tue May 9 02​:13​:20 1995 -0400 EDT
Sat Mar 7 15​:26​:40 1992 -0400 EDT <- wrong tz, gmtoff
Thu Jan 5 05​:40​:00 1989 -0400 EDT <- wrong tz, gmtoff

corrected output (from perl)​:

Sat Sep 8 21​:46​:40 2001 -0400 EDT
Thu Jul 9 12​:00​:00 1998 -0400 EDT
Tue May 9 02​:13​:20 1995 -0400 EDT
Sat Mar 7 15​:26​:40 1992 -0500 EST <- now correct
Thu Jan 5 05​:40​:00 1989 -0500 EST <- now correct

Here's (nearly) the same patch which works with bleadperl --- the
strftime code moved from POSIX.xs to util.c in perl 5.8.0, so the
original patch did not apply. Also included a test which should at
least work on Linux and FreeBSD.

#
#
# To apply this patch​:
# STEP 1​: Chdir to the source directory.
# STEP 2​: Run the 'applypatch' program with this patch file as input.
#
# If you do not have 'applypatch', it is part of the 'makepatch' package
# that you can fetch from the Comprehensive Perl Archive Network​:
# http​://www.perl.com/CPAN/authors/Johan_Vromans/makepatch-x.y.tar.gz
# In the above URL, 'x' should be 2 or higher.
#
# To apply this patch without the use of 'applypatch'​:
# STEP 1​: Chdir to the source directory.
# STEP 2​: Run the 'patch' program with this file as input.
#
#### End of Preamble ####

#### Patch data follows ####
diff -up 'bleedperl2/ext/POSIX/t/posix.t' 'bleedperl3/ext/POSIX/t/posix.t'
Index​: ./ext/POSIX/t/posix.t

Inline Patch
--- ./ext/POSIX/t/posix.t	Thu Nov  7 10:37:39 2002
+++ ./ext/POSIX/t/posix.t	Fri Nov  8 15:43:04 2002
@@ -11,7 +11,7 @@ BEGIN {
 }
 
 require "./test.pl";
-plan(tests => 61);
+plan(tests => 66);
 
 
 use POSIX qw(fcntl_h signal_h limits_h _exit getcwd open read strftime write
@@ -182,6 +182,25 @@ try_strftime("Tue Feb 29 00:00:00 2000 0
 try_strftime("Wed Mar 01 00:00:00 2000 061", 0,0,0, 1,2,100);
 try_strftime("Fri Mar 31 00:00:00 2000 091", 0,0,0, 31,2,100);
 &POSIX::setlocale(&POSIX::LC_TIME, $lc) if $Config{d_setlocale};
+
+SKIP: {
+    # XXX wait for smokers to see which OSs to skip
+
+    local $ENV{TZ} = "Europe/Berlin";
+
+    # May fail for ancient FreeBSD versions.
+    # %z is not included in POSIX, but valid on Linux and FreeBSD.
+    foreach $def ([1000,'Sun Sep  9 03:46:40 2001 +0200 CEST'],
+		  [900, 'Thu Jul  9 18:00:00 1998 +0200 CEST'],
+		  [800, 'Tue May  9 08:13:20 1995 +0200 CEST'],
+		  [700, 'Sat Mar  7 21:26:40 1992 +0100 CET'],
+		  [600, 'Thu Jan  5 11:40:00 1989 +0100 CET'],
+		 ) {
+	my($t, $expected) = @$def;
+        my @tm = localtime($t*1000000);
+	is(strftime("%c %z %Z",@tm), $expected, "validating zone setting: $expected");
+    }
+}
 
 {
     for my $test (0, 1) {
diff -up 'bleedperl2/util.c' 'bleedperl3/util.c'
Index: ./util.c
Inline Patch
--- ./util.c	Thu Nov  7 10:43:01 2002
+++ ./util.c	Fri Nov  8 15:46:35 2002
@@ -3383,6 +3383,16 @@ Perl_my_strftime(pTHX_ char *fmt, int se
   mytm.tm_yday = yday;
   mytm.tm_isdst = isdst;
   mini_mktime(&mytm);
+  /* use libc to get the values for tm_gmtoff and tm_zone [perl #18238] */
+#ifdef HAS_MKTIME
+  STMT_START {
+    struct tm mytm2;
+    mytm2 = mytm;
+    mktime(&mytm2);
+    mytm.tm_gmtoff = mytm2.tm_gmtoff;
+    mytm.tm_zone = mytm2.tm_zone;
+  } STMT_END;
+#endif
   buflen = 64;
   New(0, buf, buflen, char);
   len = strftime(buf, buflen, fmt, &mytm);
#### End of Patch data ####

#### ApplyPatch data follows ####
# Data version : 1.0
# Date generated : Fri Nov 8 18​:30​:29 2002
# Generated by : makepatch 2.00_05
# Recurse directories : Yes
# v 'patchlevel.h' 3261 1036661713 33056
# p 'ext/POSIX/t/posix.t' 7830 1036766584 0100775
# p 'util.c' 100968 1036766795 0100660
#### End of ApplyPatch data ####

#### End of Patch kit [created​: Fri Nov 8 18​:30​:29 2002] ####
#### Patch checksum​: 74 2429 41781 ####
#### Checksum​: 92 3053 27778 ####

Regards,
  Slaven

--
Slaven Rezic - slaven.rezic@​berlin.de

  tkrevdiff - graphical display of diffs between revisions (RCS or CVS)
  http​://ptktools.sourceforge.net/#tkrevdiff

@p5pRT
Copy link
Author

p5pRT commented Nov 8, 2002

From @doughera88

On 8 Nov 2002, Slaven Rezic wrote​:

while perl does reorganize the members of the "struct tm" that gets
passed to strftime() so that they're roughly internally coherent, the
dance that is does with init_tm() and mini_mktime() leave the timezone
strings and gmt offset wrong for times outside of the current daylight
setting (eg, for a time six months from now).

Here's (nearly) the same patch which works with bleadperl --- the
strftime code moved from POSIX.xs to util.c in perl 5.8.0, so the
original patch did not apply. Also included a test which should at
least work on Linux and FreeBSD.

+ /* use libc to get the values for tm_gmtoff and tm_zone [perl #18238] */
+#ifdef HAS_MKTIME
+ STMT_START {
+ struct tm mytm2;
+ mytm2 = mytm;
+ mktime(&mytm2);
+ mytm.tm_gmtoff = mytm2.tm_gmtoff;
+ mytm.tm_zone = mytm2.tm_zone;
+ } STMT_END;
+#endif

Alas that's not portable. The reason perl doesn't muck with the
....tm_gmtoff and .tm_zone fields is that they aren't part of POSIX. They
are add-ons supplied by some vendors and not by others.

Fortunately, Configure tests for this. The appropriate %Config variable
is d_tm_tm_zone, and the corresponding config.h #define is HAS_TM_TM_ZONE.

If you make the util.c code (and the tests!) conditional on those defines,
you'll probably stand a good chance of being portable.

--
  Andy Dougherty doughera@​lafayette.edu

@p5pRT
Copy link
Author

p5pRT commented Nov 8, 2002

From @eserte

Andy Dougherty <doughera@​lafayette.edu> writes​:

On 8 Nov 2002, Slaven Rezic wrote​:

while perl does reorganize the members of the "struct tm" that gets
passed to strftime() so that they're roughly internally coherent, the
dance that is does with init_tm() and mini_mktime() leave the timezone
strings and gmt offset wrong for times outside of the current daylight
setting (eg, for a time six months from now).

Here's (nearly) the same patch which works with bleadperl --- the
strftime code moved from POSIX.xs to util.c in perl 5.8.0, so the
original patch did not apply. Also included a test which should at
least work on Linux and FreeBSD.

+ /* use libc to get the values for tm_gmtoff and tm_zone [perl #18238] */
+#ifdef HAS_MKTIME
+ STMT_START {
+ struct tm mytm2;
+ mytm2 = mytm;
+ mktime(&mytm2);
+ mytm.tm_gmtoff = mytm2.tm_gmtoff;
+ mytm.tm_zone = mytm2.tm_zone;
+ } STMT_END;
+#endif

Alas that's not portable. The reason perl doesn't muck with the
.tm_gmtoff and .tm_zone fields is that they aren't part of POSIX. They
are add-ons supplied by some vendors and not by others.

Fortunately, Configure tests for this. The appropriate %Config variable
is d_tm_tm_zone, and the corresponding config.h #define is HAS_TM_TM_ZONE.

If you make the util.c code (and the tests!) conditional on those defines,
you'll probably stand a good chance of being portable.

OK, now with HAS_TM_TM_ZONE and a new HAS_TM_TM_GMTOFF test. But I
still think that test failures are possible (because of different
formats for the TZ environent variable or different timezone
databases).

Regards,
  Slaven

#
#
# To apply this patch​:
# STEP 1​: Chdir to the source directory.
# STEP 2​: Run the 'applypatch' program with this patch file as input.
#
# If you do not have 'applypatch', it is part of the 'makepatch' package
# that you can fetch from the Comprehensive Perl Archive Network​:
# http​://www.perl.com/CPAN/authors/Johan_Vromans/makepatch-x.y.tar.gz
# In the above URL, 'x' should be 2 or higher.
#
# To apply this patch without the use of 'applypatch'​:
# STEP 1​: Chdir to the source directory.
# STEP 2​: Run the 'patch' program with this file as input.
#
#### End of Preamble ####

#### Patch data follows ####
diff -up 'bleedperl2/config_h.SH' 'bleedperl3/config_h.SH'
Index​: ./config_h.SH
Prereq​: 3.0.1.5

Inline Patch
--- ./config_h.SH	Thu Nov  7 10:36:12 2002
+++ ./config_h.SH	Fri Nov  8 21:43:50 2002
@@ -2994,10 +2994,15 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#und
  *	This symbol, if defined, indicates to the C program that
  *	the struct tm has a tm_zone field.
  */
+/* HAS_TM_TM_GMTOFF:
+ *	This symbol, if defined, indicates to the C program that
+ *	the struct tm has a tm_gmtoff field.
+ */
 #$i_time I_TIME		/**/
 #$i_systime I_SYS_TIME		/**/
 #$i_systimek I_SYS_TIME_KERNEL		/**/
 #$d_tm_tm_zone HAS_TM_TM_ZONE		/**/
+#$d_tm_tm_gmtoff HAS_TM_TM_GMTOFF	/**/
 
 /* I_USTAT:
  *	This symbol, if defined, indicates that <ustat.h> exists and
diff -up 'bleedperl2/ext/POSIX/t/posix.t' 'bleedperl3/ext/POSIX/t/posix.t'
Index: ./ext/POSIX/t/posix.t
Inline Patch
--- ./ext/POSIX/t/posix.t	Thu Nov  7 10:37:39 2002
+++ ./ext/POSIX/t/posix.t	Fri Nov  8 21:41:47 2002
@@ -11,7 +11,7 @@ BEGIN {
 }
 
 require "./test.pl";
-plan(tests => 61);
+plan(tests => 66);
 
 
 use POSIX qw(fcntl_h signal_h limits_h _exit getcwd open read strftime write
@@ -182,6 +182,26 @@ try_strftime("Tue Feb 29 00:00:00 2000 0
 try_strftime("Wed Mar 01 00:00:00 2000 061", 0,0,0, 1,2,100);
 try_strftime("Fri Mar 31 00:00:00 2000 091", 0,0,0, 31,2,100);
 &POSIX::setlocale(&POSIX::LC_TIME, $lc) if $Config{d_setlocale};
+
+SKIP: {
+    # XXX wait for smokers to see which OSs else to skip
+    skip("No mktime and/or tm_gmtoff", 5)
+	if !$Config{d_mktime} || !$Config{d_tm_tm_gmtoff} || !$Config{d_tm_tm_zone};
+    local $ENV{TZ} = "Europe/Berlin";
+
+    # May fail for ancient FreeBSD versions.
+    # %z is not included in POSIX, but valid on Linux and FreeBSD.
+    foreach $def ([1000,'Sun Sep  9 03:46:40 2001 +0200 CEST'],
+		  [900, 'Thu Jul  9 18:00:00 1998 +0200 CEST'],
+		  [800, 'Tue May  9 08:13:20 1995 +0200 CEST'],
+		  [700, 'Sat Mar  7 21:26:40 1992 +0100 CET'],
+		  [600, 'Thu Jan  5 11:40:00 1989 +0100 CET'],
+		 ) {
+	my($t, $expected) = @$def;
+        my @tm = localtime($t*1000000);
+	is(strftime("%c %z %Z",@tm), $expected, "validating zone setting: $expected");
+    }
+}
 
 {
     for my $test (0, 1) {
diff -up 'bleedperl2/util.c' 'bleedperl3/util.c'
Index: ./util.c
Inline Patch
--- ./util.c	Thu Nov  7 10:43:01 2002
+++ ./util.c	Fri Nov  8 21:44:10 2002
@@ -3383,6 +3383,20 @@ Perl_my_strftime(pTHX_ char *fmt, int se
   mytm.tm_yday = yday;
   mytm.tm_isdst = isdst;
   mini_mktime(&mytm);
+  /* use libc to get the values for tm_gmtoff and tm_zone [perl #18238] */
+#ifdef HAS_MKTIME
+  STMT_START {
+    struct tm mytm2;
+    mytm2 = mytm;
+    mktime(&mytm2);
+#ifdef HAS_TM_TM_GMTOFF
+    mytm.tm_gmtoff = mytm2.tm_gmtoff;
+#endif
+#ifdef HAS_TM_TM_ZONE
+    mytm.tm_zone = mytm2.tm_zone;
+#endif
+  } STMT_END;
+#endif
   buflen = 64;
   New(0, buf, buflen, char);
   len = strftime(buf, buflen, fmt, &mytm);
#### End of Patch data ####

#### ApplyPatch data follows ####
# Data version : 1.0
# Date generated : Fri Nov 8 22​:12​:00 2002
# Generated by : makepatch 2.00_05
# Recurse directories : Yes
# v 'patchlevel.h' 3261 1036661713 33056
# p 'config_h.SH' 135779 1036788230 0100440
# p 'ext/POSIX/t/posix.t' 7830 1036788107 0100775
# p 'util.c' 100968 1036788250 0100660
#### End of ApplyPatch data ####

#### End of Patch kit [created​: Fri Nov 8 22​:12​:00 2002] ####
#### Patch checksum​: 101 3393 50049 ####
#### Checksum​: 119 4018 36083 ####

--
Slaven Rezic - slaven.rezic@​berlin.de
  BBBike - route planner for cyclists in Berlin
  WWW version​: http​://www.bbbike.de
  Perl/Tk version for Unix and Windows​: http​://bbbike.sourceforge.net

@p5pRT
Copy link
Author

p5pRT commented Nov 8, 2002

From atatat@atatdot.net

thanks for your attention to this. a few comments​:

+ # May fail for ancient FreeBSD versions.

may fail for *anything* older than, say, 1998.

+ # %z is not included in POSIX, but valid on Linux and FreeBSD.

and also on netbsd, which is where i noticed the problem.

solaris (2.6, at least) also has %Z, but doesn't have the %z format,
though posix now specifies it. solaris 2.8 also doesn't have %z.

also...any chance of this patch (or something like it) being
back-ported to the 5.6 release?

--
|-----< "CODE WARRIOR" >-----|
codewarrior@​daemon.org * "ah! i see you have the internet
twofsonet@​graffiti.com (Andrew Brown) that goes *ping*!"
werdna@​squooshy.com * "information is power -- share the wealth."

@p5pRT
Copy link
Author

p5pRT commented Nov 8, 2002

From goldbb2@earthlink.net

Slaven Rezic wrote​:
[snip]

+ /* use libc to get the values for tm_gmtoff and tm_zone [perl #18238] */
+#ifdef HAS_MKTIME
+ STMT_START {
+ struct tm mytm2;
+ mytm2 = mytm;
+ mktime(&mytm2);
+#ifdef HAS_TM_TM_GMTOFF
+ mytm.tm_gmtoff = mytm2.tm_gmtoff;
+#endif
+#ifdef HAS_TM_TM_ZONE
+ mytm.tm_zone = mytm2.tm_zone;
+#endif
+ } STMT_END;
+#endif

Surely unless at least one of HAS_TM_TM_GMTOFF or HAS_TM_TM_ZONE is
defined, we shouldn't be doing this block. That is, change​:

  #ifdef HAS_MKTIME

Into​:

  #if defined(HAS_MKTIME) && ( defined(HAS_TM_TM_GMTOFF) || (HAS_TM_TM_ZONE) )

--
my $n = 2; print +(split //, 'e,4c3H r ktulrnsJ2tPaeh'
...."\n1oa! er")[map $n = ($n * 24 + 30) % 31, (42) x 26]

@p5pRT
Copy link
Author

p5pRT commented Nov 9, 2002

From @eserte

Benjamin Goldberg <goldbb2@​earthlink.net> writes​:

Slaven Rezic wrote​:
[snip]

+ /* use libc to get the values for tm_gmtoff and tm_zone [perl #18238] */
+#ifdef HAS_MKTIME
+ STMT_START {
+ struct tm mytm2;
+ mytm2 = mytm;
+ mktime(&mytm2);
+#ifdef HAS_TM_TM_GMTOFF
+ mytm.tm_gmtoff = mytm2.tm_gmtoff;
+#endif
+#ifdef HAS_TM_TM_ZONE
+ mytm.tm_zone = mytm2.tm_zone;
+#endif
+ } STMT_END;
+#endif

Surely unless at least one of HAS_TM_TM_GMTOFF or HAS_TM_TM_ZONE is
defined, we shouldn't be doing this block. That is, change​:

#ifdef HAS_MKTIME

Into​:

#if defined(HAS_MKTIME) && ( defined(HAS_TM_TM_GMTOFF) || (HAS_TM_TM_ZONE) )

OK​:

#
#
# To apply this patch​:
# STEP 1​: Chdir to the source directory.
# STEP 2​: Run the 'applypatch' program with this patch file as input.
#
# If you do not have 'applypatch', it is part of the 'makepatch' package
# that you can fetch from the Comprehensive Perl Archive Network​:
# http​://www.perl.com/CPAN/authors/Johan_Vromans/makepatch-x.y.tar.gz
# In the above URL, 'x' should be 2 or higher.
#
# To apply this patch without the use of 'applypatch'​:
# STEP 1​: Chdir to the source directory.
# STEP 2​: Run the 'patch' program with this file as input.
#
#### End of Preamble ####

#### Patch data follows ####
diff -up 'bleedperl2/config_h.SH' 'bleedperl3/config_h.SH'
Index​: ./config_h.SH
Prereq​: 3.0.1.5

Inline Patch
--- ./config_h.SH	Thu Nov  7 10:36:12 2002
+++ ./config_h.SH	Fri Nov  8 21:43:50 2002
@@ -2994,10 +2994,15 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#und
  *	This symbol, if defined, indicates to the C program that
  *	the struct tm has a tm_zone field.
  */
+/* HAS_TM_TM_GMTOFF:
+ *	This symbol, if defined, indicates to the C program that
+ *	the struct tm has a tm_gmtoff field.
+ */
 #$i_time I_TIME		/**/
 #$i_systime I_SYS_TIME		/**/
 #$i_systimek I_SYS_TIME_KERNEL		/**/
 #$d_tm_tm_zone HAS_TM_TM_ZONE		/**/
+#$d_tm_tm_gmtoff HAS_TM_TM_GMTOFF	/**/
 
 /* I_USTAT:
  *	This symbol, if defined, indicates that <ustat.h> exists and
diff -up 'bleedperl2/ext/POSIX/t/posix.t' 'bleedperl3/ext/POSIX/t/posix.t'
Index: ./ext/POSIX/t/posix.t
Inline Patch
--- ./ext/POSIX/t/posix.t	Thu Nov  7 10:37:39 2002
+++ ./ext/POSIX/t/posix.t	Fri Nov  8 21:41:47 2002
@@ -11,7 +11,7 @@ BEGIN {
 }
 
 require "./test.pl";
-plan(tests => 61);
+plan(tests => 66);
 
 
 use POSIX qw(fcntl_h signal_h limits_h _exit getcwd open read strftime write
@@ -182,6 +182,26 @@ try_strftime("Tue Feb 29 00:00:00 2000 0
 try_strftime("Wed Mar 01 00:00:00 2000 061", 0,0,0, 1,2,100);
 try_strftime("Fri Mar 31 00:00:00 2000 091", 0,0,0, 31,2,100);
 &POSIX::setlocale(&POSIX::LC_TIME, $lc) if $Config{d_setlocale};
+
+SKIP: {
+    # XXX wait for smokers to see which OSs else to skip
+    skip("No mktime and/or tm_gmtoff", 5)
+	if !$Config{d_mktime} || !$Config{d_tm_tm_gmtoff} || !$Config{d_tm_tm_zone};
+    local $ENV{TZ} = "Europe/Berlin";
+
+    # May fail for ancient FreeBSD versions.
+    # %z is not included in POSIX, but valid on Linux and FreeBSD.
+    foreach $def ([1000,'Sun Sep  9 03:46:40 2001 +0200 CEST'],
+		  [900, 'Thu Jul  9 18:00:00 1998 +0200 CEST'],
+		  [800, 'Tue May  9 08:13:20 1995 +0200 CEST'],
+		  [700, 'Sat Mar  7 21:26:40 1992 +0100 CET'],
+		  [600, 'Thu Jan  5 11:40:00 1989 +0100 CET'],
+		 ) {
+	my($t, $expected) = @$def;
+        my @tm = localtime($t*1000000);
+	is(strftime("%c %z %Z",@tm), $expected, "validating zone setting: $expected");
+    }
+}
 
 {
     for my $test (0, 1) {
diff -up 'bleedperl2/util.c' 'bleedperl3/util.c'
Index: ./util.c
Inline Patch
--- ./util.c	Thu Nov  7 10:43:01 2002
+++ ./util.c	Sat Nov  9 23:14:07 2002
@@ -3383,6 +3383,20 @@ Perl_my_strftime(pTHX_ char *fmt, int se
   mytm.tm_yday = yday;
   mytm.tm_isdst = isdst;
   mini_mktime(&mytm);
+  /* use libc to get the values for tm_gmtoff and tm_zone [perl #18238] */
+#if defined(HAS_MKTIME) && (defined(HAS_TM_TM_GMTOFF) || defined(HAS_TM_TM_ZONE))
+  STMT_START {
+    struct tm mytm2;
+    mytm2 = mytm;
+    mktime(&mytm2);
+#ifdef HAS_TM_TM_GMTOFF
+    mytm.tm_gmtoff = mytm2.tm_gmtoff;
+#endif
+#ifdef HAS_TM_TM_ZONE
+    mytm.tm_zone = mytm2.tm_zone;
+#endif
+  } STMT_END;
+#endif
   buflen = 64;
   New(0, buf, buflen, char);
   len = strftime(buf, buflen, fmt, &mytm);
#### End of Patch data ####

#### ApplyPatch data follows ####
# Data version : 1.0
# Date generated : Sat Nov 9 23​:15​:52 2002
# Generated by : makepatch 2.00_05
# Recurse directories : Yes
# v 'patchlevel.h' 3261 1036661713 33056
# p 'config_h.SH' 135779 1036788230 0100440
# p 'ext/POSIX/t/posix.t' 7830 1036788107 0100775
# p 'util.c' 100968 1036880047 0100660
#### End of ApplyPatch data ####

#### End of Patch kit [created​: Sat Nov 9 23​:15​:52 2002] ####
#### Patch checksum​: 101 3457 55148 ####
#### Checksum​: 119 4082 41188 ####

--
Slaven Rezic - slaven.rezic@​berlin.de

Tk-AppMaster​: a perl/Tk module launcher designed for handhelds
  http​://tk-appmaster.sf.net

@p5pRT
Copy link
Author

p5pRT commented Dec 9, 2002

From @hvds

Slaven Rezic <slaven.rezic@​berlin.de> wrote​:
:"atatat@​atatdot.net (via RT)" <perlbug@​perl.org> writes​:
:> while perl does reorganize the members of the "struct tm" that gets
:> passed to strftime() so that they're roughly internally coherent, the
:> dance that is does with init_tm() and mini_mktime() leave the timezone
:> strings and gmt offset wrong for times outside of the current daylight
:> setting (eg, for a time six months from now).
:[...]
:Here's (nearly) the same patch which works with bleadperl --- the
:strftime code moved from POSIX.xs to util.c in perl 5.8.0, so the
:original patch did not apply. Also included a test which should at
:least work on Linux and FreeBSD.
[followed by a couple of iterations]

Whoops, I dropped this one, sorry. Now applied with thanks as change
#18267.

Hugo

@p5pRT
Copy link
Author

p5pRT commented Dec 9, 2002

From @jhi

Since the patch was applied, I'm marking the problem ticket as resolved.

@p5pRT
Copy link
Author

p5pRT commented Dec 9, 2002

@jhi - Status changed from 'new' to 'resolved'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant