Date: Wed, 10 Nov 1999 21:12:44 +0200 (EET) From: netch@lucky.net (Valentin Nechayev) To: FreeBSD-gnats-submit@freebsd.org Subject: bin/14813: Perl POSIX::strftime bugfeature Message-ID: <199911101912.VEX88453@burka.carrier.kiev.ua>
next in thread | raw e-mail | index | archive | help
>Number: 14813 >Category: bin >Synopsis: Perl POSIX::strftime bugfeature >Confidential: no >Severity: serious >Priority: high >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Wed Nov 10 11:20:00 PST 1999 >Closed-Date: >Last-Modified: >Originator: Valentin Nechayev <netch@lucky.net> >Release: FreeBSD 3.3-RC i386 >Organization: Lucky Net Ltd. >Environment: FreeBSD 3.3-RC Perl 5.00503 in basic system >Description: 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. >How-To-Repeat: See above. >Fix: Disable the mktime() call in POSIX::strftime. 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) ==={ --- 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 ===} >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199911101912.VEX88453>