summaryrefslogtreecommitdiff
path: root/tzselect.ksh
diff options
context:
space:
mode:
Diffstat (limited to 'tzselect.ksh')
-rw-r--r--tzselect.ksh287
1 files changed, 197 insertions, 90 deletions
diff --git a/tzselect.ksh b/tzselect.ksh
index 28c32a2430f8..9a91acfc7412 100644
--- a/tzselect.ksh
+++ b/tzselect.ksh
@@ -39,7 +39,7 @@ REPORT_BUGS_TO=tz@iana.org
: ${AWK=awk}
: ${TZDIR=`pwd`}
-# Output one argument as-is to standard output.
+# Output one argument as-is to standard output, with trailing newline.
# Safer than 'echo', which can mishandle '\' or leading '-'.
say() {
printf '%s\n' "$1"
@@ -82,8 +82,8 @@ Report bugs to $REPORT_BUGS_TO."
# Ask the user to select from the function's arguments,
# and assign the selected argument to the variable 'select_result'.
-# Exit on EOF or I/O error. Use the shell's 'select' builtin if available,
-# falling back on a less-nice but portable substitute otherwise.
+# Exit on EOF or I/O error. Use the shell's nicer 'select' builtin if
+# available, falling back on a portable substitute otherwise.
if
case $BASH_VERSION in
?*) : ;;
@@ -197,16 +197,65 @@ newline='
'
IFS=$newline
+# Awk script to output a country list.
+output_country_list='
+ BEGIN { FS = "\t" }
+ /^#$/ { next }
+ /^#[^@]/ { next }
+ {
+ commentary = $0 ~ /^#@/
+ if (commentary) {
+ 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
+ }
+ }
+ if (elsewhere) {
+ for (i = 1; i <= ncc; i++) {
+ cc_elsewhere[cc[i]] = 1
+ }
+ }
+ }
+ }
+ END {
+ while (getline <TZ_COUNTRY_TABLE) {
+ 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 read a time zone table and output the same table,
-# with each column preceded by its distance from 'here'.
-output_distances='
+# 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.
+output_distances_or_times='
BEGIN {
FS = "\t"
- while (getline <TZ_COUNTRY_TABLE)
- if ($0 ~ /^[^#]/)
- country[$1] = $2
- country["US"] = "US" # Otherwise the strings get too long.
+ if (!output_times) {
+ while (getline <TZ_COUNTRY_TABLE)
+ if ($0 ~ /^[^#]/)
+ country[$1] = $2
+ country["US"] = "US" # Otherwise the strings get too long.
+ }
}
function abs(x) {
return x < 0 ? -x : x;
@@ -268,18 +317,35 @@ output_distances='
coord_long = convert_longitude(coord)
}
/^[^#]/ {
- here_lat = convert_latitude($2)
- here_long = convert_longitude($2)
+ inline[inlines++] = $0
+ ncc = split($1, cc, /,/)
+ for (i = 1; i <= ncc; i++)
+ cc_used[cc[i]]++
+ }
+ END {
+ for (h = 0; h < inlines; h++) {
+ $0 = inline[h]
line = $1 "\t" $2 "\t" $3
sep = "\t"
ncc = split($1, cc, /,/)
+ split("", item_seen)
+ item_seen[""] = 1
for (i = 1; i <= ncc; i++) {
- line = line sep country[cc[i]]
- sep = ", "
+ item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4
+ if (item_seen[item]++) continue
+ line = line 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
+ } 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
}
- if (NF == 4)
- line = line " - " $4
- printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
+ }
}
'
@@ -300,7 +366,7 @@ while
# Ask the user for continent or ocean.
- echo >&2 'Please select a continent, ocean, "coord", or "TZ".'
+ echo >&2 'Please select a continent, ocean, "coord", "TZ", or "time".'
quoted_continents=`
$AWK '
@@ -331,7 +397,8 @@ while
eval '
doselect '"$quoted_continents"' \
"coord - I want to use geographical coordinates." \
- "TZ - I want to specify the timezone using the Posix TZ format."
+ "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;;
@@ -384,74 +451,99 @@ while
distance_table=`$AWK \
-v coord="$coord" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
- "$output_distances" <"$TZ_ZONE_TABLE" |
+ "$output_distances_or_times" <"$TZ_ZONE_TABLE" |
sort -n |
sed "${location_limit}q"
`
- regions=`say "$distance_table" | $AWK '
- BEGIN { FS = "\t" }
- { print $NF }
+ regions=`$AWK \
+ -v distance_table="$distance_table" '
+ BEGIN {
+ nlines = split(distance_table, line, /\n/)
+ for (nr = 1; nr <= nlines; nr++) {
+ nf = split(line[nr], f, /\t/)
+ print f[nf]
+ }
+ }
'`
- echo >&2 'Please select one of the following timezones,' \
+ 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=`say "$distance_table" | $AWK -v region="$region" '
- BEGIN { FS="\t" }
- $NF == region { print $4 }
+ TZ=`$AWK \
+ -v distance_table="$distance_table" \
+ -v region="$region" '
+ BEGIN {
+ 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]
+ }
+ }
+ }
'`
;;
*)
- # Get list of names of countries in the continent or ocean.
- countries=`$AWK \
+ 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?"
+ 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
+ }'
+ `
+ time=$select_result
+ zone_table=`
+ say "$time_table" |
+ $AWK -v time="$time" '{
+ if ($6 " " $7 " " $4 " " $5 == time) {
+ sub(/[^\t]*\t/, "")
+ print
+ }
+ }'
+ `
+ countries=`
+ say "$zone_table" |
+ $AWK \
+ -v continent_re='' \
+ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
+ "$output_country_list" |
+ 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" \
- '
- BEGIN { FS = "\t" }
- /^#$/ { next }
- /^#[^@]/ { next }
- {
- commentary = $0 ~ /^#@/
- if (commentary) {
- 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
- }
- }
- if (elsewhere) {
- for (i = 1; i <= ncc; i++) {
- cc_elsewhere[cc[i]] = 1
- }
- }
- }
- }
- END {
- while (getline <TZ_COUNTRY_TABLE) {
- 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
- }
- }
- ' <"$TZ_ZONE_TABLE" | sort -f`
-
+ "$output_country_list" \
+ <"$TZ_ZONE_TABLE" | sort -f
+ `;;
+ esac
# If there's more than one country, ask the user which one.
case $countries in
@@ -459,6 +551,7 @@ while
echo >&2 'Please select a country' \
'whose clocks agree with yours.'
doselect $countries
+ country_result=$select_result
country=$select_result;;
*)
country=$countries
@@ -466,10 +559,15 @@ while
# Get list of timezones in the country.
- regions=`$AWK \
+ regions=`
+ case $zone_table in
+ file) cat -- "$TZ_ZONE_TABLE";;
+ *) say "$zone_table";;
+ esac |
+ $AWK \
-v country="$country" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
- '
+ '
BEGIN {
FS = "\t"
cc = country
@@ -482,7 +580,8 @@ while
}
/^#/ { next }
$1 ~ cc { print $4 }
- ' <"$TZ_ZONE_TABLE"`
+ '
+ `
# If there's more than one region, ask the user which one.
@@ -490,17 +589,20 @@ while
*"$newline"*)
echo >&2 'Please select one of the following timezones.'
doselect $regions
- region=$select_result;;
- *)
- region=$regions
+ region=$select_result
esac
# Determine TZ from country and region.
- TZ=`$AWK \
+ TZ=`
+ case $zone_table in
+ file) cat -- "$TZ_ZONE_TABLE";;
+ *) say "$zone_table";;
+ esac |
+ $AWK \
-v country="$country" \
-v region="$region" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
- '
+ '
BEGIN {
FS = "\t"
cc = country
@@ -512,8 +614,9 @@ while
}
}
/^#/ { next }
- $1 ~ cc && $4 == region { print $3 }
- ' <"$TZ_ZONE_TABLE"`
+ $1 ~ cc && ($4 == region || !region) { print $3 }
+ '
+ `;;
esac
# Make sure the corresponding zoneinfo file exists.
@@ -549,17 +652,21 @@ Universal Time is now: $UTdate."
# Output TZ info and ask the user to confirm.
echo >&2 ""
- echo >&2 "The following information has been given:"
+ echo >&2 "Based on the following information:"
echo >&2 ""
- case $country%$region%$coord in
- ?*%?*%) say >&2 " $country$newline $region";;
- ?*%%) say >&2 " $country";;
- %?*%?*) say >&2 " coord $coord$newline $region";;
- %%?*) say >&2 " coord $coord";;
+ 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 "Therefore 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