posix for asia/tehran is wrong
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. Year DST Start Date and Time DST End Date and Time *2014* Sat, Mar 22, 0:00 AM Mon, Sep 22, 0:00 AM *2015* Sun, Mar 22, 0:00 AM Tue, Sep 22, 0:00 AM *2016* Mon, Mar 21, 0:00 AM Wed, Sep 21, 0:00 AM *2017* Wed, Mar 22, 0:00 AM Fri, Sep 22, 0:00 AM *2018* Thu, Mar 22, 0:00 AM Sat, Sep 22, 0:00 AM This mean Tehran should be "Asia/Tehran","<+0330>-3:30<+0430>,80/0,264/0". The 'n' format, which counts 29 Feb, and starts count from 0, so would not come up a day short on non-leap years.
On Nov 29, 2018, at 4:20 PM, Mark <tz@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.)
Guy Harris wrote:
the correct POSIX TZ setting for Iran, at least as I read the SUS, would be "<+0330>-3:30<+0430>,J81/0,J265/0"
As I mentioned in my previous email, there is no POSIX TZ setting for Iran because its DST changes don't follow the Gregorian calendar. The string at the end of the Asia/Tehran file is just an approximation. As near as I can tell, the string "<+0330>-3:30<+0430>,J80/0,J264/0" at the end of the Asia/Tehran file has the same behavior as the approximation that the "asia" file uses for far-future timestamps in Asia/Tehran. So we should be OK here.
omitting the "J" is not even valid for January and February,
I don't see why not. POSIX says that omitting the J makes it possible to specify February 29, so clearly omitting the J is valid for pre-March dates.
zic should probably either:
... always put the "J" in
But always putting the "J" in, even when not needed, adds a byte to the output file for rules in January and February.
or
go with 0-based Julian days
That wouldn't be useful for dates after February, as tzdb Rule lines for those dates never correspond to 0-based Julian days.
Mark wrote:
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.
No, Iranian DST starts at 24:00 on Farvardin 1 and ends at 24:00 on Shahrivar 30 in the Persian calendar, and this disagrees with the Gregorian approximation you mentioned (assuming you meant 00:00 on the stated days). For example, in 2029 DST is scheduled to start March 21 and end September 21 even though 2029 is not a leap year. This is documented in the 'asia' file. There is no simple Gregorian equivalent to the Iranian rules' use of the Persian calendar, so we might as well stick with the current Gregorian approximation for far-future years. That being said, this is a good time as any to extend the table of exact transitions past its current cutoff year 2037, as the 2037 cutoff dates back to when zic couldn't reliably handle dates past 2038. A reasonable cutoff for Iranian DST prediction is to stop just before the year 2091, as there is some controversy over how to interpret current Iranian law for that year. (Of course the DST rules will probably change before then....) Proposed patch attached.
On 2018-11-30 01:41, Paul Eggert wrote:
There is no simple Gregorian equivalent to the Iranian rules' use of the Persian calendar, so we might as well stick with the current Gregorian approximation for far-future years. That being said, this is a good time as any to extend the table of exact transitions past its current cutoff year 2037, as the 2037 cutoff dates back to when zic couldn't reliably handle dates past 2038. A reasonable cutoff for Iranian DST prediction is to stop just before the year 2091, as there is some controversy over how to interpret current Iranian law for that year. (Of course the DST rules will probably change before then...) Proposed patch attached.
You previously corrected the code output by hand, but now appear to do so in the Lisp approximation, which means the following description is confusing: -# That cal-persia used Birashk's approximation, which disagrees with the solar -# calendar predictions for the year 2025, so I corrected those dates by hand. +# I used the following code in GNU Emacs 25.2 to generate the "Rule Iran" +# lines from 2008 through 2087. This code uses Ed Reingold's cal-persia +# which uses Birashk's approximation, and this disagrees with the solar +# calendar predictions for the Persian years 1404 (Gregorian 2025) +# and 1437 (Gregorian 2058), so this code fixes those two years by hand. so I suggest something like the following: -# which uses Birashk's approximation, and this disagrees with the solar -# which uses Birashk's approximation, and disagrees with the solar +# calendar predictions for the Persian years 1404 (Gregorian 2025) -# and 1437 (Gregorian 2058), so this code fixes those two years by hand. -# and 1437 (Gregorian 2058), so this code fixes those two years. For occasional and non-Lispers, the included Emacs Lisp code is non-obvious: it may be more helpful to provide the code in a separate file, which also clearly documents its function in plain language, enhancing understanding and implementation in other languages, and reference that. -- Take care. Thanks, Brian Inglis, Calgary, Alberta, Canada This email may be disturbing to some readers as it contains too much technical detail. Reader discretion is advised.
On 11/30/18 7:50 AM, Brian Inglis wrote:
You previously corrected the code output by hand, but now appear to do so in the Lisp approximation, which means the following description is confusing:
Thanks for catching that. I tried to improve the comment (in a different way) in the attached patch.
For occasional and non-Lispers, the included Emacs Lisp code is non-obvious: it may be more helpful to provide the code in a separate file, which also clearly documents its function in plain language, enhancing understanding and implementation in other languages, and reference that.
In the past I haven't bothered to do that, as it's a pain to distribute ad-hoc files (e.g., we used to have Lisp code for Morocco, then it went away, then it came back). Also, the Lisp code is not intended to be run routinely; it's intended mainly to be documentation for the nearby data. So for now I think I'll stick with tradition and keep the Lisp code in a comment in the data file. That being said, the Lisp code could be clearer and I fussed with it a bit to do that in the attached patch, though I did not take the trouble to write extensive internal documentation.
participants (4)
-
Brian Inglis -
Guy Harris -
Mark -
Paul Eggert