[tz] posix for asia/tehran is wrong

Guy Harris guy at alum.mit.edu
Fri Nov 30 02:46:30 UTC 2018


On Nov 29, 2018, at 4:20 PM, Mark <tz at diramu.net> wrote:

> I've been adding DST time zone to espurna (https://github.com/xoseperez/espurna/pull/1295). 
> All dst entries but asia/tehran, use Mm.n.d format to specify dst dates.  
> The Tehran use 'Jn': "Asia/Tehran","<+0330>-3:30<+0430>,J80/0,J264/0"
> 
> When I implement this 'J' format, I came out a day short.
> From https://en.wikipedia.org/wiki/Iran_Standard_Time: 
> 
> The Iranian DST starts on March 22 and ends on September 22 each year with the exception of leapyears in which the DST starts and ends one day prior.

Yes, September 22 is Jn, for n = 265.  To quote the current Single Unix Specification:

	http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html

"Jn
The Julian day n (1 <= n <= 365). Leap days shall not be counted. That is, in all years-including leap years-February 28 is day 59 and March 1 is day 60. It is impossible to refer explicitly to the occasional February 29.

n
The zero-based Julian day (0 <= n <= 365). Leap days shall be counted, and it is possible to refer to February 29."

and September 22 is, in a non-leap year, the 265th day of the year, with January 1 being the 1st day.

In addition, March 22 is the 81st day of the year, so the correct POSIX TZ setting for Iran, at least as I read the SUS, would be "<+0330>-3:30<+0430>,J81/0,J265/0"

However, the Asia/Tehran file on my Mac, running High Sierra, has the string "<+0330>-3:30<+0430>,J80/0,J264/0" in it, so it appears that the version of zic used to compile it generated the wrong string.

The code in the tip of the master branch of https://github.com/eggert/tz is:

	if (rp->r_dycode == DC_DOM) {
		register int	month, total;

		if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
			return -1;
		total = 0;
		for (month = 0; month < rp->r_month; ++month)
			total += len_months[0][month];
		/* Omit the "J" in Jan and Feb, as that's shorter.  */
		if (rp->r_month <= 1)
		  result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
		else
		  result += sprintf(result, "J%d", total + rp->r_dayofmonth);

That appears to be the code that generates day number in the Julian-day TZ strings.  The first test appears to be dealing with the

	Leap days shall not be counted. That is, in all years-including leap years-February 28 is day 59 and March 1 is day 60. It is impossible to refer explicitly to the occasional February 29.

part of the specification for "Jn"; however, the count starts at 0, so it appears to be calculating values for "The zero-based Julian day (0 <= n <= 365).", except that it's not counting leap days.

Furthermore, omitting the "J" is not even valid for January and February, as "J" indicates 1-based Julian days and a number without a "J" indicates 0-based Julian days.

zic should probably either:

	go with 1-based Julian days, fail if there's a transition on February 29 (a transition that happens every February 29 happens every 4 years except for every 100 years except for every 400 years blah blah blah), start "total" at 1, and always put the "J" in

or

	go with 0-based Julian days, *don't* fail if there's a transition on February 29, make sure the loop counts an extra day for February in a leap year, start "total" at 0, and never put the "J" in.

(Both forms of Julian date are in UNIX specifications going back at least as far as The Open Group System Interface Definitions Issue 4, Version 2.  Was there ever a time where some system supported one but not the other?

localtime() in the tip of the tzdb master branch appears to support both and, on my Mac running High Sierra, which has a tzdb-sample-code-derived localtime(), I get:

	$ TZ="<+0330>-3:30<+0430>,80/0,264/0" date -j 092100002018
	Fri Sep 21 00:00:00 +0430 2018
	$ TZ="<+0330>-3:30<+0430>,80/0,264/0" date -j 092200002018
	Sat Sep 22 00:00:00 +0330 2018

which matches

	$ TZ=Asia/Tehran date -j 092100002018
	Fri Sep 21 00:00:00 +0430 2018
	$ TZ=Asia/Tehran date -j 092200002018
	Sat Sep 22 00:00:00 +0330 2018

so that appear to work.)







More information about the tz mailing list