Time zone bug

Brian Inglis Brian.Inglis@SystematicSw.ab.ca
Mon May 1 17:25:00 GMT 2017


On 2017-05-01 09:25, cyg Simple wrote:
> On 4/30/2017 7:19 PM, Steven Penny wrote:
>> On Sun, 30 Apr 2017 17:37:00, Ken Brown wrote:
>>> As a result of a failing emacs test, I came across the following:
>>>
>>> $ TZ='NZST-12NZDT,M9.5.0,M4.1.0/3' date -d@0 +'%Y-%m-%d %H:%M:%S %z (%Z)'
>>> 1970-01-01 12:00:00 +1200 (NZST)
>>>
>>> The same command on Linux yields "1970-01-01 13:00:00 +1300 (NZDT)",
>>> which is
>>> correct according to Paul Eggert
>>> (https://lists.gnu.org/archive/html/emacs-devel/2017-04/msg00881.html).

This may be a problem in: 

	winsup/cygwin/localtime.cc

not properly handling POSIX time zone specs in the S hemisphere, with DST 
which starts in autumn/fall and ends in spring, at the start of the time_t 
epoch, where the first DST transition happens with negative time_t. 

Neither localtime nor mktime on Cygwin properly handle these rules where 
both localtime and mktime on Linux do - STC and sdiff attached. 

On Linux, even if you comment out the localtime call, mktime sets the 
offset correctly; on Cygwin even using localtime and mktime fails to set 
the offset at 1970-01-01 correctly. 

Cygwin awk strftime also shows the same issue where Linux is also correct.

>> I concur, here is non-esoteric example Linux:
>>    $ TZ=Pacific/Auckland date +%Z
>>    NZST
>>    $ TZ=NZST date +%Z
>>    NZST
>>    $ TZ=NZDT date +%Z
>>    NZDT
>> Cygwin:
>>    $ TZ=Pacific/Auckland date +%Z
>>    NZST
>>    $ TZ=NZST date +%Z
>>    GMT
>>    $ TZ=NZDT date +%Z
>>    GMT

This is just a slight difference in handling POSIX time zone specs 
with no explicit offset specified - the basic format is std offset. 
If you provide only a std abbr with no offset, Cygwin defaults to 
GMT, where Linux uses the abbr and sets the offset to zero. Both 
behave identically if you specify a +-offset:

$ TZ=XXX date +"%F %a %R%z(%Z)"; TZ=XXX0 date +"%F %a %R%z(%Z)"
2017-05-01 Mon 17:13+0000(GMT)
2017-05-01 Mon 17:13+0000(XXX)
$ ssh ... 'TZ=XXX date +"%F %a %R%z(%Z)"; TZ=XXX0 date +"%F %a %R%z(%Z)"'
2017-05-01 Mon 17:13+0000(XXX)
2017-05-01 Mon 17:13+0000(XXX)

depends whether you prefer to see the same answer or the smart answer ;^>

-- 
Take care. Thanks, Brian Inglis, Calgary, Alberta, Canada
-------------- next part --------------
NZST-12NZDT,M9.5.0,M4.1.0/3			NZST-12NZDT,M9.5.0,M4.1.0/3

1970-01-01 001 4   00:00+0000s(GMT)	gm	1970-01-01 001 4   00:00+0000s(GMT)	gm
1970-01-01 001 4   13:00+1300d(NZDT)	local |	1970-01-01 001 4   12:00+1200s(NZST)	local
1970-01-01 001 4   13:00+1300d(NZDT)	mk    |	1970-01-01 001 4   12:00+1200s(NZST)	mk
1970-01-01 001 Thu 13:00+1300 (NZDT)	      |	1970-01-01 001 Thu 12:00+1200 (NZST)

1970-04-04 094 6   13:00+0000s(GMT)	gm	1970-04-04 094 6   13:00+0000s(GMT)	gm
1970-04-05 095 0   02:00+1300d(NZDT)	local |	1970-04-05 095 0   01:00+1200s(NZST)	local
1970-04-05 095 0   02:00+1300d(NZDT)	mk    |	1970-04-05 095 0   01:00+1200s(NZST)	mk
1970-04-05 095 Sun 02:00+1300 (NZDT)	      |	1970-04-05 095 Sun 01:00+1200 (NZST)

1970-04-04 094 6   14:00+0000s(GMT)	gm	1970-04-04 094 6   14:00+0000s(GMT)	gm
1970-04-05 095 0   02:00+1200s(NZST)	local	1970-04-05 095 0   02:00+1200s(NZST)	local
1970-04-05 095 0   02:00+1200s(NZST)	mk	1970-04-05 095 0   02:00+1200s(NZST)	mk
1970-04-05 095 Sun 02:00+1200 (NZST)		1970-04-05 095 Sun 02:00+1200 (NZST)

1970-09-26 269 6   13:00+0000s(GMT)	gm	1970-09-26 269 6   13:00+0000s(GMT)	gm
1970-09-27 270 0   01:00+1200s(NZST)	local	1970-09-27 270 0   01:00+1200s(NZST)	local
1970-09-27 270 0   01:00+1200s(NZST)	mk	1970-09-27 270 0   01:00+1200s(NZST)	mk
1970-09-27 270 Sun 01:00+1200 (NZST)		1970-09-27 270 Sun 01:00+1200 (NZST)

1970-09-26 269 6   14:00+0000s(GMT)	gm	1970-09-26 269 6   14:00+0000s(GMT)	gm
1970-09-27 270 0   03:00+1300d(NZDT)	local	1970-09-27 270 0   03:00+1300d(NZDT)	local
1970-09-27 270 0   03:00+1300d(NZDT)	mk	1970-09-27 270 0   03:00+1300d(NZDT)	mk
1970-09-27 270 Sun 03:00+1300 (NZDT)		1970-09-27 270 Sun 03:00+1300 (NZDT)

1971-01-01 001 5   00:00+0000s(GMT)	gm	1971-01-01 001 5   00:00+0000s(GMT)	gm
1971-01-01 001 5   13:00+1300d(NZDT)	local	1971-01-01 001 5   13:00+1300d(NZDT)	local
1971-01-01 001 5   13:00+1300d(NZDT)	mk	1971-01-01 001 5   13:00+1300d(NZDT)	mk
1971-01-01 001 Fri 13:00+1300 (NZDT)		1971-01-01 001 Fri 13:00+1300 (NZDT)

1971-04-03 093 6   13:00+0000s(GMT)	gm	1971-04-03 093 6   13:00+0000s(GMT)	gm
1971-04-04 094 0   02:00+1300d(NZDT)	local	1971-04-04 094 0   02:00+1300d(NZDT)	local
1971-04-04 094 0   02:00+1300d(NZDT)	mk	1971-04-04 094 0   02:00+1300d(NZDT)	mk
1971-04-04 094 Sun 02:00+1300 (NZDT)		1971-04-04 094 Sun 02:00+1300 (NZDT)

1971-04-03 093 6   14:00+0000s(GMT)	gm	1971-04-03 093 6   14:00+0000s(GMT)	gm
1971-04-04 094 0   02:00+1200s(NZST)	local	1971-04-04 094 0   02:00+1200s(NZST)	local
1971-04-04 094 0   02:00+1200s(NZST)	mk	1971-04-04 094 0   02:00+1200s(NZST)	mk
1971-04-04 094 Sun 02:00+1200 (NZST)		1971-04-04 094 Sun 02:00+1200 (NZST)

0						0
-------------- next part --------------
/* newlib/libc/time/strftime.c %z format STC */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define TZ	"TZ=NZST-12NZDT,M9.5.0,M4.1.0/3"
#define DFMT	"%04d-%02d-%02d %03d %-3d %02d:%02d%+03ld00%s(%s)\t%s\n" //:%02d
#define TFMT	"%F %j %a %R%z (%Z)"

#define EPOCH	1970
#define YADD	1900
#define MADD	 1
#define DADD	 1
#define YR_MTH	12
#define MTH_DAY	30
#define DAY_HR	24
#define HR_MIN	60
#define MIN_S	60
#define HR_S	(HR_MIN*MIN_S)

/* extra is total days over 30 in preceding months - net total 5/year */
#define S(extra,yr,mth,day,hr)	\
	((((((yr) - EPOCH)*YR_MTH + ((mth) - 1))*MTH_DAY + (extra) + (day) - 1)\
		*DAY_HR + (hr))*HR_S)


int
dump( struct tm* tp, char *label) {
    return printf( DFMT, tp->tm_year + YADD, tp->tm_mon + MADD, tp->tm_mday,
		tp->tm_yday + DADD, tp->tm_wday,
		tp->tm_hour, tp->tm_min,			// tp->tm_sec,
# ifdef __TM_GMTOFF
		tp->__TM_GMTOFF/HR_S,
# elif  __USE_BSD
		tp->tm_gmtoff/HR_S,
# else
		tp->__tm_gmtoff/HR_S,
# endif
		tp->tm_isdst < 0 ? "?" : tp->tm_isdst ? "d" : "s",
# ifdef __TM_ZONE
		tp->__TM_ZONE,
# elif  __USE_BSD
		tp->tm_zone,
# else
		tp->__tm_zone,
# endif
		label
	  	);
}


int
test( time_t tt ) {
    char	ss[BUFSIZ]	= "";
    struct tm *	tp;
    size_t	st;
    int		rc;

    if (EOF == (rc = puts( "" )))			return 5;
    if (!(tp = gmtime( &tt )))				return 1;
    if ((rc = dump( tp, "gm" )) <= 0)			return -rc;
    if (!(tp = localtime( &tt )))			return 2;
    if ((rc = dump( tp, "local" )) <= 0)		return -rc;
    if (-1 == (tt = mktime( tp )))			return 3;
    if ((rc = dump( tp, "mk" )) <= 0)			return -rc;
    if ((st = strftime( ss, sizeof ss, TFMT, tp)) <= 0)	return 4;
    if (EOF == (rc = puts( ss )))			return 5;

    return 0;
}


int
main( void ) {
    int	rc;

    if ((rc = putenv( TZ )))			return rc;
    if (EOF == (rc = puts( getenv( "TZ" ))))	return 6;
    /*		   extra  year  m day hour	*/
    if ((rc = test( S( 0, 1970, 1,  1,  0))))	return rc;
    if ((rc = test( S( 0, 1970, 4,  4, 13))))	return rc;
    if ((rc = test( S( 0, 1970, 4,  4, 14))))	return rc;
    if ((rc = test( S( 3, 1970, 9, 26, 13))))	return rc;
    if ((rc = test( S( 3, 1970, 9, 26, 14))))	return rc;
    if ((rc = test( S( 5, 1971, 1,  1,  0))))	return rc;
    if ((rc = test( S( 5, 1971, 4,  3, 13))))	return rc;
    if ((rc = test( S( 5, 1971, 4,  3, 14))))	return rc;

    return rc;
}

-------------- next part --------------

--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple


More information about the Cygwin mailing list