[PROPOSED 00/18] tzselect improvements for configuring "from now on"
For some time I've been meaning to extend tzselect so that users who care only about timestamps from now on can have a smaller set of timezones to choose from, thus simplifying configuration. This patchset does this via a new file zonenow.tab, with contents like zone1970.tab except that zonenow.tab partitions based on timestamps from now on, instead of from 1970 on. The new file's format is still experimental and feedback is welcome. These patches mostly just refactor tzselect, to simplify support for the new feature. The last patch adds the new feature. Paul Eggert (18): More-systematic punctuation in *.tab comments Improve Canadian comments in zone*.tab Open /dev/null less often in tzselect * Makefile: Say that ‘make’ should conform to POSIX. Improve tzselect diagnostics after rejection Don’t set TZ in tzselect Do not use empty RE in tzselect Port to POSIX awk, which prohibits -v newlines tzselect: work around mawk bug with {2,} * tzselect: Improve leading comments. tzselect: clarify translit need tzselect: port to POSIX iconv * NEWS: Improve formatting of tzselect news. tzselect: do not create temporary files * tzselect: Indent more consistently. * tzselect.ksh: Consistently omit ‘;;’ before ‘esac’. * tzselect.ksh: Indent more consistently. New tzselection option ‘now’ Makefile | 34 +- NEWS | 28 +- checknow.awk | 53 +++ checktab.awk | 7 +- tzselect.ksh | 977 ++++++++++++++++++++++++++++----------------------- zone.tab | 24 +- zone1970.tab | 24 +- zonenow.tab | 298 ++++++++++++++++ 8 files changed, 978 insertions(+), 467 deletions(-) create mode 100644 checknow.awk create mode 100644 zonenow.tab -- 2.40.1
* zone.tab, zone1970.tab: Be more systematic about using “;” for high level separation, “,” for medium, and “&” for low. For example, “Durango; Coahuila, Nuevo León, Tamaulipas (most areas)” because it’s all of Durango and most of the others; and “ON & QC (most areas)” instead of “ON, QC (most areas)” because otherwise the “(most areas)” would seem to apply only to QC. --- zone.tab | 24 ++++++++++++------------ zone1970.tab | 22 +++++++++++----------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/zone.tab b/zone.tab index dbcb6179..fe345265 100644 --- a/zone.tab +++ b/zone.tab @@ -48,7 +48,7 @@ AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucuman (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) @@ -87,7 +87,7 @@ BN +0456+11455 Asia/Brunei BO -1630-06809 America/La_Paz BQ +120903-0681636 America/Kralendijk BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Para (east); Amapa +BR -0127-04829 America/Belem Para (east), Amapa BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) BR -0803-03454 America/Recife Pernambuco BR -0712-04812 America/Araguaina Tocantins @@ -107,21 +107,21 @@ BT +2728+08939 Asia/Thimphu BW -2439+02555 Africa/Gaborone BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) -CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4734-05243 America/St_Johns Newfoundland, Labrador (southeast) +CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) -CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA +4339-07923 America/Toronto Eastern - ON & QC (most areas) CA +6344-06828 America/Iqaluit Eastern - NU (most areas) -CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) -CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +484531-0913718 America/Atikokan EST - ON (Atikokan), NU (Coral H) +CA +4953-09709 America/Winnipeg Central - ON (west), Manitoba CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W) +CA +5333-11328 America/Edmonton Mountain - AB, BC (E), NT (E), SK (W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +4906-11631 America/Creston MST - BC (Creston) @@ -207,8 +207,8 @@ HT +1832-07220 America/Port-au-Prince HU +4730+01905 Europe/Budapest ID -0610+10648 Asia/Jakarta Java, Sumatra ID -0002+10920 Asia/Pontianak Borneo (west, central) -ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) -ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas +ID -0507+11924 Asia/Makassar Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya), Malukus/Moluccas IE +5320-00615 Europe/Dublin IL +314650+0351326 Asia/Jerusalem IM +5409-00428 Europe/Isle_of_Man @@ -355,7 +355,7 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky RU +5934+15048 Asia/Magadan MSK+08 - Magadan RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E), N Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea RW -0157+03004 Africa/Kigali @@ -418,7 +418,7 @@ US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) -US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US +433649-1161209 America/Boise Mountain - ID (south), OR (east) US +332654-1120424 America/Phoenix MST - AZ (except Navajo) US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) diff --git a/zone1970.tab b/zone1970.tab index c510fa58..446b911d 100644 --- a/zone1970.tab +++ b/zone1970.tab @@ -52,7 +52,7 @@ AR -3124-06411 America/Argentina/Cordoba most areas: CB, CC, CN, ER, FM, MN, SE, AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucumán (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) @@ -81,7 +81,7 @@ BG +4241+02319 Europe/Sofia BM +3217-06446 Atlantic/Bermuda BO -1630-06809 America/La_Paz BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Pará (east); Amapá +BR -0127-04829 America/Belem Pará (east), Amapá BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) BR -0803-03454 America/Recife Pernambuco BR -0712-04812 America/Araguaina Tocantins @@ -99,19 +99,19 @@ BR -0958-06748 America/Rio_Branco Acre BT +2728+08939 Asia/Thimphu BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) -CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4734-05243 America/St_Johns Newfoundland, Labrador (southeast) +CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) -CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA,BS +4339-07923 America/Toronto Eastern - ON & QC (most areas) CA +6344-06828 America/Iqaluit Eastern - NU (most areas) -CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +4953-09709 America/Winnipeg Central - ON (west), Manitoba CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W) +CA +5333-11328 America/Edmonton Mountain - AB, BC (E), NT (E), SK (W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) @@ -171,8 +171,8 @@ HT +1832-07220 America/Port-au-Prince HU +4730+01905 Europe/Budapest ID -0610+10648 Asia/Jakarta Java, Sumatra ID -0002+10920 Asia/Pontianak Borneo (west, central) -ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) -ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas +ID -0507+11924 Asia/Makassar Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya), Malukus/Moluccas IE +5320-00615 Europe/Dublin IL +314650+0351326 Asia/Jerusalem IN +2232+08822 Asia/Kolkata @@ -287,7 +287,7 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky RU +5934+15048 Asia/Magadan MSK+08 - Magadan RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E), N Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea SA,AQ,KW,YE +2438+04643 Asia/Riyadh Syowa @@ -329,7 +329,7 @@ US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) -US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US +433649-1161209 America/Boise Mountain - ID (south), OR (east) US,CA +332654-1120424 America/Phoenix MST - AZ (most areas), Creston BC US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) -- 2.40.1
* zone.tab, zone1970.tab: Shorten the comments in the Canadian entries so that the output of ‘tzselect’ fits onto a 24×80 screen again, when you select “Americas” followed by “Canada”. Over time the number of entries had grown so that the output no longer fit. Also, in zone1970.tab add a CA-relevant comment for America/Puerto_Rico since that comment is used only for CA; this is the same idea as the comment for Europe/Zurich and DE. --- zone.tab | 4 ++-- zone1970.tab | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/zone.tab b/zone.tab index fe345265..3fa9306a 100644 --- a/zone.tab +++ b/zone.tab @@ -107,7 +107,7 @@ BT +2728+08939 Asia/Thimphu BW -2439+02555 Africa/Gaborone BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland, Labrador (southeast) +CA +4734-05243 America/St_Johns Newfoundland, Labrador (SE) CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick @@ -121,7 +121,7 @@ CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB, BC (E), NT (E), SK (W) +CA +5333-11328 America/Edmonton Mountain - AB, BC(E), NT(E), SK(W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +4906-11631 America/Creston MST - BC (Creston) diff --git a/zone1970.tab b/zone1970.tab index 446b911d..c7dcc436 100644 --- a/zone1970.tab +++ b/zone1970.tab @@ -99,7 +99,7 @@ BR -0958-06748 America/Rio_Branco Acre BT +2728+08939 Asia/Thimphu BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland, Labrador (southeast) +CA +4734-05243 America/St_Johns Newfoundland, Labrador (SE) CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick @@ -111,7 +111,7 @@ CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB, BC (E), NT (E), SK (W) +CA +5333-11328 America/Edmonton Mountain - AB, BC(E), NT(E), SK(W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) @@ -251,7 +251,7 @@ PK +2452+06703 Asia/Karachi PL +5215+02100 Europe/Warsaw PM +4703-05620 America/Miquelon PN -2504-13005 Pacific/Pitcairn -PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST +PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST - QC (Lower North Shore) PS +3130+03428 Asia/Gaza Gaza Strip PS +313200+0350542 Asia/Hebron West Bank PT +3843-00908 Europe/Lisbon Portugal (mainland) -- 2.40.1
* tzselect.ksh: Use >& so that the shell can dup2 rather than open. --- tzselect.ksh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tzselect.ksh b/tzselect.ksh index 8f5f9b26..e85259b2 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -46,7 +46,7 @@ say() { } # Check for awk POSIX compliance. -($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1 +($AWK -v x=y 'BEGIN { exit 123 }') <>/dev/null >&0 2>&0 [ $? = 123 ] || { say >&2 "$0: Sorry, your '$AWK' program is not POSIX compatible." exit 1 @@ -89,7 +89,7 @@ if ?*) : ;; '') # '; exit' should be redundant, but Dash doesn't properly fail without it. - (eval 'set --; select x; do break; done; exit') </dev/null 2>/dev/null + (eval 'set --; select x; do break; done; exit') <>/dev/null 2>&0 esac then # Do this inside 'eval', as otherwise the shell might exit when parsing it -- 2.40.1
--- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 58e78777..a6051531 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,9 @@ # 2009-05-17 by Arthur David Olson. # Request POSIX conformance; this must be the first non-comment line. .POSIX: +# On older platforms you may need to scrounge for a POSIX-conforming 'make'. +# For example, on Solaris 10 (2005), use /usr/sfw/bin/gmake or +# /usr/xpg4/bin/make, not /usr/ccs/bin/make. # To affect how this Makefile works, you can run a shell script like this: # -- 2.40.1
* tzselect.ksh (handle_entry): Initialize country_result and time each time through the loop, so that proper diagnostics are used even when they are set in the environment or the user rejected the first setting. --- tzselect.ksh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tzselect.ksh b/tzselect.ksh index e85259b2..19dcd43e 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -357,7 +357,9 @@ while continent= country= + country_result= region= + time= case $coord in ?*) -- 2.40.1
* tzselect.ksh (tz): Rename this shell variable from TZ, so that invoked commands don’t change behavior merely because tzselect is speculating about what TZ setting the user wants. This is a safety measure for oddball platforms where this might make a difference, as POSIX allows utilities to crash if TZ is bogus. --- tzselect.ksh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tzselect.ksh b/tzselect.ksh index 19dcd43e..3efb39fd 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -419,8 +419,8 @@ while 'AEST and is 10 hours' echo >&2 'ahead (east) of Greenwich,' \ 'with no daylight saving time.' - read TZ - $AWK -v TZ="$TZ" 'BEGIN { + read tz + $AWK -v tz="$tz" 'BEGIN { tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})" time = "(2[0-4]|[0-1]?[0-9])" \ "(:[0-5][0-9](:[0-5][0-9])?)?" @@ -431,13 +431,13 @@ while datetime = ",(" mdate "|" jdate ")(/" time ")?" tzpattern = "^(:.*|" tzname offset "(" tzname \ "(" offset ")?(" datetime datetime ")?)?)$" - if (TZ ~ tzpattern) exit 1 + if (tz ~ tzpattern) exit 1 exit 0 }' do - say >&2 "'$TZ' is not a conforming POSIX timezone string." + say >&2 "'$tz' is not a conforming POSIX timezone string." done - TZ_for_date=$TZ;; + TZ_for_date=$tz;; *) case $continent in coord) @@ -472,7 +472,7 @@ while "of distance from $coord". doselect $regions region=$select_result - TZ=`$AWK \ + tz=`$AWK \ -v distance_table="$distance_table" \ -v region="$region" ' BEGIN { @@ -594,8 +594,8 @@ while region=$select_result esac - # Determine TZ from country and region. - TZ=` + # Determine tz from country and region. + tz=` case $zone_table in file) cat -- "$TZ_ZONE_TABLE";; *) say "$zone_table";; @@ -622,7 +622,7 @@ while esac # Make sure the corresponding zoneinfo file exists. - TZ_for_date=$TZDIR/$TZ + TZ_for_date=$TZDIR/$tz <"$TZ_for_date" || { say >&2 "$0: time zone files are not set up correctly" exit 1 @@ -665,10 +665,10 @@ Universal Time is now: $UTdate." %?*%%) say >&2 " $country_result";; %%?*%?*) say >&2 " coord $coord$newline $region";; %%%?*) say >&2 " coord $coord";; - *) say >&2 " TZ='$TZ'" + *) say >&2 " TZ='$tz'" esac say >&2 "" - say >&2 "TZ='$TZ' will be used.$extra_info" + say >&2 "TZ='$tz' will be used.$extra_info" say >&2 "Is the above information OK?" doselect Yes No @@ -680,8 +680,8 @@ do coord= done case $SHELL in -*csh) file=.login line="setenv TZ '$TZ'";; -*) file=.profile line="TZ='$TZ'; export TZ" +*csh) file=.login line="setenv TZ '$tz'";; +*) file=.profile line="TZ='$tz'; export TZ" esac test -t 1 && say >&2 " @@ -692,4 +692,4 @@ to the file '$file' in your home directory; then log out and log in again. Here is that TZ value again, this time on standard output so that you can use the $0 command in shell scripts:" -say "$TZ" +say "$tz" -- 2.40.1
On 2023-12-19 15:25:55 (+0800), Paul Eggert via tz wrote:
* tzselect.ksh (tz): Rename this shell variable from TZ, so that invoked commands don’t change behavior merely because tzselect is speculating about what TZ setting the user wants. This is a safety measure for oddball platforms where this might make a difference, as POSIX allows utilities to crash if TZ is bogus.
Do such oddball platforms exist in the wild? To a casual reader, seeing $tz instead of $TZ may be confusing. Should we use $tz_for_date instead of $TZ_for_date for consistency? No objection to this. But it feels like refactoring for the sake of refactoring. Philip -- Philip Paeps Senior Reality Engineer Alternative Enterprises
On 2023-12-19 23:30, Philip Paeps wrote:
On 2023-12-19 15:25:55 (+0800), Paul Eggert via tz wrote:
* tzselect.ksh (tz): Rename this shell variable from TZ, so that invoked commands don’t change behavior merely because tzselect is speculating about what TZ setting the user wants. This is a safety measure for oddball platforms where this might make a difference, as POSIX allows utilities to crash if TZ is bogus.
Do such oddball platforms exist in the wild?
Yes, we've seen a report of such a problem with old FreeBSD versions, as well as in old tzcode versions. See, for example: https://mm.icann.org/pipermail/tz/1997-January/009772.html To be honest, though, I had forgotten about these old bug reports. I made the change mostly because it's better to be careful with TZ settings in a utility that's designed to let you choose TZ and that lets you specify the TZ string yourself.
To a casual reader, seeing $tz instead of $TZ may be confusing.
Yes, I renamed it that way mostly to avoid indenting changes....
Should we use $tz_for_date instead of $TZ_for_date for consistency?
I didn't make that change, mostly to avoid bloating the output of 'git diff' and thus making it easier to review. Could make the change later at some point; low priority of course.
* tzselect.ksh (countries): Use /^/, not //, as a regular expression that matches any line. POSIX does not allow empty REs, and Solaris 10 nawk rejects them. --- tzselect.ksh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tzselect.ksh b/tzselect.ksh index 3efb39fd..27bddfe0 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -530,7 +530,7 @@ while countries=` say "$zone_table" | $AWK \ - -v continent_re='' \ + -v continent_re='^' \ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ "$output_country_list" | sort -f -- 2.40.1
* NEWS: Mention this. * tzselect.ksh: Instead of ‘awk -v "VAR=VALUE" ...’, use ‘awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE"’. --- NEWS | 3 ++ tzselect.ksh | 83 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/NEWS b/NEWS index 25788bd7..bad81aa2 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,9 @@ Unreleased, experimental changes tzselect no longer mishandles spaces and most other special characters in BUGEMAIL, PACKAGE, TZDIR, and VERSION. + tzselect no longer mishandles ISO 6709 coordinates when using + an awk that does not support newlines in -v option-arguments. + zic no longer mishandles data for Palestine after the year 2075. Previously, it incorrectly omitted post-2075 transitions that are predicted for just before and just after Ramadan. (Thanks to Ken diff --git a/tzselect.ksh b/tzselect.ksh index 27bddfe0..1bef35ec 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -33,7 +33,12 @@ REPORT_BUGS_TO=tz@iana.org # Gawk (GNU awk) <https://www.gnu.org/software/gawk/> # mawk <https://invisible-island.net/mawk/> # nawk <https://github.com/onetrueawk/awk> - +# +# Because 'awk "VAR=VALUE" ...' and 'awk -v "VAR=VALUE" ...' are not portable +# if VALUE contains \, ", or newline, awk scripts in this file use: +# awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE" +# The substr avoids problems when VALUE is of the form X=Y and would be +# misinterpreted as an assignment. # Specify default values for environment variables if they are unset. : ${AWK=awk} @@ -199,7 +204,12 @@ IFS=$newline # Awk script to output a country list. output_country_list=' - BEGIN { FS = "\t" } + BEGIN { + continent_re = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + FS = "\t" + } /^#$/ { next } /^#[^@]/ { next } { @@ -249,6 +259,9 @@ output_country_list=' # and any apostrophes are escaped for the shell. output_distances_or_times=' BEGIN { + coord = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" FS = "\t" if (!output_times) { while (getline <TZ_COUNTRY_TABLE) @@ -420,7 +433,9 @@ while echo >&2 'ahead (east) of Greenwich,' \ 'with no daylight saving time.' read tz - $AWK -v tz="$tz" 'BEGIN { + $AWK 'BEGIN { + tz = substr(ARGV[1], 2) + ARGV[1] = "" tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})" time = "(2[0-4]|[0-1]?[0-9])" \ "(:[0-5][0-9](:[0-5][0-9])?)?" @@ -433,7 +448,7 @@ while "(" offset ")?(" datetime datetime ")?)?)$" if (tz ~ tzpattern) exit 1 exit 0 - }' + }' ="$tz" do say >&2 "'$tz' is not a conforming POSIX timezone string." done @@ -451,31 +466,32 @@ while read coord;; esac distance_table=`$AWK \ - -v coord="$coord" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ - "$output_distances_or_times" <"$TZ_ZONE_TABLE" | + "$output_distances_or_times" \ + ="$coord" ="$TZ_COUNTRY_TABLE" <"$TZ_ZONE_TABLE" | sort -n | sed "${location_limit}q" ` - regions=`$AWK \ - -v distance_table="$distance_table" ' + regions=`$AWK ' BEGIN { + distance_table = substr(ARGV[1], 2) + ARGV[1] = "" nlines = split(distance_table, line, /\n/) for (nr = 1; nr <= nlines; nr++) { nf = split(line[nr], f, /\t/) print f[nf] } } - '` + ' ="$distance_table"` echo >&2 'Please select one of the following timezones,' echo >&2 'listed roughly in increasing order' \ "of distance from $coord". doselect $regions region=$select_result - tz=`$AWK \ - -v distance_table="$distance_table" \ - -v region="$region" ' + tz=`$AWK ' BEGIN { + distance_table = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" nlines = split(distance_table, line, /\n/) for (nr = 1; nr <= nlines; nr++) { nf = split(line[nr], f, /\t/) @@ -484,7 +500,7 @@ while } } } - '` + ' ="$distance_table" ="$region"` ;; *) case $continent in @@ -493,9 +509,10 @@ while old_minute=`TZ=UTC0 date +"$minute_format"` for i in 1 2 3 do - time_table_command=` - $AWK -v output_times=1 \ - "$output_distances_or_times" <"$TZ_ZONE_TABLE" + time_table_command=`$AWK \ + -v output_times=1 \ + "$output_distances_or_times" \ + = = <"$TZ_ZONE_TABLE" ` time_table=`eval "$time_table_command"` new_minute=`TZ=UTC0 date +"$minute_format"` @@ -520,30 +537,28 @@ while time=$select_result zone_table=` say "$time_table" | - $AWK -v time="$time" '{ + $AWK 'BEGIN { time = substr(ARGV[1], 2); ARGV[1] = "" } { if ($6 " " $7 " " $4 " " $5 == time) { sub(/[^\t]*\t/, "") print } - }' + }' ="$time" ` countries=` say "$zone_table" | $AWK \ - -v continent_re='^' \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ - "$output_country_list" | + ="$output_country_list" ='^' ="$TZ_COUNTRY_TABLE" | sort -f ` ;; *) zone_table=file # Get list of names of countries in the continent or ocean. - countries=`$AWK \ - -v continent_re="^$continent/" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ + countries=` + $AWK \ "$output_country_list" \ - <"$TZ_ZONE_TABLE" | sort -f + "=^$continent/" ="$TZ_COUNTRY_TABLE" <"$TZ_ZONE_TABLE" | + sort -f `;; esac @@ -567,10 +582,11 @@ while *) say "$zone_table";; esac | $AWK \ - -v country="$country" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ ' BEGIN { + country = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" FS = "\t" cc = country while (getline <TZ_COUNTRY_TABLE) { @@ -582,7 +598,7 @@ while } /^#/ { next } $1 ~ cc { print $4 } - ' + ' ="$country" ="$TZ_COUNTRY_TABLE" ` @@ -601,11 +617,12 @@ while *) say "$zone_table";; esac | $AWK \ - -v country="$country" \ - -v region="$region" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ ' BEGIN { + country = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + TZ_COUNTRY_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" FS = "\t" cc = country while (getline <TZ_COUNTRY_TABLE) { @@ -617,7 +634,7 @@ while } /^#/ { next } $1 ~ cc && ($4 == region || !region) { print $3 } - ' + ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" `;; esac -- 2.40.1
* tzselect.ksh (check_POSIX_TZ_STRING): New var. Use it to work around a bug in mawk 1.3.4 2023-0730 <https://github.com/ThomasDickey/original-mawk/issues/72> as it uses /XXX+/ rather than /X{3,}/ in awk patterns. --- NEWS | 3 +++ tzselect.ksh | 35 +++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/NEWS b/NEWS index bad81aa2..a0d87bf6 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,9 @@ Unreleased, experimental changes tzselect no longer mishandles ISO 6709 coordinates when using an awk that does not support newlines in -v option-arguments. + tzselect no longer mishandles TZ strings when using mawk 1.4.3, + which mishandles regular expressions of the form /X{2,}/. + zic no longer mishandles data for Palestine after the year 2075. Previously, it incorrectly omitted post-2075 transitions that are predicted for just before and just after Ramadan. (Thanks to Ken diff --git a/tzselect.ksh b/tzselect.ksh index 1bef35ec..ffcec71b 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -425,6 +425,24 @@ while case $continent in TZ) # Ask the user for a POSIX TZ string. Check that it conforms. + check_POSIX_TZ_string='BEGIN { + tz = substr(ARGV[1], 2) + ARGV[1] = "" + tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \ + "|[[:alpha:]][[:alpha:]][[:alpha:]]+)") + time = ("(2[0-4]|[0-1]?[0-9])" \ + "(:[0-5][0-9](:[0-5][0-9])?)?") + offset = "[-+]?" time + mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" + jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \ + "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])") + datetime = ",(" mdate "|" jdate ")(/" time ")?" + tzpattern = ("^(:.*|" tzname offset "(" tzname \ + "(" offset ")?(" datetime datetime ")?)?)$") + if (tz ~ tzpattern) exit 1 + exit 0 + }' + while echo >&2 'Please enter the desired value' \ 'of the TZ environment variable.' @@ -433,22 +451,7 @@ while echo >&2 'ahead (east) of Greenwich,' \ 'with no daylight saving time.' read tz - $AWK 'BEGIN { - tz = substr(ARGV[1], 2) - ARGV[1] = "" - tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})" - time = "(2[0-4]|[0-1]?[0-9])" \ - "(:[0-5][0-9](:[0-5][0-9])?)?" - offset = "[-+]?" time - mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" - jdate = "((J[1-9]|[0-9]|J?[1-9][0-9]" \ - "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])" - datetime = ",(" mdate "|" jdate ")(/" time ")?" - tzpattern = "^(:.*|" tzname offset "(" tzname \ - "(" offset ")?(" datetime datetime ")?)?)$" - if (tz ~ tzpattern) exit 1 - exit 0 - }' ="$tz" + $AWK "$check_POSIX_TZ_string" ="$tz" do say >&2 "'$tz' is not a conforming POSIX timezone string." done -- 2.40.1
--- tzselect.ksh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tzselect.ksh b/tzselect.ksh index ffcec71b..cda902a7 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -22,13 +22,13 @@ REPORT_BUGS_TO=tz@iana.org # # For portability to Solaris 10 /bin/sh (supported by Oracle through # January 2025) this script avoids some POSIX features and common -# extensions, such as $(...) (which works sometimes but not others), -# $((...)), ! CMD, ${#ID}, ${ID##PAT}, ${ID%%PAT}, and $10. - +# extensions, such as $(...), $((...)), ! CMD, unquoted ^, ${#ID}, +# ${ID##PAT}, ${ID%%PAT}, and $10. Although some of these constructs +# work sometimes, it's simpler to avoid them entirely. # -# This script also uses several features of modern awk programs. +# This script also uses several features of POSIX awk. # If your host lacks awk, or has an old awk that does not conform to POSIX, -# you can use either of the following free programs instead: +# you can use any of the following free programs instead: # # Gawk (GNU awk) <https://www.gnu.org/software/gawk/> # mawk <https://invisible-island.net/mawk/> -- 2.40.1
* tzselect.ksh (translit): New var, to make things clearer. Use ot to determine whether to try to transliterate. 2023-12-12 Paul Eggert <eggert@cs.ucla.edu> * tzselect.ksh (TZ_COUNTRY_TABLE, TZ_ZONE_TABLE): Now strings, not file names. All uses changed. (translit): New var, so that iconv is not retried after failing. --- tzselect.ksh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tzselect.ksh b/tzselect.ksh index cda902a7..91ba572a 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -171,6 +171,15 @@ case $# in *) say >&2 "$0: $1: unknown argument"; exit 1 ;; esac +# translit=true to try transliteration. +# This is false if U+12345 CUNEIFORM SIGN URU TIMES KI has length 1 +# which means awk (and presumably the shell) do not need transliteration. +if $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) == 1 }'; then + translit=true +else + translit=false +fi + # Make sure the tables are readable. TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab @@ -185,7 +194,7 @@ done # If the current locale does not support UTF-8, convert data to current # locale's format if possible, as the shell aligns columns better that way. # Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI. -$AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) != 1 }' || { +$translit && { { tmp=`(mktemp -d) 2>/dev/null` || { tmp=${TMPDIR-/tmp}/tzselect.$$ && (umask 77 && mkdir -- "$tmp") -- 2.40.1
* NEWS: Mention this. * tzselect.ksh: If iconv does not support support the GNU //TRANSLIT extension, fall back on POSIX iconv. This is needed, for example, on Solaris 10. --- NEWS | 3 +++ tzselect.ksh | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index a0d87bf6..4360a4b6 100644 --- a/NEWS +++ b/NEWS @@ -41,6 +41,9 @@ Unreleased, experimental changes tzselect no longer mishandles TZ strings when using mawk 1.4.3, which mishandles regular expressions of the form /X{2,}/. + tzselect no longer mishandles non-UTF-8 locales on platforms + where the iconv command lacks the GNU //TRANSLIT extension. + zic no longer mishandles data for Palestine after the year 2075. Previously, it incorrectly omitted post-2075 transitions that are predicted for just before and just after Ramadan. (Thanks to Ken diff --git a/tzselect.ksh b/tzselect.ksh index 91ba572a..43d91799 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -200,8 +200,11 @@ $translit && { (umask 77 && mkdir -- "$tmp") };} && trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM && - (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \ - 2>/dev/null && + { (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \ + 2>/dev/null || + (iconv -f UTF-8 <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \ + 2>/dev/null + } && TZ_COUNTRY_TABLE=$tmp/iso3166.tab && iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab && TZ_ZONE_TABLE=$tmp/$zonetabtype.tab -- 2.40.1
--- NEWS | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 4360a4b6..a8b7972f 100644 --- a/NEWS +++ b/NEWS @@ -32,17 +32,19 @@ Unreleased, experimental changes DST was in effect before the transition too. (Thanks to Alois Treindl for debugging help.) - tzselect no longer mishandles spaces and most other special - characters in BUGEMAIL, PACKAGE, TZDIR, and VERSION. + tzselect no longer mishandles the following: - tzselect no longer mishandles ISO 6709 coordinates when using - an awk that does not support newlines in -v option-arguments. + Spaces and most other special characters in BUGEMAIL, PACKAGE, + TZDIR, and VERSION. - tzselect no longer mishandles TZ strings when using mawk 1.4.3, - which mishandles regular expressions of the form /X{2,}/. + TZ strings when using mawk 1.4.3, which mishandles regular + expressions of the form /X{2,}/. - tzselect no longer mishandles non-UTF-8 locales on platforms - where the iconv command lacks the GNU //TRANSLIT extension. + ISO 6709 coordinates when using an awk that lacks the GNU + extension of newlines in -v option-arguments. + + Non UTF-8 locales when using an iconv command that lacks the GNU + //TRANSLIT extension. zic no longer mishandles data for Palestine after the year 2075. Previously, it incorrectly omitted post-2075 transitions that are -- 2.40.1
* NEWS: Mention this. * tzselect.ksh (TZ_COUNTRY_TABLE, TZ_ZONE_TABLE): Now the contents of the files, not the file names. All uses changed. This lets us avoid the need to create temporary files; instead, we just update the variable contents. (read_file): New function, to implement this. (output_country_list, output_distances_or_times): Adjust to this. (sorted_table): New var, to help implement this. (continent_re): New var, to simplify country selection. --- NEWS | 2 + tzselect.ksh | 237 +++++++++++++++++++++++++++++---------------------- 2 files changed, 135 insertions(+), 104 deletions(-) diff --git a/NEWS b/NEWS index a8b7972f..f2d0bc3c 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,8 @@ Unreleased, experimental changes DST was in effect before the transition too. (Thanks to Alois Treindl for debugging help.) + tzselect no longer creates temporary files. + tzselect no longer mishandles the following: Spaces and most other special characters in BUGEMAIL, PACKAGE, diff --git a/tzselect.ksh b/tzselect.ksh index 43d91799..d2b3ecda 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -180,35 +180,23 @@ else translit=false fi -# Make sure the tables are readable. -TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab -TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab -for f in "$TZ_COUNTRY_TABLE" "$TZ_ZONE_TABLE" -do - <"$f" || { - say >&2 "$0: time zone files are not set up correctly" - exit 1 - } -done - -# If the current locale does not support UTF-8, convert data to current -# locale's format if possible, as the shell aligns columns better that way. -# Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI. -$translit && { - { tmp=`(mktemp -d) 2>/dev/null` || { - tmp=${TMPDIR-/tmp}/tzselect.$$ && - (umask 77 && mkdir -- "$tmp") - };} && - trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM && - { (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \ - 2>/dev/null || - (iconv -f UTF-8 <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \ - 2>/dev/null - } && - TZ_COUNTRY_TABLE=$tmp/iso3166.tab && - iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab && - TZ_ZONE_TABLE=$tmp/$zonetabtype.tab +# Read into shell variable $1 the contents of file $2. +# Convert to the current locale's encoding if possible, +# as the shell aligns columns better that way. +# If GNU iconv's //TRANSLIT does not work, fall back on POSIXish iconv; +# if that does not work, fall back on 'cat'. +read_file() { + { $translit && { + eval "$1=\`(iconv -f UTF-8 -t //TRANSLIT) 2>/dev/null <\"\$2\"\`" || + eval "$1=\`(iconv -f UTF-8) 2>/dev/null <\"\$2\"\`" + }; } || + eval "$1=\`cat <\"\$2\"\`" || { + say >&2 "$0: time zone files are not set up correctly" + exit 1 + } } +read_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab" +read_file TZ_ZONE_TABLE "$TZDIR/$zonetabtype.tab" newline=' ' @@ -219,14 +207,15 @@ output_country_list=' BEGIN { continent_re = substr(ARGV[1], 2) TZ_COUNTRY_TABLE = substr(ARGV[2], 2) - ARGV[1] = ARGV[2] = "" + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" FS = "\t" - } - /^#$/ { next } - /^#[^@]/ { next } - { + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (iline = 1; iline <= nlines; iline++) { + $0 = line[iline] commentary = $0 ~ /^#@/ if (commentary) { + if ($0 !~ /^#@/) continue col1ccs = substr($1, 3) conts = $2 } else { @@ -250,8 +239,10 @@ output_country_list=' } } } - END { - while (getline <TZ_COUNTRY_TABLE) { + { + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] if ($0 !~ /^#/) cc_name[$1] = $2 } for (i = 1; i <= ccs; i++) { @@ -263,9 +254,10 @@ output_country_list=' print country } } + } ' -# Awk script to read a time zone table and output the same table, +# Awk script to process a time zone table and output the same table, # with each row preceded by its distance from 'here'. # If output_times is set, each row is instead preceded by its local time # and any apostrophes are escaped for the shell. @@ -273,12 +265,16 @@ output_distances_or_times=' BEGIN { coord = substr(ARGV[1], 2) TZ_COUNTRY_TABLE = substr(ARGV[2], 2) - ARGV[1] = ARGV[2] = "" + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" FS = "\t" if (!output_times) { - while (getline <TZ_COUNTRY_TABLE) - if ($0 ~ /^[^#]/) - country[$1] = $2 + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) continue + country[$1] = $2 + } country["US"] = "US" # Otherwise the strings get too long. } } @@ -338,19 +334,20 @@ output_distances_or_times=' return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2) } BEGIN { - coord_lat = convert_latitude(coord) - coord_long = convert_longitude(coord) - } - /^[^#]/ { - inline[inlines++] = $0 - ncc = split($1, cc, /,/) - for (i = 1; i <= ncc; i++) - cc_used[cc[i]]++ - } - END { + coord_lat = convert_latitude(coord) + coord_long = convert_longitude(coord) + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (h = 1; h <= nlines; h++) { + $0 = line[h] + if ($0 ~ /^#/) continue + inline[inlines++] = $0 + ncc = split($1, cc, /,/) + for (i = 1; i <= ncc; i++) + cc_used[cc[i]]++ + } for (h = 0; h < inlines; h++) { $0 = inline[h] - line = $1 "\t" $2 "\t" $3 + outline = $1 "\t" $2 "\t" $3 sep = "\t" ncc = split($1, cc, /,/) split("", item_seen) @@ -358,17 +355,18 @@ output_distances_or_times=' for (i = 1; i <= ncc; i++) { item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4 if (item_seen[item]++) continue - line = line sep item + outline = outline sep item sep = "; " } if (output_times) { fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n" - gsub(/'\''/, "&\\\\&&", line) - printf fmt, $3, h, line + gsub(/'\''/, "&\\\\&&", outline) + printf fmt, $3, h, outline } else { here_lat = convert_latitude($2) here_long = convert_longitude($2) - printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line + printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), \ + outline } } } @@ -405,17 +403,23 @@ while entry = entry " Ocean" printf "'\''%s'\''\n", entry } - BEGIN { FS = "\t" } - /^[^#]/ { - handle_entry($3) - } - /^#@/ { - ncont = split($2, cont, /,/) - for (ci = 1; ci <= ncont; ci++) { - handle_entry(cont[ci]) + BEGIN { + TZ_ZONE_TABLE = substr(ARGV[1], 2) + ARGV[1] = "" + FS = "\t" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^[^#]/) + handle_entry($3) + else if ($0 ~ /^#@/) { + ncont = split($2, cont, /,/) + for (ci = 1; ci <= ncont; ci++) + handle_entry(cont[ci]) + } } } - ' <"$TZ_ZONE_TABLE" | + ' ="$TZ_ZONE_TABLE" | sort -u | tr '\n' ' ' echo '' @@ -482,7 +486,7 @@ while esac distance_table=`$AWK \ "$output_distances_or_times" \ - ="$coord" ="$TZ_COUNTRY_TABLE" <"$TZ_ZONE_TABLE" | + ="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" | sort -n | sed "${location_limit}q" ` @@ -527,7 +531,7 @@ while time_table_command=`$AWK \ -v output_times=1 \ "$output_distances_or_times" \ - = = <"$TZ_ZONE_TABLE" + = = ="$TZ_ZONE_TABLE" ` time_table=`eval "$time_table_command"` new_minute=`TZ=UTC0 date +"$minute_format"` @@ -538,44 +542,61 @@ while done echo >&2 "The system says Universal Time is $new_minute." echo >&2 "Assuming that's correct, what is the local time?" + sorted_table=`say "$time_table" | sort -k2n -k2,5 -k1n` || { + say >&2 "$0: cannot sort time table" + exit 1 + } eval doselect ` - say "$time_table" | - sort -k2n -k2,5 -k1n | - $AWK '{ - line = $6 " " $7 " " $4 " " $5 - if (line == oldline) next - oldline = line - gsub(/'\''/, "&\\\\&&", line) - printf "'\''%s'\''\n", line - }' + $AWK 'BEGIN { + sorted_table = substr(ARGV[1], 2) + ARGV[1] = "" + nlines = split(sorted_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + outline = $6 " " $7 " " $4 " " $5 + if (outline == oldline) continue + oldline = outline + gsub(/'\''/, "&\\\\&&", outline) + printf "'\''%s'\''\n", outline + } + }' ="$sorted_table" ` time=$select_result + continent_re='^' zone_table=` - say "$time_table" | - $AWK 'BEGIN { time = substr(ARGV[1], 2); ARGV[1] = "" } { + $AWK 'BEGIN { + time = substr(ARGV[1], 2) + time_table = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + nlines = split(time_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] if ($6 " " $7 " " $4 " " $5 == time) { sub(/[^\t]*\t/, "") print } - }' ="$time" + } + }' ="$time" ="$time_table" ` countries=` - say "$zone_table" | $AWK \ - ="$output_country_list" ='^' ="$TZ_COUNTRY_TABLE" | + "$output_country_list" \ + ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | sort -f ` ;; *) - zone_table=file - # Get list of names of countries in the continent or ocean. - countries=` + continent_re="^$continent/" + zone_table=$TZ_ZONE_TABLE + esac + + # Get list of names of countries in the continent or ocean. + countries=` $AWK \ "$output_country_list" \ - "=^$continent/" ="$TZ_COUNTRY_TABLE" <"$TZ_ZONE_TABLE" | + ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | sort -f - `;; - esac + ` # If there's more than one country, ask the user which one. case $countries in @@ -592,28 +613,32 @@ while # Get list of timezones in the country. regions=` - case $zone_table in - file) cat -- "$TZ_ZONE_TABLE";; - *) say "$zone_table";; - esac | $AWK \ ' BEGIN { country = substr(ARGV[1], 2) TZ_COUNTRY_TABLE = substr(ARGV[2], 2) - ARGV[1] = ARGV[2] = "" + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" FS = "\t" cc = country - while (getline <TZ_COUNTRY_TABLE) { + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] if ($0 !~ /^#/ && country == $2) { cc = $1 break } } + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) continue + if ($1 ~ cc) + print $4 + } } - /^#/ { next } - $1 ~ cc { print $4 } - ' ="$country" ="$TZ_COUNTRY_TABLE" + ' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table" ` @@ -627,29 +652,33 @@ while # Determine tz from country and region. tz=` - case $zone_table in - file) cat -- "$TZ_ZONE_TABLE";; - *) say "$zone_table";; - esac | $AWK \ ' BEGIN { country = substr(ARGV[1], 2) region = substr(ARGV[2], 2) TZ_COUNTRY_TABLE = substr(ARGV[3], 2) - ARGV[1] = ARGV[2] = ARGV[3] = "" + TZ_ZONE_TABLE = substr(ARGV[4], 2) + ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = "" FS = "\t" cc = country - while (getline <TZ_COUNTRY_TABLE) { + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] if ($0 !~ /^#/ && country == $2) { cc = $1 break } } + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) continue + if ($1 ~ cc && ($4 == region || !region)) + print $3 + } } - /^#/ { next } - $1 ~ cc && ($4 == region || !region) { print $3 } - ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" + ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table" `;; esac -- 2.40.1
* tzselect.ksh: Be more consistent about indenting two spaces for each level, and no space before ‘;;’. This changes only white space (you can verify with ‘diff -w’). --- tzselect.ksh | 920 +++++++++++++++++++++++++-------------------------- 1 file changed, 460 insertions(+), 460 deletions(-) diff --git a/tzselect.ksh b/tzselect.ksh index d2b3ecda..69d7e188 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -47,14 +47,14 @@ REPORT_BUGS_TO=tz@iana.org # Output one argument as-is to standard output, with trailing newline. # Safer than 'echo', which can mishandle '\' or leading '-'. say() { - printf '%s\n' "$1" + printf '%s\n' "$1" } # Check for awk POSIX compliance. ($AWK -v x=y 'BEGIN { exit 123 }') <>/dev/null >&0 2>&0 [ $? = 123 ] || { - say >&2 "$0: Sorry, your '$AWK' program is not POSIX compatible." - exit 1 + say >&2 "$0: Sorry, your '$AWK' program is not POSIX compatible." + exit 1 } coord= @@ -91,7 +91,7 @@ Report bugs to $REPORT_BUGS_TO." # available, falling back on a portable substitute otherwise. if case $BASH_VERSION in - ?*) : ;; + ?*) :;; '') # '; exit' should be redundant, but Dash doesn't properly fail without it. (eval 'set --; select x; do break; done; exit') <>/dev/null 2>&0 @@ -104,7 +104,7 @@ then select select_result do case $select_result in - "") echo >&2 "Please enter a number in range." ;; + "") echo >&2 "Please enter a number in range.";; ?*) break esac done || exit @@ -126,9 +126,9 @@ else do select_i=`expr $select_i + 1` printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word" - done ;; + done;; *[!0-9]*) - echo >&2 'Please enter a number in range.' ;; + echo >&2 'Please enter a number in range.';; *) if test 1 -le $select_i && test $select_i -le $#; then shift `expr $select_i - 1` @@ -147,37 +147,37 @@ fi while getopts c:n:t:-: opt do - case $opt$OPTARG in - c*) - coord=$OPTARG ;; - n*) - location_limit=$OPTARG ;; - t*) # Undocumented option, used for developer testing. - zonetabtype=$OPTARG ;; - -help) - exec echo "$usage" ;; - -version) - exec echo "tzselect $PKGVERSION$TZVERSION" ;; - -*) - say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;; - *) - say >&2 "$0: try '$0 --help'"; exit 1 ;; - esac + case $opt$OPTARG in + c*) + coord=$OPTARG;; + n*) + location_limit=$OPTARG;; + t*) # Undocumented option, used for developer testing. + zonetabtype=$OPTARG;; + -help) + exec echo "$usage";; + -version) + exec echo "tzselect $PKGVERSION$TZVERSION";; + -*) + say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1;; + *) + say >&2 "$0: try '$0 --help'"; exit 1;; + esac done shift `expr $OPTIND - 1` case $# in 0) ;; -*) say >&2 "$0: $1: unknown argument"; exit 1 ;; +*) say >&2 "$0: $1: unknown argument"; exit 1;; esac # translit=true to try transliteration. # This is false if U+12345 CUNEIFORM SIGN URU TIMES KI has length 1 # which means awk (and presumably the shell) do not need transliteration. if $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) == 1 }'; then - translit=true + translit=true else - translit=false + translit=false fi # Read into shell variable $1 the contents of file $2. @@ -186,14 +186,14 @@ fi # If GNU iconv's //TRANSLIT does not work, fall back on POSIXish iconv; # if that does not work, fall back on 'cat'. read_file() { - { $translit && { - eval "$1=\`(iconv -f UTF-8 -t //TRANSLIT) 2>/dev/null <\"\$2\"\`" || - eval "$1=\`(iconv -f UTF-8) 2>/dev/null <\"\$2\"\`" - }; } || - eval "$1=\`cat <\"\$2\"\`" || { - say >&2 "$0: time zone files are not set up correctly" - exit 1 - } + { $translit && { + eval "$1=\`(iconv -f UTF-8 -t //TRANSLIT) 2>/dev/null <\"\$2\"\`" || + eval "$1=\`(iconv -f UTF-8) 2>/dev/null <\"\$2\"\`" + }; } || + eval "$1=\`cat <\"\$2\"\`" || { + say >&2 "$0: time zone files are not set up correctly" + exit 1 + } } read_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab" read_file TZ_ZONE_TABLE "$TZDIR/$zonetabtype.tab" @@ -204,57 +204,57 @@ IFS=$newline # Awk script to output a country list. output_country_list=' - BEGIN { - continent_re = substr(ARGV[1], 2) - TZ_COUNTRY_TABLE = substr(ARGV[2], 2) - TZ_ZONE_TABLE = substr(ARGV[3], 2) - ARGV[1] = ARGV[2] = ARGV[3] = "" - FS = "\t" - nlines = split(TZ_ZONE_TABLE, line, /\n/) - for (iline = 1; iline <= nlines; iline++) { - $0 = line[iline] - commentary = $0 ~ /^#@/ - if (commentary) { - if ($0 !~ /^#@/) continue - col1ccs = substr($1, 3) - conts = $2 - } else { - col1ccs = $1 - conts = $3 - } - ncc = split(col1ccs, cc, /,/) - ncont = split(conts, cont, /,/) - for (i = 1; i <= ncc; i++) { - elsewhere = commentary - for (ci = 1; ci <= ncont; ci++) { - if (cont[ci] ~ continent_re) { - if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i] - elsewhere = 0 - } + BEGIN { + continent_re = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" + FS = "\t" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (iline = 1; iline <= nlines; iline++) { + $0 = line[iline] + commentary = $0 ~ /^#@/ + if (commentary) { + if ($0 !~ /^#@/) continue + col1ccs = substr($1, 3) + conts = $2 + } else { + col1ccs = $1 + conts = $3 } - if (elsewhere) { - for (i = 1; i <= ncc; i++) { - cc_elsewhere[cc[i]] = 1 + ncc = split(col1ccs, cc, /,/) + ncont = split(conts, cont, /,/) + for (i = 1; i <= ncc; i++) { + elsewhere = commentary + for (ci = 1; ci <= ncont; ci++) { + if (cont[ci] ~ continent_re) { + if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i] + elsewhere = 0 + } + } + if (elsewhere) { + for (i = 1; i <= ncc; i++) { + cc_elsewhere[cc[i]] = 1 + } } } } + { + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 !~ /^#/) cc_name[$1] = $2 + } + for (i = 1; i <= ccs; i++) { + country = cc_list[i] + if (cc_elsewhere[country]) continue + if (cc_name[country]) { + country = cc_name[country] + } + print country + } + } } - { - nlines = split(TZ_COUNTRY_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 !~ /^#/) cc_name[$1] = $2 - } - for (i = 1; i <= ccs; i++) { - country = cc_list[i] - if (cc_elsewhere[country]) continue - if (cc_name[country]) { - country = cc_name[country] - } - print country - } - } - } ' # Awk script to process a time zone table and output the same table, @@ -334,415 +334,415 @@ output_distances_or_times=' return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2) } BEGIN { - coord_lat = convert_latitude(coord) - coord_long = convert_longitude(coord) - nlines = split(TZ_ZONE_TABLE, line, /\n/) - for (h = 1; h <= nlines; h++) { - $0 = line[h] - if ($0 ~ /^#/) continue - inline[inlines++] = $0 - ncc = split($1, cc, /,/) - for (i = 1; i <= ncc; i++) - cc_used[cc[i]]++ - } - for (h = 0; h < inlines; h++) { - $0 = inline[h] - outline = $1 "\t" $2 "\t" $3 - sep = "\t" - ncc = split($1, cc, /,/) - split("", item_seen) - item_seen[""] = 1 - for (i = 1; i <= ncc; i++) { - item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4 - if (item_seen[item]++) continue - outline = outline sep item - sep = "; " + coord_lat = convert_latitude(coord) + coord_long = convert_longitude(coord) + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (h = 1; h <= nlines; h++) { + $0 = line[h] + if ($0 ~ /^#/) continue + inline[inlines++] = $0 + ncc = split($1, cc, /,/) + for (i = 1; i <= ncc; i++) + cc_used[cc[i]]++ } - if (output_times) { - fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n" - gsub(/'\''/, "&\\\\&&", outline) - printf fmt, $3, h, outline - } else { - here_lat = convert_latitude($2) - here_long = convert_longitude($2) - printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), \ - outline + for (h = 0; h < inlines; h++) { + $0 = inline[h] + outline = $1 "\t" $2 "\t" $3 + sep = "\t" + ncc = split($1, cc, /,/) + split("", item_seen) + item_seen[""] = 1 + for (i = 1; i <= ncc; i++) { + item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4 + if (item_seen[item]++) continue + outline = outline sep item + sep = "; " + } + if (output_times) { + fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n" + gsub(/'\''/, "&\\\\&&", outline) + printf fmt, $3, h, outline + } else { + here_lat = convert_latitude($2) + here_long = convert_longitude($2) + printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), \ + outline + } } - } } ' # Begin the main loop. We come back here if the user wants to retry. while - echo >&2 'Please identify a location' \ - 'so that time zone rules can be set correctly.' - - continent= - country= - country_result= - region= - time= - - case $coord in - ?*) - continent=coord;; - '') - - # Ask the user for continent or ocean. - - echo >&2 'Please select a continent, ocean, "coord", "TZ", or "time".' - - quoted_continents=` - $AWK ' - function handle_entry(entry) { - entry = substr(entry, 1, index(entry, "/") - 1) - if (entry == "America") - entry = entry "s" - if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/) - entry = entry " Ocean" - printf "'\''%s'\''\n", entry + echo >&2 'Please identify a location' \ + 'so that time zone rules can be set correctly.' + + continent= + country= + country_result= + region= + time= + + case $coord in + ?*) + continent=coord;; + '') + + # Ask the user for continent or ocean. + + echo >&2 'Please select a continent, ocean, "coord", "TZ", or "time".' + + quoted_continents=` + $AWK ' + function handle_entry(entry) { + entry = substr(entry, 1, index(entry, "/") - 1) + if (entry == "America") + entry = entry "s" + if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/) + entry = entry " Ocean" + printf "'\''%s'\''\n", entry + } + BEGIN { + TZ_ZONE_TABLE = substr(ARGV[1], 2) + ARGV[1] = "" + FS = "\t" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^[^#]/) + handle_entry($3) + else if ($0 ~ /^#@/) { + ncont = split($2, cont, /,/) + for (ci = 1; ci <= ncont; ci++) + handle_entry(cont[ci]) + } + } + } + ' ="$TZ_ZONE_TABLE" | + sort -u | + tr '\n' ' ' + echo '' + ` + + eval ' + doselect '"$quoted_continents"' \ + "coord - I want to use geographical coordinates." \ + "TZ - I want to specify the timezone using the POSIX TZ format." \ + "time - I know local time already." + continent=$select_result + case $continent in + Americas) continent=America;; + *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''` + esac + ' + esac + + case $continent in + TZ) + # Ask the user for a POSIX TZ string. Check that it conforms. + check_POSIX_TZ_string='BEGIN { + tz = substr(ARGV[1], 2) + ARGV[1] = "" + tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \ + "|[[:alpha:]][[:alpha:]][[:alpha:]]+)") + time = ("(2[0-4]|[0-1]?[0-9])" \ + "(:[0-5][0-9](:[0-5][0-9])?)?") + offset = "[-+]?" time + mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" + jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \ + "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])") + datetime = ",(" mdate "|" jdate ")(/" time ")?" + tzpattern = ("^(:.*|" tzname offset "(" tzname \ + "(" offset ")?(" datetime datetime ")?)?)$") + if (tz ~ tzpattern) exit 1 + exit 0 + }' + + while + echo >&2 'Please enter the desired value' \ + 'of the TZ environment variable.' + echo >&2 'For example, AEST-10 is abbreviated' \ + 'AEST and is 10 hours' + echo >&2 'ahead (east) of Greenwich,' \ + 'with no daylight saving time.' + read tz + $AWK "$check_POSIX_TZ_string" ="$tz" + do + say >&2 "'$tz' is not a conforming POSIX timezone string." + done + TZ_for_date=$tz;; + *) + case $continent in + coord) + case $coord in + '') + echo >&2 'Please enter coordinates' \ + 'in ISO 6709 notation.' + echo >&2 'For example, +4042-07403 stands for' + echo >&2 '40 degrees 42 minutes north,' \ + '74 degrees 3 minutes west.' + read coord;; + esac + distance_table=`$AWK \ + "$output_distances_or_times" \ + ="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" | + sort -n | + sed "${location_limit}q" + ` + regions=`$AWK ' + BEGIN { + distance_table = substr(ARGV[1], 2) + ARGV[1] = "" + nlines = split(distance_table, line, /\n/) + for (nr = 1; nr <= nlines; nr++) { + nf = split(line[nr], f, /\t/) + print f[nf] + } + } + ' ="$distance_table"` + echo >&2 'Please select one of the following timezones,' + echo >&2 'listed roughly in increasing order' \ + "of distance from $coord". + doselect $regions + region=$select_result + tz=`$AWK ' + BEGIN { + distance_table = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + nlines = split(distance_table, line, /\n/) + for (nr = 1; nr <= nlines; nr++) { + nf = split(line[nr], f, /\t/) + if (f[nf] == region) { + print f[4] + } + } + } + ' ="$distance_table" ="$region"` + ;; + *) + case $continent in + time) + minute_format='%a %b %d %H:%M' + old_minute=`TZ=UTC0 date +"$minute_format"` + for i in 1 2 3 + do + time_table_command=`$AWK \ + -v output_times=1 \ + "$output_distances_or_times" \ + = = ="$TZ_ZONE_TABLE" + ` + time_table=`eval "$time_table_command"` + new_minute=`TZ=UTC0 date +"$minute_format"` + case $old_minute in + "$new_minute") break;; + esac + old_minute=$new_minute + done + echo >&2 "The system says Universal Time is $new_minute." + echo >&2 "Assuming that's correct, what is the local time?" + sorted_table=`say "$time_table" | sort -k2n -k2,5 -k1n` || { + say >&2 "$0: cannot sort time table" + exit 1 + } + eval doselect ` + $AWK 'BEGIN { + sorted_table = substr(ARGV[1], 2) + ARGV[1] = "" + nlines = split(sorted_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + outline = $6 " " $7 " " $4 " " $5 + if (outline == oldline) continue + oldline = outline + gsub(/'\''/, "&\\\\&&", outline) + printf "'\''%s'\''\n", outline + } + }' ="$sorted_table" + ` + time=$select_result + continent_re='^' + zone_table=` + $AWK 'BEGIN { + time = substr(ARGV[1], 2) + time_table = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + nlines = split(time_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($6 " " $7 " " $4 " " $5 == time) { + sub(/[^\t]*\t/, "") + print + } } + }' ="$time" ="$time_table" + ` + countries=` + $AWK \ + "$output_country_list" \ + ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | + sort -f + ` + ;; + *) + continent_re="^$continent/" + zone_table=$TZ_ZONE_TABLE + esac + + # Get list of names of countries in the continent or ocean. + countries=` + $AWK \ + "$output_country_list" \ + ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | + sort -f + ` + + # If there's more than one country, ask the user which one. + case $countries in + *"$newline"*) + echo >&2 'Please select a country' \ + 'whose clocks agree with yours.' + doselect $countries + country_result=$select_result + country=$select_result;; + *) + country=$countries + esac + + + # Get list of timezones in the country. + regions=` + $AWK \ + ' BEGIN { - TZ_ZONE_TABLE = substr(ARGV[1], 2) - ARGV[1] = "" + country = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" FS = "\t" - nlines = split(TZ_ZONE_TABLE, line, /\n/) + cc = country + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) for (i = 1; i <= nlines; i++) { $0 = line[i] - if ($0 ~ /^[^#]/) - handle_entry($3) - else if ($0 ~ /^#@/) { - ncont = split($2, cont, /,/) - for (ci = 1; ci <= ncont; ci++) - handle_entry(cont[ci]) + if ($0 !~ /^#/ && country == $2) { + cc = $1 + break } } + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) continue + if ($1 ~ cc) + print $4 + } } - ' ="$TZ_ZONE_TABLE" | - sort -u | - tr '\n' ' ' - echo '' - ` + ' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table" + ` - eval ' - doselect '"$quoted_continents"' \ - "coord - I want to use geographical coordinates." \ - "TZ - I want to specify the timezone using the POSIX TZ format." \ - "time - I know local time already." - continent=$select_result - case $continent in - Americas) continent=America;; - *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''` - esac - ' - esac - case $continent in - TZ) - # Ask the user for a POSIX TZ string. Check that it conforms. - check_POSIX_TZ_string='BEGIN { - tz = substr(ARGV[1], 2) - ARGV[1] = "" - tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \ - "|[[:alpha:]][[:alpha:]][[:alpha:]]+)") - time = ("(2[0-4]|[0-1]?[0-9])" \ - "(:[0-5][0-9](:[0-5][0-9])?)?") - offset = "[-+]?" time - mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" - jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \ - "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])") - datetime = ",(" mdate "|" jdate ")(/" time ")?" - tzpattern = ("^(:.*|" tzname offset "(" tzname \ - "(" offset ")?(" datetime datetime ")?)?)$") - if (tz ~ tzpattern) exit 1 - exit 0 - }' - - while - echo >&2 'Please enter the desired value' \ - 'of the TZ environment variable.' - echo >&2 'For example, AEST-10 is abbreviated' \ - 'AEST and is 10 hours' - echo >&2 'ahead (east) of Greenwich,' \ - 'with no daylight saving time.' - read tz - $AWK "$check_POSIX_TZ_string" ="$tz" - do - say >&2 "'$tz' is not a conforming POSIX timezone string." - done - TZ_for_date=$tz;; - *) - case $continent in - coord) - case $coord in - '') - echo >&2 'Please enter coordinates' \ - 'in ISO 6709 notation.' - echo >&2 'For example, +4042-07403 stands for' - echo >&2 '40 degrees 42 minutes north,' \ - '74 degrees 3 minutes west.' - read coord;; - esac - distance_table=`$AWK \ - "$output_distances_or_times" \ - ="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" | - sort -n | - sed "${location_limit}q" - ` - regions=`$AWK ' - BEGIN { - distance_table = substr(ARGV[1], 2) - ARGV[1] = "" - nlines = split(distance_table, line, /\n/) - for (nr = 1; nr <= nlines; nr++) { - nf = split(line[nr], f, /\t/) - print f[nf] - } - } - ' ="$distance_table"` - echo >&2 'Please select one of the following timezones,' - echo >&2 'listed roughly in increasing order' \ - "of distance from $coord". - doselect $regions - region=$select_result - tz=`$AWK ' - BEGIN { - distance_table = substr(ARGV[1], 2) - region = substr(ARGV[2], 2) - ARGV[1] = ARGV[2] = "" - nlines = split(distance_table, line, /\n/) - for (nr = 1; nr <= nlines; nr++) { - nf = split(line[nr], f, /\t/) - if (f[nf] == region) { - print f[4] - } - } - } - ' ="$distance_table" ="$region"` - ;; - *) - case $continent in - time) - minute_format='%a %b %d %H:%M' - old_minute=`TZ=UTC0 date +"$minute_format"` - for i in 1 2 3 - do - time_table_command=`$AWK \ - -v output_times=1 \ - "$output_distances_or_times" \ - = = ="$TZ_ZONE_TABLE" - ` - time_table=`eval "$time_table_command"` - new_minute=`TZ=UTC0 date +"$minute_format"` - case $old_minute in - "$new_minute") break;; - esac - old_minute=$new_minute - done - echo >&2 "The system says Universal Time is $new_minute." - echo >&2 "Assuming that's correct, what is the local time?" - sorted_table=`say "$time_table" | sort -k2n -k2,5 -k1n` || { - say >&2 "$0: cannot sort time table" - exit 1 - } - eval doselect ` - $AWK 'BEGIN { - sorted_table = substr(ARGV[1], 2) - ARGV[1] = "" - nlines = split(sorted_table, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - outline = $6 " " $7 " " $4 " " $5 - if (outline == oldline) continue - oldline = outline - gsub(/'\''/, "&\\\\&&", outline) - printf "'\''%s'\''\n", outline - } - }' ="$sorted_table" - ` - time=$select_result - continent_re='^' - zone_table=` - $AWK 'BEGIN { - time = substr(ARGV[1], 2) - time_table = substr(ARGV[2], 2) - ARGV[1] = ARGV[2] = "" - nlines = split(time_table, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($6 " " $7 " " $4 " " $5 == time) { - sub(/[^\t]*\t/, "") - print - } - } - }' ="$time" ="$time_table" - ` - countries=` - $AWK \ - "$output_country_list" \ - ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | - sort -f - ` - ;; - *) - continent_re="^$continent/" - zone_table=$TZ_ZONE_TABLE - esac - - # Get list of names of countries in the continent or ocean. - countries=` - $AWK \ - "$output_country_list" \ - ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | - sort -f - ` - - # If there's more than one country, ask the user which one. - case $countries in - *"$newline"*) - echo >&2 'Please select a country' \ - 'whose clocks agree with yours.' - doselect $countries - country_result=$select_result - country=$select_result;; - *) - country=$countries - esac - - - # Get list of timezones in the country. - regions=` - $AWK \ - ' - BEGIN { - country = substr(ARGV[1], 2) - TZ_COUNTRY_TABLE = substr(ARGV[2], 2) - TZ_ZONE_TABLE = substr(ARGV[3], 2) - ARGV[1] = ARGV[2] = ARGV[3] = "" - FS = "\t" - cc = country - nlines = split(TZ_COUNTRY_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 !~ /^#/ && country == $2) { - cc = $1 - break - } - } - nlines = split(TZ_ZONE_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 ~ /^#/) continue - if ($1 ~ cc) - print $4 - } - } - ' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table" - ` - - - # If there's more than one region, ask the user which one. - case $regions in - *"$newline"*) - echo >&2 'Please select one of the following timezones.' - doselect $regions - region=$select_result - esac - - # Determine tz from country and region. - tz=` - $AWK \ - ' - BEGIN { - country = substr(ARGV[1], 2) - region = substr(ARGV[2], 2) - TZ_COUNTRY_TABLE = substr(ARGV[3], 2) - TZ_ZONE_TABLE = substr(ARGV[4], 2) - ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = "" - FS = "\t" - cc = country - nlines = split(TZ_COUNTRY_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 !~ /^#/ && country == $2) { - cc = $1 - break - } - } - nlines = split(TZ_ZONE_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 ~ /^#/) continue - if ($1 ~ cc && ($4 == region || !region)) - print $3 - } - } - ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table" - `;; - esac - - # Make sure the corresponding zoneinfo file exists. - TZ_for_date=$TZDIR/$tz - <"$TZ_for_date" || { - say >&2 "$0: time zone files are not set up correctly" - exit 1 + # If there's more than one region, ask the user which one. + case $regions in + *"$newline"*) + echo >&2 'Please select one of the following timezones.' + doselect $regions + region=$select_result + esac + + # Determine tz from country and region. + tz=` + $AWK \ + ' + BEGIN { + country = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + TZ_COUNTRY_TABLE = substr(ARGV[3], 2) + TZ_ZONE_TABLE = substr(ARGV[4], 2) + ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = "" + FS = "\t" + cc = country + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 !~ /^#/ && country == $2) { + cc = $1 + break } - esac + } + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) continue + if ($1 ~ cc && ($4 == region || !region)) + print $3 + } + } + ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table" + `;; + esac + # Make sure the corresponding zoneinfo file exists. + TZ_for_date=$TZDIR/$tz + <"$TZ_for_date" || { + say >&2 "$0: time zone files are not set up correctly" + exit 1 + } + esac - # Use the proposed TZ to output the current date relative to UTC. - # Loop until they agree in seconds. - # Give up after 8 unsuccessful tries. - extra_info= - for i in 1 2 3 4 5 6 7 8 - do - TZdate=`LANG=C TZ="$TZ_for_date" date` - UTdate=`LANG=C TZ=UTC0 date` - TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'` - UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'` - case $TZsec in - $UTsec) - extra_info=" + # Use the proposed TZ to output the current date relative to UTC. + # Loop until they agree in seconds. + # Give up after 8 unsuccessful tries. + + extra_info= + for i in 1 2 3 4 5 6 7 8 + do + TZdate=`LANG=C TZ="$TZ_for_date" date` + UTdate=`LANG=C TZ=UTC0 date` + TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'` + UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'` + case $TZsec in + $UTsec) + extra_info=" Selected time is now: $TZdate. Universal Time is now: $UTdate." - break - esac - done - - - # Output TZ info and ask the user to confirm. - - echo >&2 "" - echo >&2 "Based on the following information:" - echo >&2 "" - case $time%$country_result%$region%$coord in - ?*%?*%?*%) - say >&2 " $time$newline $country_result$newline $region";; - ?*%?*%%|?*%%?*%) say >&2 " $time$newline $country_result$region";; - ?*%%%) say >&2 " $time";; - %?*%?*%) say >&2 " $country_result$newline $region";; - %?*%%) say >&2 " $country_result";; - %%?*%?*) say >&2 " coord $coord$newline $region";; - %%%?*) say >&2 " coord $coord";; - *) say >&2 " TZ='$tz'" - esac - say >&2 "" - say >&2 "TZ='$tz' will be used.$extra_info" - say >&2 "Is the above information OK?" - - doselect Yes No - ok=$select_result - case $ok in - Yes) break - esac + break + esac + done + + + # Output TZ info and ask the user to confirm. + + echo >&2 "" + echo >&2 "Based on the following information:" + echo >&2 "" + case $time%$country_result%$region%$coord in + ?*%?*%?*%) + say >&2 " $time$newline $country_result$newline $region";; + ?*%?*%%|?*%%?*%) say >&2 " $time$newline $country_result$region";; + ?*%%%) say >&2 " $time";; + %?*%?*%) say >&2 " $country_result$newline $region";; + %?*%%) say >&2 " $country_result";; + %%?*%?*) say >&2 " coord $coord$newline $region";; + %%%?*) say >&2 " coord $coord";; + *) say >&2 " TZ='$tz'" + esac + say >&2 "" + say >&2 "TZ='$tz' will be used.$extra_info" + say >&2 "Is the above information OK?" + + doselect Yes No + ok=$select_result + case $ok in + Yes) break + esac do coord= done case $SHELL in *csh) file=.login line="setenv TZ '$tz'";; -*) file=.profile line="TZ='$tz'; export TZ" +*) file=.profile line="TZ='$tz'; export TZ" esac test -t 1 && say >&2 " -- 2.40.1
--- tzselect.ksh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tzselect.ksh b/tzselect.ksh index 69d7e188..8c8ef1f5 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -161,14 +161,14 @@ do -*) say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1;; *) - say >&2 "$0: try '$0 --help'"; exit 1;; + say >&2 "$0: try '$0 --help'"; exit 1 esac done shift `expr $OPTIND - 1` case $# in 0) ;; -*) say >&2 "$0: $1: unknown argument"; exit 1;; +*) say >&2 "$0: $1: unknown argument"; exit 1 esac # translit=true to try transliteration. @@ -482,7 +482,7 @@ while echo >&2 'For example, +4042-07403 stands for' echo >&2 '40 degrees 42 minutes north,' \ '74 degrees 3 minutes west.' - read coord;; + read coord esac distance_table=`$AWK \ "$output_distances_or_times" \ @@ -536,7 +536,7 @@ while time_table=`eval "$time_table_command"` new_minute=`TZ=UTC0 date +"$minute_format"` case $old_minute in - "$new_minute") break;; + "$new_minute") break esac old_minute=$new_minute done @@ -679,7 +679,7 @@ while } } ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table" - `;; + ` esac # Make sure the corresponding zoneinfo file exists. -- 2.40.1
These indenting changes aren’t ignored by ‘diff -b’ but are still helpful in making the code more consistent. --- tzselect.ksh | 277 ++++++++++++++++++++++++++------------------------- 1 file changed, 144 insertions(+), 133 deletions(-) diff --git a/tzselect.ksh b/tzselect.ksh index 8c8ef1f5..f894bb56 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -215,7 +215,8 @@ output_country_list=' $0 = line[iline] commentary = $0 ~ /^#@/ if (commentary) { - if ($0 !~ /^#@/) continue + if ($0 !~ /^#@/) + continue col1ccs = substr($1, 3) conts = $2 } else { @@ -228,32 +229,30 @@ output_country_list=' elsewhere = commentary for (ci = 1; ci <= ncont; ci++) { if (cont[ci] ~ continent_re) { - if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i] + if (!cc_seen[cc[i]]++) + cc_list[++ccs] = cc[i] elsewhere = 0 } } - if (elsewhere) { - for (i = 1; i <= ncc; i++) { + if (elsewhere) + for (i = 1; i <= ncc; i++) cc_elsewhere[cc[i]] = 1 - } - } } } - { nlines = split(TZ_COUNTRY_TABLE, line, /\n/) for (i = 1; i <= nlines; i++) { $0 = line[i] - if ($0 !~ /^#/) cc_name[$1] = $2 + if ($0 !~ /^#/) + cc_name[$1] = $2 } for (i = 1; i <= ccs; i++) { country = cc_list[i] - if (cc_elsewhere[country]) continue - if (cc_name[country]) { + if (cc_elsewhere[country]) + continue + if (cc_name[country]) country = cc_name[country] - } print country } - } } ' @@ -272,7 +271,8 @@ output_distances_or_times=' nlines = split(TZ_COUNTRY_TABLE, line, /\n/) for (i = 1; i <= nlines; i++) { $0 = line[i] - if ($0 ~ /^#/) continue + if ($0 ~ /^#/) + continue country[$1] = $2 } country["US"] = "US" # Otherwise the strings get too long. @@ -339,7 +339,8 @@ output_distances_or_times=' nlines = split(TZ_ZONE_TABLE, line, /\n/) for (h = 1; h <= nlines; h++) { $0 = line[h] - if ($0 ~ /^#/) continue + if ($0 ~ /^#/) + continue inline[inlines++] = $0 ncc = split($1, cc, /,/) for (i = 1; i <= ncc; i++) @@ -354,7 +355,8 @@ output_distances_or_times=' item_seen[""] = 1 for (i = 1; i <= ncc; i++) { item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4 - if (item_seen[item]++) continue + if (item_seen[item]++) + continue outline = outline sep item sep = "; " } @@ -441,23 +443,24 @@ while case $continent in TZ) # Ask the user for a POSIX TZ string. Check that it conforms. - check_POSIX_TZ_string='BEGIN { - tz = substr(ARGV[1], 2) - ARGV[1] = "" - tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \ - "|[[:alpha:]][[:alpha:]][[:alpha:]]+)") - time = ("(2[0-4]|[0-1]?[0-9])" \ - "(:[0-5][0-9](:[0-5][0-9])?)?") - offset = "[-+]?" time - mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" - jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \ - "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])") - datetime = ",(" mdate "|" jdate ")(/" time ")?" - tzpattern = ("^(:.*|" tzname offset "(" tzname \ - "(" offset ")?(" datetime datetime ")?)?)$") - if (tz ~ tzpattern) exit 1 - exit 0 - }' + check_POSIX_TZ_string=' + BEGIN { + tz = substr(ARGV[1], 2) + ARGV[1] = "" + tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \ + "|[[:alpha:]][[:alpha:]][[:alpha:]]+)") + time = ("(2[0-4]|[0-1]?[0-9])" \ + "(:[0-5][0-9](:[0-5][0-9])?)?") + offset = "[-+]?" time + mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" + jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \ + "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])") + datetime = ",(" mdate "|" jdate ")(/" time ")?" + tzpattern = ("^(:.*|" tzname offset "(" tzname \ + "(" offset ")?(" datetime datetime ")?)?)$") + exit tz ~ tzpattern + } + ' while echo >&2 'Please enter the desired value' \ @@ -484,43 +487,46 @@ while '74 degrees 3 minutes west.' read coord esac - distance_table=`$AWK \ + distance_table=` + $AWK \ "$output_distances_or_times" \ ="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" | sort -n | sed "${location_limit}q" ` - regions=`$AWK ' - BEGIN { - distance_table = substr(ARGV[1], 2) - ARGV[1] = "" - nlines = split(distance_table, line, /\n/) - for (nr = 1; nr <= nlines; nr++) { - nf = split(line[nr], f, /\t/) - print f[nf] + regions=` + $AWK ' + BEGIN { + distance_table = substr(ARGV[1], 2) + ARGV[1] = "" + nlines = split(distance_table, line, /\n/) + for (nr = 1; nr <= nlines; nr++) { + nf = split(line[nr], f, /\t/) + print f[nf] + } } - } - ' ="$distance_table"` + ' ="$distance_table" + ` echo >&2 'Please select one of the following timezones,' echo >&2 'listed roughly in increasing order' \ "of distance from $coord". doselect $regions region=$select_result - tz=`$AWK ' - BEGIN { - distance_table = substr(ARGV[1], 2) - region = substr(ARGV[2], 2) - ARGV[1] = ARGV[2] = "" - nlines = split(distance_table, line, /\n/) - for (nr = 1; nr <= nlines; nr++) { - nf = split(line[nr], f, /\t/) - if (f[nf] == region) { - print f[4] + tz=` + $AWK ' + BEGIN { + distance_table = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + nlines = split(distance_table, line, /\n/) + for (nr = 1; nr <= nlines; nr++) { + nf = split(line[nr], f, /\t/) + if (f[nf] == region) + print f[4] } } - } - ' ="$distance_table" ="$region"` - ;; + ' ="$distance_table" ="$region" + `;; *) case $continent in time) @@ -528,10 +534,11 @@ while old_minute=`TZ=UTC0 date +"$minute_format"` for i in 1 2 3 do - time_table_command=`$AWK \ - -v output_times=1 \ - "$output_distances_or_times" \ - = = ="$TZ_ZONE_TABLE" + time_table_command=` + $AWK \ + -v output_times=1 \ + "$output_distances_or_times" \ + = = ="$TZ_ZONE_TABLE" ` time_table=`eval "$time_table_command"` new_minute=`TZ=UTC0 date +"$minute_format"` @@ -547,36 +554,41 @@ while exit 1 } eval doselect ` - $AWK 'BEGIN { - sorted_table = substr(ARGV[1], 2) - ARGV[1] = "" - nlines = split(sorted_table, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - outline = $6 " " $7 " " $4 " " $5 - if (outline == oldline) continue - oldline = outline - gsub(/'\''/, "&\\\\&&", outline) - printf "'\''%s'\''\n", outline + $AWK ' + BEGIN { + sorted_table = substr(ARGV[1], 2) + ARGV[1] = "" + nlines = split(sorted_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + outline = $6 " " $7 " " $4 " " $5 + if (outline == oldline) + continue + oldline = outline + gsub(/'\''/, "&\\\\&&", outline) + printf "'\''%s'\''\n", outline + } } - }' ="$sorted_table" + ' ="$sorted_table" ` time=$select_result continent_re='^' zone_table=` - $AWK 'BEGIN { - time = substr(ARGV[1], 2) - time_table = substr(ARGV[2], 2) - ARGV[1] = ARGV[2] = "" - nlines = split(time_table, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($6 " " $7 " " $4 " " $5 == time) { - sub(/[^\t]*\t/, "") - print + $AWK ' + BEGIN { + time = substr(ARGV[1], 2) + time_table = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + nlines = split(time_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($6 " " $7 " " $4 " " $5 == time) { + sub(/[^\t]*\t/, "") + print + } } } - }' ="$time" ="$time_table" + ' ="$time" ="$time_table" ` countries=` $AWK \ @@ -613,35 +625,34 @@ while # Get list of timezones in the country. regions=` - $AWK \ - ' - BEGIN { - country = substr(ARGV[1], 2) - TZ_COUNTRY_TABLE = substr(ARGV[2], 2) - TZ_ZONE_TABLE = substr(ARGV[3], 2) - ARGV[1] = ARGV[2] = ARGV[3] = "" - FS = "\t" - cc = country - nlines = split(TZ_COUNTRY_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 !~ /^#/ && country == $2) { - cc = $1 - break - } - } - nlines = split(TZ_ZONE_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 ~ /^#/) continue - if ($1 ~ cc) - print $4 + $AWK ' + BEGIN { + country = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" + FS = "\t" + cc = country + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 !~ /^#/ && country == $2) { + cc = $1 + break } } - ' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) + continue + if ($1 ~ cc) + print $4 + } + } + ' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table" ` - # If there's more than one region, ask the user which one. case $regions in *"$newline"*) @@ -652,33 +663,33 @@ while # Determine tz from country and region. tz=` - $AWK \ - ' - BEGIN { - country = substr(ARGV[1], 2) - region = substr(ARGV[2], 2) - TZ_COUNTRY_TABLE = substr(ARGV[3], 2) - TZ_ZONE_TABLE = substr(ARGV[4], 2) - ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = "" - FS = "\t" - cc = country - nlines = split(TZ_COUNTRY_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 !~ /^#/ && country == $2) { - cc = $1 - break - } - } - nlines = split(TZ_ZONE_TABLE, line, /\n/) - for (i = 1; i <= nlines; i++) { - $0 = line[i] - if ($0 ~ /^#/) continue - if ($1 ~ cc && ($4 == region || !region)) - print $3 + $AWK ' + BEGIN { + country = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + TZ_COUNTRY_TABLE = substr(ARGV[3], 2) + TZ_ZONE_TABLE = substr(ARGV[4], 2) + ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = "" + FS = "\t" + cc = country + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 !~ /^#/ && country == $2) { + cc = $1 + break } } - ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) + continue + if ($1 ~ cc && ($4 == region || !region)) + print $3 + } + } + ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table" ` esac -- 2.40.1
* Makefile (ZONETABLES, VERSION_DEPS): Add zonenow.tab. (CHECK_NOW_TIMESTAMP, CHECK_NOW_FUTURE_YEARS, CHECK_NOW_FUTURE_SECS): New macros. (check_now): New target rule. (check_mild): Depend on it. * NEWS: Mention this new feature. * checknow.awk, zonenow.tab: New files. * checktab.awk: Adjust (i.e., loosen) checks for zonenow.tab. * tzselect.ksh (TZ_ZONETABTYPE_TABLE, TZ_ZONENOW_TABLE): New vars, so that the code can switch back and for easily between zone1970.tab and zonenow.tab. (TZ_ZONE_TABLE): Set to one of those two vars, depending on user. Let users choose ‘now’ if they care only about timestamps from now on. Narrow ‘regions’ based on ‘now’ selection, if possible (which it always is, with the current data). --- Makefile | 31 +++++- NEWS | 11 ++ checknow.awk | 53 +++++++++ checktab.awk | 7 +- tzselect.ksh | 49 +++++++-- zonenow.tab | 298 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 435 insertions(+), 14 deletions(-) create mode 100644 checknow.awk create mode 100644 zonenow.tab diff --git a/Makefile b/Makefile index a6051531..f473af67 100644 --- a/Makefile +++ b/Makefile @@ -580,7 +580,7 @@ YDATA= $(PRIMARY_YDATA) etcetera NDATA= factory TDATA_TO_CHECK= $(YDATA) $(NDATA) backward TDATA= $(YDATA) $(NDATA) $(BACKWARD) -ZONETABLES= zone1970.tab zone.tab +ZONETABLES= zone.tab zone1970.tab zonenow.tab TABDATA= iso3166.tab $(TZDATA_TEXT) $(ZONETABLES) LEAP_DEPS= leapseconds.awk leap-seconds.list TZDATA_ZI_DEPS= ziguard.awk zishrink.awk version $(TDATA) \ @@ -619,7 +619,7 @@ VERSION_DEPS= \ tzfile.5 tzfile.h tzselect.8 tzselect.ksh \ workman.sh zdump.8 zdump.c zic.8 zic.c \ ziguard.awk zishrink.awk \ - zone.tab zone1970.tab + zone.tab zone1970.tab zonenow.tab all: tzselect zic zdump libtz.a $(TABDATA) \ vanguard.zi main.zi rearguard.zi @@ -825,7 +825,8 @@ tzselect: tzselect.ksh version check: check_back check_mild check_mild: check_character_set check_white_space check_links \ - check_name_lengths check_slashed_abbrs check_sorted \ + check_name_lengths check_now \ + check_slashed_abbrs check_sorted \ check_tables check_web check_ziguard check_zishrink check_tzs check_character_set: $(ENCHILADA) @@ -893,7 +894,29 @@ check_links: checklinks.awk tzdata.zi -f checklinks.awk tzdata.zi touch $@ -check_tables: checktab.awk $(YDATA) backward $(ZONETABLES) +# Check timestamps from now through 28 years from now, to make sure +# that zonenow.tab contains all sequences of planned timestamps, +# without any duplicate sequences. In theory this might require +# 2800 years but that would take a long time to check. +CHECK_NOW_TIMESTAMP = `./date +%s` +CHECK_NOW_FUTURE_YEARS = 28 +CHECK_NOW_FUTURE_SECS = $(CHECK_NOW_FUTURE_YEARS) '*' 366 '*' 24 '*' 60 '*' 60 +check_now: checknow.awk date tzdata.zi zdump zic zone1970.tab zonenow.tab + rm -fr $@.dir + mkdir $@.dir + ./zic -d $@.dir tzdata.zi + now=$(CHECK_NOW_TIMESTAMP) && \ + future=`expr $(CHECK_NOW_FUTURE_SECS) + $$now` && \ + ./zdump -i -t $$now,$$future \ + $$(find $$PWD/$@.dir/????*/ -type f) \ + >$@.dir/zdump.tab + $(AWK) \ + -v zdump_table=$@.dir/zdump.tab \ + -f checknow.awk zonenow.tab + rm -fr $@.dir + touch $@ + +check_tables: checktab.awk $(YDATA) backward zone.tab zone1970.tab for tab in $(ZONETABLES); do \ test "$$tab" = zone.tab && links='$(BACKWARD)' || links=''; \ $(AWK) -f checktab.awk -v zone_table=$$tab $(YDATA) $$links \ diff --git a/NEWS b/NEWS index f2d0bc3c..43070aae 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Unreleased, experimental changes Briefly: Ittoqqortoormiit, Greenland changes time zones on 2024-03-31. Code and data fixes for Palestine timestamps starting in 2072. + A new data file zonenow.tab for timestamps starting now. Changes to future timestamps @@ -25,6 +26,13 @@ Unreleased, experimental changes This does not affect UTC offsets, only the tm_isdst flag. (Thanks to Thomas M. Steenholdt.) + New data file + + A new data file zonenow.tab helps configure applications that use + timestamps dated from now on. This simplifies configuration, + since users choose from a smaller Zone set. The file's format is + experimental and subject to change. + Changes to code localtime.c no longer mishandles TZif files that contain a single @@ -32,6 +40,9 @@ Unreleased, experimental changes DST was in effect before the transition too. (Thanks to Alois Treindl for debugging help.) + tzselect now optionally reads zonenow.tab, to simplify when + configuring only for timestamps dated from now on. + tzselect no longer creates temporary files. tzselect no longer mishandles the following: diff --git a/checknow.awk b/checknow.awk new file mode 100644 index 00000000..51ee998f --- /dev/null +++ b/checknow.awk @@ -0,0 +1,53 @@ +# Check zonenow.tab for consistency with primary data. + +# Contributed by Paul Eggert. This file is in the public domain. + +function record_zone(zone, data) { + if (zone) { + zone_data[zone] = data + zones[data] = zones[data] " " zone + } +} + +BEGIN { + while (getline <zdump_table) { + if ($0 ~ /^TZ/) { + record_zone(zone, data) + zone = $0 + sub(/.*\.dir\//, "", zone) + sub(/"/, "", zone) + data = "" + } else if ($0 ~ /./) + data = data $0 "\n" + } + record_zone(zone, data) + FS = "\t" +} + +/^[^#]/ { + zone = $3 + data = zone_data[zone] + if (!data) { + printf "%s: no data\n" + status = 1 + } else { + zone2 = zonenow[data] + if (zone2) { + printf "zones %s and %s identical from now on\n", zone, zone2 + status = 1 + } else + zonenow[data] = zone + } +} + +END { + for (zone in zone_data) { + data = zone_data[zone] + if (!zonenow[data]) { + printf "checknow.tab should have one of:%s\n", zones[data] + zonenow[data] = zone # This suppresses duplicate diagnostics. + status = 1 + } + } + exit status +} diff --git a/checktab.awk b/checktab.awk index 2dbf485f..9a26e465 100644 --- a/checktab.awk +++ b/checktab.awk @@ -83,7 +83,7 @@ BEGIN { cc = cca[i] if (cc2name[cc]) { cc_used[cc]++ - } else { + } else if (! (cc == "XX" && zone_table == "zonenow.tab")) { printf "%s:%d: %s: unknown country code\n", \ zone_table, zone_NR, cc >>"/dev/stderr" status = 1 @@ -110,7 +110,7 @@ BEGIN { used_max_cc = cc } } - if (used_max <= 1 && comments) { + if (used_max <= 1 && comments && zone_table != "zonenow.tab") { printf "%s:%d: unnecessary comment '%s'\n", \ zone_table, i, comments \ >>"/dev/stderr" @@ -149,7 +149,8 @@ $1 ~ /^#/ { next } if ($3 ~ /%/) rulePercentUsed[$2] = 1 } if (tz && tz ~ /\// && tz !~ /^Etc\//) { - if (!tztab[tz] && FILENAME != "backward") { + if (!tztab[tz] && FILENAME != "backward" \ + && zone_table != "zonenow.tab") { printf "%s: no data for '%s'\n", zone_table, tz \ >>"/dev/stderr" status = 1 diff --git a/tzselect.ksh b/tzselect.ksh index f894bb56..70bda9c0 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -196,7 +196,8 @@ read_file() { } } read_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab" -read_file TZ_ZONE_TABLE "$TZDIR/$zonetabtype.tab" +read_file TZ_ZONETABTYPE_TABLE "$TZDIR/$zonetabtype.tab" +TZ_ZONENOW_TABLE= newline=' ' @@ -385,6 +386,7 @@ while country_result= region= time= + TZ_ZONE_TABLE=$TZ_ZONETABTYPE_TABLE case $coord in ?*) @@ -393,7 +395,8 @@ while # Ask the user for continent or ocean. - echo >&2 'Please select a continent, ocean, "coord", "TZ", or "time".' + echo >&2 \ + 'Please select a continent, ocean, "coord", "TZ", "time", or "now".' quoted_continents=` $AWK ' @@ -406,10 +409,10 @@ while printf "'\''%s'\''\n", entry } BEGIN { - TZ_ZONE_TABLE = substr(ARGV[1], 2) + TZ_ZONETABTYPE_TABLE = substr(ARGV[1], 2) ARGV[1] = "" FS = "\t" - nlines = split(TZ_ZONE_TABLE, line, /\n/) + nlines = split(TZ_ZONETABTYPE_TABLE, line, /\n/) for (i = 1; i <= nlines; i++) { $0 = line[i] if ($0 ~ /^[^#]/) @@ -421,7 +424,7 @@ while } } } - ' ="$TZ_ZONE_TABLE" | + ' ="$TZ_ZONETABTYPE_TABLE" | sort -u | tr '\n' ' ' echo '' @@ -431,12 +434,19 @@ while doselect '"$quoted_continents"' \ "coord - I want to use geographical coordinates." \ "TZ - I want to specify the timezone using the POSIX TZ format." \ - "time - I know local time already." + "time - I know local time already." \ + "now - Like \"time\", but configure only for timestamps from now on." continent=$select_result case $continent in Americas) continent=America;; *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''` esac + case $zonetabtype,$continent in + zonenow,*) ;; + *,now) + ${TZ_ZONENOW_TABLE:+:} read_file TZ_ZONENOW_TABLE "$TZDIR/zonenow.tab" + TZ_ZONE_TABLE=$TZ_ZONENOW_TABLE + esac ' esac @@ -529,7 +539,7 @@ while `;; *) case $continent in - time) + now|time) minute_format='%a %b %d %H:%M' old_minute=`TZ=UTC0 date +"$minute_format"` for i in 1 2 3 @@ -609,6 +619,31 @@ while ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | sort -f ` + # If all zone table entries have comments, and there are + # at most 22 entries, asked based on those comments. + # This fits the prompt onto old-fashioned 24-line screens. + regions=` + $AWK ' + BEGIN { + TZ_ZONE_TABLE = substr(ARGV[1], 2) + ARGV[1] = "" + FS = "\t" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^[^#]/ && !missing_comment) { + if ($4) + comment[++inlines] = $4 + else + missing_comment = 1 + } + } + if (!missing_comment && inlines <= 22) + for (i = 1; i <= inlines; i++) + print comment[i] + } + ' ="$zone_table" + ` # If there's more than one country, ask the user which one. case $countries in diff --git a/zonenow.tab b/zonenow.tab new file mode 100644 index 00000000..b1c1f332 --- /dev/null +++ b/zonenow.tab @@ -0,0 +1,298 @@ +# tzdb timezone descriptions, for users who do not care about old timestamps +# +# This file is in the public domain. +# +# From Paul Eggert (2023-12-18): +# This file contains a table where each row stands for a timezone +# where civil timestamps are predicted to agree from now on. +# This file is like zone1970.tab (see zone1970.tab's coments), +# but with the following changes: +# +# 1. Each timezone corresponds to a set of clocks that are planned +# to agree from now on. This is a larger set of clocks than in +# zone1970.tab, where each timezone's clocks must agree from 1970 on. +# 2. The first column is irrelevant and ignored. +# 3. The table is sorted in a different way: +# first by standard time UTC offset; +# then, if DST is used, by daylight saving UTC offset; +# then by time zone abbreviation. +# +# The format of this table is experimental, and may change in future versions. +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#XX coordinates TZ comments +# +# -11 - SST +XX -1416-17042 Pacific/Pago_Pago Midway; Samoa ("SST") +# +# -11 +XX -1901-16955 Pacific/Niue Niue +# +# -10 - HST +XX +211825-1575130 Pacific/Honolulu Hawaii ("HST") +# +# -10 +XX -1732-14934 Pacific/Tahiti Tahiti; Cook Islands +# +# -10/-09 - HST / HDT (North America DST) +XX +515248-1763929 America/Adak western Aleutians in Alaska ("HST/HDT") +# +# -09:30 +XX -0900-13930 Pacific/Marquesas Marquesas +# +# -09 +XX -2308-13457 Pacific/Gambier Gambier +# +# -09/-08 - AKST/AKDT (North America DST) +XX +611305-1495401 America/Anchorage most of Alaska ("AKST/AKDT") +# +# -08 +XX -2504-13005 Pacific/Pitcairn Pitcairn +# +# -08/-07 - PST/PDT (North America DST) +XX +340308-1181434 America/Los_Angeles Pacific ("PST/PDT") - US & Canada; Mexico near US border +# +# -07 - MST +XX +332654-1120424 America/Phoenix Mountain Standard ("MST") - Arizona; western Mexico; Yukon +# +# -07/-06 - MST/MDT (North America DST) +XX +394421-1045903 America/Denver Mountain ("MST/MDT") - US & Canada; Mexico near US border +# +# -06 +XX -0054-08936 Pacific/Galapagos Galápagos +# +# -06 - CST +XX +1924-09909 America/Mexico_City Central Standard ("CST") - Saskatchewan; central Mexico; Central America +# +# -06/-05 (Chile DST) +XX -2709-10926 Pacific/Easter Easter Island +# +# -06/-05 - CST/CDT (North America DST) +XX +415100-0873900 America/Chicago Central ("CST/CDT") - US & Canada; Mexico near US border +# +# -05 +XX -1203-07703 America/Lima eastern South America +# +# -05 - EST +XX +175805-0764736 America/Jamaica Eastern Standard ("EST") - Caymans; Jamaica; eastern Mexico; Panama +# +# -05/-04 - CST/CDT (Cuba DST) +XX +2308-08222 America/Havana Cuba +# +# -05/-04 - EST/EDT (North America DST) +XX +404251-0740023 America/New_York Eastern ("EST/EDT") - US & Canada +# +# -04 +XX +1030-06656 America/Caracas western South America +# +# -04 - AST +XX +1828-06954 America/Santo_Domingo Atlantic Standard ("AST") - eastern Caribbean +# +# -04/-03 (Chile DST) +XX -3327-07040 America/Santiago most of Chile +# +# -04/-03 (Paraguay DST) +XX -2516-05740 America/Asuncion Paraguay +# +# -04/-03 - AST/ADT (North America DST) +XX +4439-06336 America/Halifax Atlantic ("AST/ADT") - Canada; Bermuda +# +# -03:30/-02:30 - NST/NDT (North America DST) +XX +4734-05243 America/St_Johns Newfoundland ("NST/NDT") +# +# -03 +XX -2332-04637 America/Sao_Paulo eastern South America +# +# -03/-02 (North America DST) +XX +4703-05620 America/Miquelon St Pierre & Miquelon +# +# -02 +XX -0351-03225 America/Noronha Fernando de Noronha; South Georgia +# +# -02/-01 (EU DST) +XX +6411-05144 America/Nuuk most of Greenland +# +# -01 +XX +1455-02331 Atlantic/Cape_Verde Cape Verde +# +# -01/+00 (EU DST) +XX +3744-02540 Atlantic/Azores Azores +# -01/+00 (EU DST) until 2024-03-01; then -02/-01 (EU DST) +XX +7029-02158 America/Scoresbysund Ittoqqortoormiit +# +# +00 - GMT +XX +0519-00402 Africa/Abidjan far western Africa; Iceland ("GMT") +# +# +00/+01 - GMT/BST (EU DST) +XX +513030-0000731 Europe/London United Kingdom ("GMT/BST") +# +# +00/+01 - WET/WEST (EU DST) +XX +3843-00908 Europe/Lisbon western Europe ("WET/WEST") +# +# +00/+01 - Troll DST +XX -720041+0023206 Antarctica/Troll Troll Station in Antarctica +# +# +01 - CET +XX +3647+00303 Africa/Algiers Algeria, Tunisia ("CET") +# +# +01 - WAT +XX +0627+00324 Africa/Lagos western Africa ("WAT") +# +# +01/+00 - IST/GMT (EU DST in reverse) +XX +5320-00615 Europe/Dublin Ireland ("IST/GMT") +# +# +01/+00 - (Morocco DST) +XX +3339-00735 Africa/Casablanca Morocco +# +# +01/+02 - CET/CEST (EU DST) +XX +4852+00220 Europe/Paris central Europe ("CET/CEST") +# +# +02 - CAT +XX -2558+03235 Africa/Maputo central Africa ("CAT") +# +# +02 - EET +XX +3254+01311 Africa/Tripoli Libya; Kaliningrad ("EET") +# +# +02 - SAST +XX -2615+02800 Africa/Johannesburg southern Africa ("SAST") +# +# +02/+03 - EET/EEST (EU DST) +XX +3758+02343 Europe/Athens eastern Europe ("EET/EEST") +# +# +02/+03 - EET/EEST (Egypt DST) +XX +3003+03115 Africa/Cairo Egypt +# +# +02/+03 - EET/EEST (Lebanon DST) +XX +3353+03530 Asia/Beirut Lebanon +# +# +02/+03 - EET/EEST (Moldova DST) +XX +4700+02850 Europe/Chisinau Moldova +# +# +02/+03 - EET/EEST (Palestine DST) +XX +3130+03428 Asia/Gaza Palestine +# +# +02/+03 - IST/IDT (Israel DST) +XX +314650+0351326 Asia/Jerusalem Israel +# +# +03 +XX +4101+02858 Europe/Istanbul Near East; Belarus +# +# +03 - EAT +XX -0117+03649 Africa/Nairobi eastern Africa ("EAT") +# +# +03 - MSK +XX +554521+0373704 Europe/Moscow Moscow ("MSK") +# +# +03:30 +XX +3540+05126 Asia/Tehran Iran +# +# +04 +XX +2518+05518 Asia/Dubai Russia; Caucasus; Persian Gulf; Seychelles; Réunion +# +# +04:30 +XX +3431+06912 Asia/Kabul Afghanistan +# +# +05 +XX +4120+06918 Asia/Tashkent Russia; Tajikistan; Turkmenistan; Uzbekistan; Maldives +# +# +05 - PKT +XX +2452+06703 Asia/Karachi Pakistan ("PKT") +# +# +05:30 +XX +0656+07951 Asia/Colombo Sri Lanka +# +# +05:30 - IST +XX +2232+08822 Asia/Kolkata India ("IST") +# +# +05:45 +XX +2743+08519 Asia/Kathmandu Nepal +# +# +06 +XX +2343+09025 Asia/Dhaka Russia; Kyrgyzstan; Bhutan; Bangladesh; Chagos +# +# +06:30 +XX +1647+09610 Asia/Yangon Myanmar; Cocos +# +# +07 +XX +1345+10031 Asia/Bangkok Russia; Indochina; Christmas Island +# +# +07 - WIB +XX -0610+10648 Asia/Jakarta Indonesia ("WIB") +# +# +08 +XX +0117+10351 Asia/Singapore Russia; Brunei; Malaysia; Singapore +# +# +08 - AWST +XX -3157+11551 Australia/Perth Western Australia ("AWST") +# +# +08 - CST +XX +3114+12128 Asia/Shanghai China ("CST") +# +# +08 - HKT +XX +2217+11409 Asia/Hong_Kong Hong Kong ("HKT") +# +# +08 - PHT +XX +1435+12100 Asia/Manila Philippines ("PHT") +# +# +08 - WITA +XX -0507+11924 Asia/Makassar Indonesia ("WITA") +# +# +08:45 +XX -3143+12852 Australia/Eucla Eucla +# +# +09 +XX +5203+11328 Asia/Chita Russia; Palau; East Timor +# +# +09 - JST +XX +353916+1394441 Asia/Tokyo Japan ("JST") +# +# +09 - KST +XX +3733+12658 Asia/Seoul Korea ("KST") +# +# +09 - WIT +XX -0232+14042 Asia/Jayapura Indonesia ("WIT") +# +# +09:30 - ACST +XX -1228+13050 Australia/Darwin Northern Territory ("ACST") +# +# +09:30/+10:30 - ACST/ACDT (Australia DST) +XX -3455+13835 Australia/Adelaide South Australia ("ACST/ACDT") +# +# +10 +XX +4310+13156 Asia/Vladivostok Russia; Yap; Chuuk; Papua New Guinea; Dumont d'Urville +# +# +10 - AEST +XX -2728+15302 Australia/Brisbane Queensland ("AEST") +# +# +10 - ChST +XX +1328+14445 Pacific/Guam Mariana Islands ("ChST") +# +# +10/+11 - AEST/AEDT (Australia DST) +XX -3352+15113 Australia/Sydney southeast Australia ("AEST/AEDT") +# +# +10:30/+11 +XX -3133+15905 Australia/Lord_Howe Lord Howe Island +# +# +11 +XX -0613+15534 Pacific/Bougainville Russia; Kosrae; Bougainville; Solomons +# +# +11/+12 (Australia DST) +XX -2903+16758 Pacific/Norfolk Norfolk Island +# +# +12 +XX +5301+15839 Asia/Kamchatka Russia; Tuvalu; Fiji; etc. +# +# +12/+13 (New Zealand DST) +XX -3652+17446 Pacific/Auckland New Zealand ("NZST/NZDT") +# +# +12:45/+13:45 (Chatham DST) +XX -4357-17633 Pacific/Chatham Chatham Islands +# +# +13 +XX -210800-1751200 Pacific/Tongatapu Kanton; Tokelau; Samoa (western); Tonga +# +# +14 +XX +0152-15720 Pacific/Kiritimati Kiritimati -- 2.40.1
Fixing up a small typo in the comments. One drawback of this approach is that such lines will need to be merged into the appropriate long-term zonenow entry after the date listed. -- Tim Parenti On Tue, 19 Dec 2023 at 02:30, Paul Eggert via tz <tz@iana.org> wrote:
* Makefile (ZONETABLES, VERSION_DEPS): Add zonenow.tab. (CHECK_NOW_TIMESTAMP, CHECK_NOW_FUTURE_YEARS, CHECK_NOW_FUTURE_SECS): New macros. (check_now): New target rule. (check_mild): Depend on it. * NEWS: Mention this new feature. * checknow.awk, zonenow.tab: New files. * checktab.awk: Adjust (i.e., loosen) checks for zonenow.tab. * tzselect.ksh (TZ_ZONETABTYPE_TABLE, TZ_ZONENOW_TABLE): New vars, so that the code can switch back and for easily between zone1970.tab and zonenow.tab. (TZ_ZONE_TABLE): Set to one of those two vars, depending on user. Let users choose ‘now’ if they care only about timestamps from now on. Narrow ‘regions’ based on ‘now’ selection, if possible (which it always is, with the current data). --- Makefile | 31 +++++- NEWS | 11 ++ checknow.awk | 53 +++++++++ checktab.awk | 7 +- tzselect.ksh | 49 +++++++-- zonenow.tab | 298 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 435 insertions(+), 14 deletions(-) create mode 100644 checknow.awk create mode 100644 zonenow.tab
diff --git a/Makefile b/Makefile index a6051531..f473af67 100644 --- a/Makefile +++ b/Makefile @@ -580,7 +580,7 @@ YDATA= $(PRIMARY_YDATA) etcetera NDATA= factory TDATA_TO_CHECK= $(YDATA) $(NDATA) backward TDATA= $(YDATA) $(NDATA) $(BACKWARD) -ZONETABLES= zone1970.tab zone.tab +ZONETABLES= zone.tab zone1970.tab zonenow.tab TABDATA= iso3166.tab $(TZDATA_TEXT) $(ZONETABLES) LEAP_DEPS= leapseconds.awk leap-seconds.list TZDATA_ZI_DEPS= ziguard.awk zishrink.awk version $(TDATA) \ @@ -619,7 +619,7 @@ VERSION_DEPS= \ tzfile.5 tzfile.h tzselect.8 tzselect.ksh \ workman.sh zdump.8 zdump.c zic.8 zic.c \ ziguard.awk zishrink.awk \ - zone.tab zone1970.tab + zone.tab zone1970.tab zonenow.tab
all: tzselect zic zdump libtz.a $(TABDATA) \ vanguard.zi main.zi rearguard.zi @@ -825,7 +825,8 @@ tzselect: tzselect.ksh version
check: check_back check_mild check_mild: check_character_set check_white_space check_links \ - check_name_lengths check_slashed_abbrs check_sorted \ + check_name_lengths check_now \ + check_slashed_abbrs check_sorted \ check_tables check_web check_ziguard check_zishrink check_tzs
check_character_set: $(ENCHILADA) @@ -893,7 +894,29 @@ check_links: checklinks.awk tzdata.zi -f checklinks.awk tzdata.zi touch $@
-check_tables: checktab.awk $(YDATA) backward $(ZONETABLES) +# Check timestamps from now through 28 years from now, to make sure +# that zonenow.tab contains all sequences of planned timestamps, +# without any duplicate sequences. In theory this might require +# 2800 years but that would take a long time to check. +CHECK_NOW_TIMESTAMP = `./date +%s` +CHECK_NOW_FUTURE_YEARS = 28 +CHECK_NOW_FUTURE_SECS = $(CHECK_NOW_FUTURE_YEARS) '*' 366 '*' 24 '*' 60 '*' 60 +check_now: checknow.awk date tzdata.zi zdump zic zone1970.tab zonenow.tab + rm -fr $@.dir + mkdir $@.dir + ./zic -d $@.dir tzdata.zi + now=$(CHECK_NOW_TIMESTAMP) && \ + future=`expr $(CHECK_NOW_FUTURE_SECS) + $$now` && \ + ./zdump -i -t $$now,$$future \ + $$(find $$PWD/$@.dir/????*/ -type f) \ + >$@.dir/zdump.tab + $(AWK) \ + -v zdump_table=$@.dir/zdump.tab \ + -f checknow.awk zonenow.tab + rm -fr $@.dir + touch $@ + +check_tables: checktab.awk $(YDATA) backward zone.tab zone1970.tab for tab in $(ZONETABLES); do \ test "$$tab" = zone.tab && links='$(BACKWARD)' || links=''; \ $(AWK) -f checktab.awk -v zone_table=$$tab $(YDATA) $$links \ diff --git a/NEWS b/NEWS index f2d0bc3c..43070aae 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Unreleased, experimental changes Briefly: Ittoqqortoormiit, Greenland changes time zones on 2024-03-31. Code and data fixes for Palestine timestamps starting in 2072. + A new data file zonenow.tab for timestamps starting now.
Changes to future timestamps
@@ -25,6 +26,13 @@ Unreleased, experimental changes This does not affect UTC offsets, only the tm_isdst flag. (Thanks to Thomas M. Steenholdt.)
+ New data file + + A new data file zonenow.tab helps configure applications that use + timestamps dated from now on. This simplifies configuration, + since users choose from a smaller Zone set. The file's format is + experimental and subject to change. + Changes to code
localtime.c no longer mishandles TZif files that contain a single @@ -32,6 +40,9 @@ Unreleased, experimental changes DST was in effect before the transition too. (Thanks to Alois Treindl for debugging help.)
+ tzselect now optionally reads zonenow.tab, to simplify when + configuring only for timestamps dated from now on. + tzselect no longer creates temporary files.
tzselect no longer mishandles the following: diff --git a/checknow.awk b/checknow.awk new file mode 100644 index 00000000..51ee998f --- /dev/null +++ b/checknow.awk @@ -0,0 +1,53 @@ +# Check zonenow.tab for consistency with primary data. + +# Contributed by Paul Eggert. This file is in the public domain. + +function record_zone(zone, data) { + if (zone) { + zone_data[zone] = data + zones[data] = zones[data] " " zone + } +} + +BEGIN { + while (getline <zdump_table) { + if ($0 ~ /^TZ/) { + record_zone(zone, data) + zone = $0 + sub(/.*\.dir\//, "", zone) + sub(/"/, "", zone) + data = "" + } else if ($0 ~ /./) + data = data $0 "\n" + } + record_zone(zone, data) + FS = "\t" +} + +/^[^#]/ { + zone = $3 + data = zone_data[zone] + if (!data) { + printf "%s: no data\n" + status = 1 + } else { + zone2 = zonenow[data] + if (zone2) { + printf "zones %s and %s identical from now on\n", zone, zone2 + status = 1 + } else + zonenow[data] = zone + } +} + +END { + for (zone in zone_data) { + data = zone_data[zone] + if (!zonenow[data]) { + printf "checknow.tab should have one of:%s\n", zones[data] + zonenow[data] = zone # This suppresses duplicate diagnostics. + status = 1 + } + } + exit status +} diff --git a/checktab.awk b/checktab.awk index 2dbf485f..9a26e465 100644 --- a/checktab.awk +++ b/checktab.awk @@ -83,7 +83,7 @@ BEGIN { cc = cca[i] if (cc2name[cc]) { cc_used[cc]++ - } else { + } else if (! (cc == "XX" && zone_table == "zonenow.tab")) { printf "%s:%d: %s: unknown country code\n", \ zone_table, zone_NR, cc >>"/dev/stderr" status = 1 @@ -110,7 +110,7 @@ BEGIN { used_max_cc = cc } } - if (used_max <= 1 && comments) { + if (used_max <= 1 && comments && zone_table != "zonenow.tab") { printf "%s:%d: unnecessary comment '%s'\n", \ zone_table, i, comments \ >>"/dev/stderr" @@ -149,7 +149,8 @@ $1 ~ /^#/ { next } if ($3 ~ /%/) rulePercentUsed[$2] = 1 } if (tz && tz ~ /\// && tz !~ /^Etc\//) { - if (!tztab[tz] && FILENAME != "backward") { + if (!tztab[tz] && FILENAME != "backward" \ + && zone_table != "zonenow.tab") { printf "%s: no data for '%s'\n", zone_table, tz \ >>"/dev/stderr" status = 1 diff --git a/tzselect.ksh b/tzselect.ksh index f894bb56..70bda9c0 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -196,7 +196,8 @@ read_file() { } } read_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab" -read_file TZ_ZONE_TABLE "$TZDIR/$zonetabtype.tab" +read_file TZ_ZONETABTYPE_TABLE "$TZDIR/$zonetabtype.tab" +TZ_ZONENOW_TABLE=
newline=' ' @@ -385,6 +386,7 @@ while country_result= region= time= + TZ_ZONE_TABLE=$TZ_ZONETABTYPE_TABLE
case $coord in ?*) @@ -393,7 +395,8 @@ while
# Ask the user for continent or ocean.
- echo >&2 'Please select a continent, ocean, "coord", "TZ", or "time".' + echo >&2 \ + 'Please select a continent, ocean, "coord", "TZ", "time", or "now".'
quoted_continents=` $AWK ' @@ -406,10 +409,10 @@ while printf "'\''%s'\''\n", entry } BEGIN { - TZ_ZONE_TABLE = substr(ARGV[1], 2) + TZ_ZONETABTYPE_TABLE = substr(ARGV[1], 2) ARGV[1] = "" FS = "\t" - nlines = split(TZ_ZONE_TABLE, line, /\n/) + nlines = split(TZ_ZONETABTYPE_TABLE, line, /\n/) for (i = 1; i <= nlines; i++) { $0 = line[i] if ($0 ~ /^[^#]/) @@ -421,7 +424,7 @@ while } } } - ' ="$TZ_ZONE_TABLE" | + ' ="$TZ_ZONETABTYPE_TABLE" | sort -u | tr '\n' ' ' echo '' @@ -431,12 +434,19 @@ while doselect '"$quoted_continents"' \ "coord - I want to use geographical coordinates." \ "TZ - I want to specify the timezone using the POSIX TZ format." \ - "time - I know local time already." + "time - I know local time already." \ + "now - Like \"time\", but configure only for timestamps from now on." continent=$select_result case $continent in Americas) continent=America;; *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''` esac + case $zonetabtype,$continent in + zonenow,*) ;; + *,now) + ${TZ_ZONENOW_TABLE:+:} read_file TZ_ZONENOW_TABLE "$TZDIR/zonenow.tab" + TZ_ZONE_TABLE=$TZ_ZONENOW_TABLE + esac ' esac
@@ -529,7 +539,7 @@ while `;; *) case $continent in - time) + now|time) minute_format='%a %b %d %H:%M' old_minute=`TZ=UTC0 date +"$minute_format"` for i in 1 2 3 @@ -609,6 +619,31 @@ while ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | sort -f ` + # If all zone table entries have comments, and there are + # at most 22 entries, asked based on those comments. + # This fits the prompt onto old-fashioned 24-line screens. + regions=` + $AWK ' + BEGIN { + TZ_ZONE_TABLE = substr(ARGV[1], 2) + ARGV[1] = "" + FS = "\t" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^[^#]/ && !missing_comment) { + if ($4) + comment[++inlines] = $4 + else + missing_comment = 1 + } + } + if (!missing_comment && inlines <= 22) + for (i = 1; i <= inlines; i++) + print comment[i] + } + ' ="$zone_table" + `
# If there's more than one country, ask the user which one. case $countries in diff --git a/zonenow.tab b/zonenow.tab new file mode 100644 index 00000000..b1c1f332 --- /dev/null +++ b/zonenow.tab @@ -0,0 +1,298 @@ +# tzdb timezone descriptions, for users who do not care about old timestamps +# +# This file is in the public domain. +# +# From Paul Eggert (2023-12-18): +# This file contains a table where each row stands for a timezone +# where civil timestamps are predicted to agree from now on. +# This file is like zone1970.tab (see zone1970.tab's coments), +# but with the following changes: +# +# 1. Each timezone corresponds to a set of clocks that are planned +# to agree from now on. This is a larger set of clocks than in +# zone1970.tab, where each timezone's clocks must agree from 1970 on. +# 2. The first column is irrelevant and ignored. +# 3. The table is sorted in a different way: +# first by standard time UTC offset; +# then, if DST is used, by daylight saving UTC offset; +# then by time zone abbreviation. +# +# The format of this table is experimental, and may change in future versions. +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#XX coordinates TZ comments +# +# -11 - SST +XX -1416-17042 Pacific/Pago_Pago Midway; Samoa ("SST") +# +# -11 +XX -1901-16955 Pacific/Niue Niue +# +# -10 - HST +XX +211825-1575130 Pacific/Honolulu Hawaii ("HST") +# +# -10 +XX -1732-14934 Pacific/Tahiti Tahiti; Cook Islands +# +# -10/-09 - HST / HDT (North America DST) +XX +515248-1763929 America/Adak western Aleutians in Alaska ("HST/HDT") +# +# -09:30 +XX -0900-13930 Pacific/Marquesas Marquesas +# +# -09 +XX -2308-13457 Pacific/Gambier Gambier +# +# -09/-08 - AKST/AKDT (North America DST) +XX +611305-1495401 America/Anchorage most of Alaska ("AKST/AKDT") +# +# -08 +XX -2504-13005 Pacific/Pitcairn Pitcairn +# +# -08/-07 - PST/PDT (North America DST) +XX +340308-1181434 America/Los_Angeles Pacific ("PST/PDT") - US & Canada; Mexico near US border +# +# -07 - MST +XX +332654-1120424 America/Phoenix Mountain Standard ("MST") - Arizona; western Mexico; Yukon +# +# -07/-06 - MST/MDT (North America DST) +XX +394421-1045903 America/Denver Mountain ("MST/MDT") - US & Canada; Mexico near US border +# +# -06 +XX -0054-08936 Pacific/Galapagos Galápagos +# +# -06 - CST +XX +1924-09909 America/Mexico_City Central Standard ("CST") - Saskatchewan; central Mexico; Central America +# +# -06/-05 (Chile DST) +XX -2709-10926 Pacific/Easter Easter Island +# +# -06/-05 - CST/CDT (North America DST) +XX +415100-0873900 America/Chicago Central ("CST/CDT") - US & Canada; Mexico near US border +# +# -05 +XX -1203-07703 America/Lima eastern South America +# +# -05 - EST +XX +175805-0764736 America/Jamaica Eastern Standard ("EST") - Caymans; Jamaica; eastern Mexico; Panama +# +# -05/-04 - CST/CDT (Cuba DST) +XX +2308-08222 America/Havana Cuba +# +# -05/-04 - EST/EDT (North America DST) +XX +404251-0740023 America/New_York Eastern ("EST/EDT") - US & Canada +# +# -04 +XX +1030-06656 America/Caracas western South America +# +# -04 - AST +XX +1828-06954 America/Santo_Domingo Atlantic Standard ("AST") - eastern Caribbean +# +# -04/-03 (Chile DST) +XX -3327-07040 America/Santiago most of Chile +# +# -04/-03 (Paraguay DST) +XX -2516-05740 America/Asuncion Paraguay +# +# -04/-03 - AST/ADT (North America DST) +XX +4439-06336 America/Halifax Atlantic ("AST/ADT") - Canada; Bermuda +# +# -03:30/-02:30 - NST/NDT (North America DST) +XX +4734-05243 America/St_Johns Newfoundland ("NST/NDT") +# +# -03 +XX -2332-04637 America/Sao_Paulo eastern South America +# +# -03/-02 (North America DST) +XX +4703-05620 America/Miquelon St Pierre & Miquelon +# +# -02 +XX -0351-03225 America/Noronha Fernando de Noronha; South Georgia +# +# -02/-01 (EU DST) +XX +6411-05144 America/Nuuk most of Greenland +# +# -01 +XX +1455-02331 Atlantic/Cape_Verde Cape Verde +# +# -01/+00 (EU DST) +XX +3744-02540 Atlantic/Azores Azores +# -01/+00 (EU DST) until 2024-03-01; then -02/-01 (EU DST) +XX +7029-02158 America/Scoresbysund Ittoqqortoormiit +# +# +00 - GMT +XX +0519-00402 Africa/Abidjan far western Africa; Iceland ("GMT") +# +# +00/+01 - GMT/BST (EU DST) +XX +513030-0000731 Europe/London United Kingdom ("GMT/BST") +# +# +00/+01 - WET/WEST (EU DST) +XX +3843-00908 Europe/Lisbon western Europe ("WET/WEST") +# +# +00/+01 - Troll DST +XX -720041+0023206 Antarctica/Troll Troll Station in Antarctica +# +# +01 - CET +XX +3647+00303 Africa/Algiers Algeria, Tunisia ("CET") +# +# +01 - WAT +XX +0627+00324 Africa/Lagos western Africa ("WAT") +# +# +01/+00 - IST/GMT (EU DST in reverse) +XX +5320-00615 Europe/Dublin Ireland ("IST/GMT") +# +# +01/+00 - (Morocco DST) +XX +3339-00735 Africa/Casablanca Morocco +# +# +01/+02 - CET/CEST (EU DST) +XX +4852+00220 Europe/Paris central Europe ("CET/CEST") +# +# +02 - CAT +XX -2558+03235 Africa/Maputo central Africa ("CAT") +# +# +02 - EET +XX +3254+01311 Africa/Tripoli Libya; Kaliningrad ("EET") +# +# +02 - SAST +XX -2615+02800 Africa/Johannesburg southern Africa ("SAST") +# +# +02/+03 - EET/EEST (EU DST) +XX +3758+02343 Europe/Athens eastern Europe ("EET/EEST") +# +# +02/+03 - EET/EEST (Egypt DST) +XX +3003+03115 Africa/Cairo Egypt +# +# +02/+03 - EET/EEST (Lebanon DST) +XX +3353+03530 Asia/Beirut Lebanon +# +# +02/+03 - EET/EEST (Moldova DST) +XX +4700+02850 Europe/Chisinau Moldova +# +# +02/+03 - EET/EEST (Palestine DST) +XX +3130+03428 Asia/Gaza Palestine +# +# +02/+03 - IST/IDT (Israel DST) +XX +314650+0351326 Asia/Jerusalem Israel +# +# +03 +XX +4101+02858 Europe/Istanbul Near East; Belarus +# +# +03 - EAT +XX -0117+03649 Africa/Nairobi eastern Africa ("EAT") +# +# +03 - MSK +XX +554521+0373704 Europe/Moscow Moscow ("MSK") +# +# +03:30 +XX +3540+05126 Asia/Tehran Iran +# +# +04 +XX +2518+05518 Asia/Dubai Russia; Caucasus; Persian Gulf; Seychelles; Réunion +# +# +04:30 +XX +3431+06912 Asia/Kabul Afghanistan +# +# +05 +XX +4120+06918 Asia/Tashkent Russia; Tajikistan; Turkmenistan; Uzbekistan; Maldives +# +# +05 - PKT +XX +2452+06703 Asia/Karachi Pakistan ("PKT") +# +# +05:30 +XX +0656+07951 Asia/Colombo Sri Lanka +# +# +05:30 - IST +XX +2232+08822 Asia/Kolkata India ("IST") +# +# +05:45 +XX +2743+08519 Asia/Kathmandu Nepal +# +# +06 +XX +2343+09025 Asia/Dhaka Russia; Kyrgyzstan; Bhutan; Bangladesh; Chagos +# +# +06:30 +XX +1647+09610 Asia/Yangon Myanmar; Cocos +# +# +07 +XX +1345+10031 Asia/Bangkok Russia; Indochina; Christmas Island +# +# +07 - WIB +XX -0610+10648 Asia/Jakarta Indonesia ("WIB") +# +# +08 +XX +0117+10351 Asia/Singapore Russia; Brunei; Malaysia; Singapore +# +# +08 - AWST +XX -3157+11551 Australia/Perth Western Australia ("AWST") +# +# +08 - CST +XX +3114+12128 Asia/Shanghai China ("CST") +# +# +08 - HKT +XX +2217+11409 Asia/Hong_Kong Hong Kong ("HKT") +# +# +08 - PHT +XX +1435+12100 Asia/Manila Philippines ("PHT") +# +# +08 - WITA +XX -0507+11924 Asia/Makassar Indonesia ("WITA") +# +# +08:45 +XX -3143+12852 Australia/Eucla Eucla +# +# +09 +XX +5203+11328 Asia/Chita Russia; Palau; East Timor +# +# +09 - JST +XX +353916+1394441 Asia/Tokyo Japan ("JST") +# +# +09 - KST +XX +3733+12658 Asia/Seoul Korea ("KST") +# +# +09 - WIT +XX -0232+14042 Asia/Jayapura Indonesia ("WIT") +# +# +09:30 - ACST +XX -1228+13050 Australia/Darwin Northern Territory ("ACST") +# +# +09:30/+10:30 - ACST/ACDT (Australia DST) +XX -3455+13835 Australia/Adelaide South Australia ("ACST/ACDT") +# +# +10 +XX +4310+13156 Asia/Vladivostok Russia; Yap; Chuuk; Papua New Guinea; Dumont d'Urville +# +# +10 - AEST +XX -2728+15302 Australia/Brisbane Queensland ("AEST") +# +# +10 - ChST +XX +1328+14445 Pacific/Guam Mariana Islands ("ChST") +# +# +10/+11 - AEST/AEDT (Australia DST) +XX -3352+15113 Australia/Sydney southeast Australia ("AEST/AEDT") +# +# +10:30/+11 +XX -3133+15905 Australia/Lord_Howe Lord Howe Island +# +# +11 +XX -0613+15534 Pacific/Bougainville Russia; Kosrae; Bougainville; Solomons +# +# +11/+12 (Australia DST) +XX -2903+16758 Pacific/Norfolk Norfolk Island +# +# +12 +XX +5301+15839 Asia/Kamchatka Russia; Tuvalu; Fiji; etc. +# +# +12/+13 (New Zealand DST) +XX -3652+17446 Pacific/Auckland New Zealand ("NZST/NZDT") +# +# +12:45/+13:45 (Chatham DST) +XX -4357-17633 Pacific/Chatham Chatham Islands +# +# +13 +XX -210800-1751200 Pacific/Tongatapu Kanton; Tokelau; Samoa (western); Tonga +# +# +14 +XX +0152-15720 Pacific/Kiritimati Kiritimati -- 2.40.1
On 2023-12-20 09:41, Tim Parenti wrote:
One drawback of this approach is that such lines will need to be merged into the appropriate long-term zonenow entry after the date listed.
That shouldn't be a problem in this particular case. After the date listed, the comment line and associated data line should simply vanish. There should then be no need to alter the comment line or comments column for the America/Nuuk ("most of Greenland") line, since Ittoqqortoormiit is in "most of Greenland". The main reason there's a comment with a date now, is to remind us to remove the comment and the associated data line then. (This is belt and suspenders as "make check" should also remind us.) You're right that it is an issue in the general case when data lines are removed (or added) in zonenow.tab. That's why zonenow.tab is a separate file. Although I originally wanted to calculate zonenow.tab automatically, there was too much human judgment in its "comments" column. Thanks for catching the typo. I have more typo fixes on the way....
null On Wed, Dec 20, 2023 at 12:00 PM Paul Eggert via tz <tz@iana.org> wrote:
On 2023-12-20 09:41, Tim Parenti wrote:
One drawback of this approach is that such lines will need to be merged into the appropriate long-term zonenow entry after the date listed.
That shouldn't be a problem in this particular case. After the date listed, the comment line and associated data line should simply vanish. There should then be no need to alter the comment line or comments column for the America/Nuuk ("most of Greenland") line, since Ittoqqortoormiit is in "most of Greenland".
The main reason there's a comment with a date now, is to remind us to remove the comment and the associated data line then. (This is belt and suspenders as "make check" should also remind us.)
You're right that it is an issue in the general case when data lines are removed (or added) in zonenow.tab. That's why zonenow.tab is a separate file. Although I originally wanted to calculate zonenow.tab automatically, there was too much human judgment in its "comments" column.
Thanks for catching the typo. I have more typo fixes on the way....
On 2023-12-19 15:25:49 (+0800), Paul Eggert via tz wrote:
For some time I've been meaning to extend tzselect so that users who care only about timestamps from now on can have a smaller set of timezones to choose from, thus simplifying configuration.
This patchset does this via a new file zonenow.tab, with contents like zone1970.tab except that zonenow.tab partitions based on timestamps from now on, instead of from 1970 on. The new file's format is still experimental and feedback is welcome.
These patches mostly just refactor tzselect, to simplify support for the new feature. The last patch adds the new feature.
I skimmed through all the patches in this set. Other than the minor comment I made about refactoring for the sake of refactoring, the diffs look good to me. I'll try to find a chance to test this on FreeBSD in the coming days. Philip -- Philip Paeps Senior Reality Engineer Alternative Enterprises
On 2023-12-19 23:51, Philip Paeps wrote:
I'll try to find a chance to test this on FreeBSD in the coming days.
Thanks for the quick review. Your email inspired me to run 'make check' on FreeBSD 14 and I found a false positive from FreeBSD's C compiler suite: FreeBSD 14's c99 command incorrectly diagnoses uses of _Generic even when the uses are protected by "#if __has_extension(c_generic_selections)". I installed this patch to work around the problem: https://github.com/eggert/tz/commit/ce293ccb574a77956c758a81287dbc8ef4f4eedf along with some other patches I just now emailed to the list. This is by no means a full set of tests on FreeBSD, so any further testing you can do would be appreciated, even if the testing is after the next TZDB release is published.
participants (4)
-
Alfred Finamore -
Paul Eggert -
Philip Paeps -
Tim Parenti