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

POSIX::strftime bugfeature: incorrect mktime() call #838

Closed
p5pRT opened this issue Nov 10, 1999 · 2 comments
Closed

POSIX::strftime bugfeature: incorrect mktime() call #838

p5pRT opened this issue Nov 10, 1999 · 2 comments

Comments

@p5pRT
Copy link

p5pRT commented Nov 10, 1999

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

Searchable as RT1766$

@p5pRT
Copy link
Author

p5pRT commented Nov 10, 1999

From netch@lucky.net

GMTime of unixtime 941284799 is 30th of October, 1999, 11​:59​:59.

The following code​:

#!/usr/bin/perl
use POSIX;
print strftime("%Y %m %d %H %M %S", gmtime(941284799)), "\n";

prints

1999 10 30 12 59 59

(note environment information​: timezone is Europe/Kiev)

This effect (adding of 1 hour) appeared in perl 5.00503 and did not exist in
perl 5.00502.

Diff of ${perl}/ext/POSIX/POSIX.xs between 5.00502 and 5.00503
contains following​:

==={
@​@​ -3591,7 +3603,7 @​@​
  RETVAL

char *
-strftime(fmt, sec, min, hour, mday, mon, year, wday = 0, yday = 0, isdst = 0)
+strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1
  char * fmt
  int sec
  int min
@​@​ -3617,8 +3629,45 @​@​
  mytm.tm_wday = wday;
  mytm.tm_yday = yday;
  mytm.tm_isdst = isdst;
+ (void) mktime(&mytm);
  len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
===}

Well, test it and see that mktime() normalizes time according to
local time zone (Europe/Kiev in our case)​:

==={
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

void f( int x )
{
  char buf[ 200 ];
  struct tm stm;
  bzero( &stm, sizeof stm );
  stm.tm_year = 99; stm.tm_mon = 9; stm.tm_mday = 30; stm.tm_hour = 11;
  stm.tm_min = 59; stm.tm_sec = 59; stm.tm_isdst = 0;
  if( x )
  mktime( &stm );
  bzero( buf, sizeof buf );
  strftime( buf, sizeof buf, "%Y %m %d %H %M %S", &stm );
  puts( buf );
  printf( "%d %d %d %d %d %d\n", stm.tm_year, stm.tm_mon, stm.tm_mday,
  stm.tm_hour, stm.tm_min, stm.tm_sec, stm.tm_isdst );
}

int main() {
  f( 0 ); f( 1 ); return 1;
}
===}

output is​:

==={
netch@​burka​:~/prog/tiny/2>./3
1999 10 30 11 59 59
99 9 30 11 59 59 0
1999 10 30 12 59 59
99 9 30 12 59 59 1
===}

This mktime() behavior possibly correct and in any case accords to its man page​:

==={
  On successful completion, the values of the tm_wday and tm_yday compo-
  nents of the structure are set appropriately, and the other components
  are set to represent the specified calendar time, but with their values
  forced to their normal ranges; the final value of tm_mday is not set un-
  til tm_mon and tm_year are determined. Mktime() returns the specified
  calendar time; if the calendar time cannot be represented, it returns -1;
===}

But, the time in question was GMT time, not local time.

Thus, perl MUST NOT call mktime() in strftime() because strftime MUST
ONLY PRINT its data and MUST NOT have any opinion of its content because
it cannot know real time zone of the data.

  >Fix​:
 
Disable the mktime() call in POSIX​::strftime.

(For FreeBSD only. This text originally was PR to FreeBSD.)
Also disable init_tm(),
whis is really localtime(time()) - IMHO the better solution for FreeBSD
in case of tm_gmtoff & tm_zone patameters is to set them to most safe value,
i.e. 0.

(note​: spaces/tabs are incorrect in this diff)
==={

Inline Patch
--- src/contrib/perl5/ext/POSIX/POSIX.xs.orig   Wed May  5 16:15:29 1999
+++ src/contrib/perl5/ext/POSIX/POSIX.xs        Wed Nov 10 20:04:30 1999
@@ -3617,21 +3617,20 @@
     CODE:
        {
            char tmpbuf[128];
            struct tm mytm;
            int len;
-           init_tm(&mytm);     /* XXX workaround - see init_tm() above */
+           bzero(&mytm, sizeof(mytm));
            mytm.tm_sec = sec;
            mytm.tm_min = min;
            mytm.tm_hour = hour;
            mytm.tm_mday = mday;
            mytm.tm_mon = mon;
            mytm.tm_year = year;
            mytm.tm_wday = wday;

  mytm.tm_yday = yday;
  mytm.tm_isdst = isdst;
- (void) mktime(&mytm);
  len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
  /*
  ** The following is needed to handle to the situation where
  ** tmpbuf overflows. Basically we want to allocate a buffer
  ** and try repeatedly. The reason why it is so complicated
===}

Perl Info


Site configuration information for perl 5.00503:

Configured by markm at $Date: 1999/05/05 19:42:40 $.

Summary of my perl5 (5.0 patchlevel 5 subversion 3) configuration:
  Platform:
    osname=freebsd, osvers=4.0-current, archname=i386-freebsd
    uname='freebsd freefall.freebsd.org 4.0-current freebsd 4.0-current #0: $Date: 1999/05/05 19:42:40 $'
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef useperlio=undef d_sfio=undef
  Compiler:
    cc='cc', optimize='undef', gccversion=egcs-2.91.66 19990314 (egcs-1.1.2 release)
    cppflags=''
    ccflags =''
    stdchar='char', d_stdstdio=undef, usevfork=true
    intsize=4, longsize=4, ptrsize=4, doublesize=8
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    alignbytes=4, usemymalloc=n, prototype=define
  Linker and Libraries:
    ld='cc', ldflags ='-Wl,-E'
    libpth=/usr/lib
    libs=-lm -lc -lcrypt
    libc=/usr/lib/libc.so, so=so, useshrplib=true, libperl=libperl.so.3
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' '
    cccdlflags='-DPIC -fpic', lddlflags='-shared'

Locally applied patches:
    


@INC for perl 5.00503:
    /usr/libdata/perl/5.00503/mach
    /usr/libdata/perl/5.00503
    /usr/local/lib/perl5/site_perl/5.005/i386-freebsd
    /usr/local/lib/perl5/site_perl/5.005
    .


Environment for perl 5.00503:
    HOME=/usr/homes/netch
    LANG=ru_RU.KOI8-R
    LANGUAGE (unset)
    LC_COLLATE=ru_RU.KOI8-R
    LC_CTYPE=ru_RU.KOI8-R
    LC_TIME=C
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/usr/homes/netch/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
    PERL_BADLANG (unset)
    SHELL=/usr/local/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Nov 13, 1999

From [Unknown Contact. See original ticket]

On Wed, 10 Nov 1999 at 21​:51​:22 +0200, Valentin Nechayev wrote​:

This effect (adding of 1 hour) appeared in perl 5.00503 and did not exist in
perl 5.00502.

This is fixed, it appears, in 5.00562 [*]

[ 4223] By​: gsar on 1999/09/24 05​:05​:06
  Log​: normalize time for strftime() (without the isdst effects of
  mktime()) using a custom mini_mktime()
  From​: spider-perl@​Orb.Nashua.NH.US
  Date​: Thu, 23 Sep 1999 17​:54​:53 -0400
  Message-Id​: <199909232154.RAA25151@​leggy.zk3.dec.com>
  Subject​: Re​: [ID 19990913.003] Possible bug using POSIX​::strftime

One cannot do without this normalisation if the optionality of the three
trailing parameters is to be preserved. The versions of strftime before
5.00503 got things totally wrong, or core-dumped, because of this.

Ian

[*] I really must get round to loading 5.00562... unfortunately, my
  patch (2.1b) barfs on the 5.00562 patch file - too long a line in
  the big-float test module...​:)

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