develooper Front page | perl.perl5.porters | Postings from October 2001

bug: strftime and %z

T.J. Mather
October 28, 2001 17:54
bug: strftime and %z
Message ID:
Perl's strftime reports the daylight savings offset based on the current
system time, not the offset of the date being formatted.  Test script:

use POSIX;
$ENV{'TZ'} = 'America/New_York';
print strftime("%m/%d/%Y %z", localtime(1000000000)) . "\n";
print strftime("%m/%d/%Y %z", localtime(1010000000)) . "\n";

Output (5.7.2, sys time in dst):
08/05/2001 -0400
11/28/2001 -0400

Output (5.7.2, sys time not in dst):
08/05/2001 -0500
11/28/2001 -0500

Correct Output (patched 5.7.2_01)
08/05/2001 -0400
11/28/2001 -0500

(tested on Linux 2.4.2, glibc 2.2.2, perl 5.6.1 and 5.7.2)

The problem is with the Perl_my_strftime function in
perl-5.7.2/util.c.  It calls mini_mktime instead of mktime to normalize
the time structure.  However unlike mktime, mini_mktime does not set
tm_gmtoff, and tm_gmtoff remains set to the value set by
localtime(&now) in init_tm.  Therefore, the timezone offset gets set based
on the current system time, *not* the passed time.

I was able to fix the problem by replacing Perl's mini_mktime with
system mktime:

--- perl-5.7.2/util.c   Thu Jul 12 09:59:49 2001
+++ perl-5.7.2_01/util.c        Sun Oct 28 20:07:02 2001
@@ -3604,7 +3604,8 @@
   mytm.tm_wday = wday;
   mytm.tm_yday = yday;
   mytm.tm_isdst = isdst;
-  mini_mktime(&mytm);
+  /* set tm_gmtoff so that %z will work properly */
+  mktime(&mytm);
   buflen = 64;
   New(0, buf, buflen, char);
   len = strftime(buf, buflen, fmt, &mytm);

However, I understand that mini_mktime was put in for
portability reasons.  It might be a good idea to make the use of
mini_mktime dependent on a #define HAS_SANE_MKTIME.  On systems that have
a working mktime (such as Linux), strftime would use mktime by default,
and for others strftime would use mini_mktime.

-TJ Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About