michael@0: #! /bin/ksh
michael@0:
michael@0: # '@(#)tzselect.ksh 8.1'
michael@0:
michael@0: # Ask the user about the time zone, and output the resulting TZ value to stdout.
michael@0: # Interact with the user via stderr and stdin.
michael@0:
michael@0: # Contributed by Paul Eggert.
michael@0:
michael@0: # Porting notes:
michael@0: #
michael@0: # This script requires several features of the Korn shell.
michael@0: # If your host lacks the Korn shell,
michael@0: # you can use either of the following free programs instead:
michael@0: #
michael@0: #
michael@0: # Bourne-Again shell (bash)
michael@0: #
michael@0: #
michael@0: #
michael@0: # Public domain ksh
michael@0: #
michael@0: #
michael@0: # This script also uses several features of modern awk programs.
michael@0: # If your host lacks awk, or has an old awk that does not conform to Posix.2,
michael@0: # you can use either of the following free programs instead:
michael@0: #
michael@0: #
michael@0: # GNU awk (gawk)
michael@0: #
michael@0: #
michael@0: #
michael@0: # mawk
michael@0: #
michael@0:
michael@0:
michael@0: # Specify default values for environment variables if they are unset.
michael@0: : ${AWK=awk}
michael@0: : ${TZDIR=$(pwd)}
michael@0:
michael@0: # Check for awk Posix compliance.
michael@0: ($AWK -v x=y 'BEGIN { exit 123 }') /dev/null 2>&1
michael@0: [ $? = 123 ] || {
michael@0: echo >&2 "$0: Sorry, your \`$AWK' program is not Posix compatible."
michael@0: exit 1
michael@0: }
michael@0:
michael@0: # Make sure the tables are readable.
michael@0: TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
michael@0: TZ_ZONE_TABLE=$TZDIR/zone.tab
michael@0: for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
michael@0: do
michael@0: <$f || {
michael@0: echo >&2 "$0: time zone files are not set up correctly"
michael@0: exit 1
michael@0: }
michael@0: done
michael@0:
michael@0: newline='
michael@0: '
michael@0: IFS=$newline
michael@0:
michael@0:
michael@0: # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
michael@0: case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in
michael@0: ?*) PS3=
michael@0: esac
michael@0:
michael@0:
michael@0: # Begin the main loop. We come back here if the user wants to retry.
michael@0: while
michael@0:
michael@0: echo >&2 'Please identify a location' \
michael@0: 'so that time zone rules can be set correctly.'
michael@0:
michael@0: continent=
michael@0: country=
michael@0: region=
michael@0:
michael@0:
michael@0: # Ask the user for continent or ocean.
michael@0:
michael@0: echo >&2 'Please select a continent or ocean.'
michael@0:
michael@0: select continent in \
michael@0: Africa \
michael@0: Americas \
michael@0: Antarctica \
michael@0: 'Arctic Ocean' \
michael@0: Asia \
michael@0: 'Atlantic Ocean' \
michael@0: Australia \
michael@0: Europe \
michael@0: 'Indian Ocean' \
michael@0: 'Pacific Ocean' \
michael@0: 'none - I want to specify the time zone using the Posix TZ format.'
michael@0: do
michael@0: case $continent in
michael@0: '')
michael@0: echo >&2 'Please enter a number in range.';;
michael@0: ?*)
michael@0: case $continent in
michael@0: Americas) continent=America;;
michael@0: *' '*) continent=$(expr "$continent" : '\([^ ]*\)')
michael@0: esac
michael@0: break
michael@0: esac
michael@0: done
michael@0: case $continent in
michael@0: '')
michael@0: exit 1;;
michael@0: none)
michael@0: # Ask the user for a Posix TZ string. Check that it conforms.
michael@0: while
michael@0: echo >&2 'Please enter the desired value' \
michael@0: 'of the TZ environment variable.'
michael@0: echo >&2 'For example, GST-10 is a zone named GST' \
michael@0: 'that is 10 hours ahead (east) of UTC.'
michael@0: read TZ
michael@0: $AWK -v TZ="$TZ" 'BEGIN {
michael@0: tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+"
michael@0: time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?"
michael@0: offset = "[-+]?" time
michael@0: date = "(J?[0-9]+|M[0-9]+\.[0-9]+\.[0-9]+)"
michael@0: datetime = "," date "(/" time ")?"
michael@0: tzpattern = "^(:.*|" tzname offset "(" tzname \
michael@0: "(" offset ")?(" datetime datetime ")?)?)$"
michael@0: if (TZ ~ tzpattern) exit 1
michael@0: exit 0
michael@0: }'
michael@0: do
michael@0: echo >&2 "\`$TZ' is not a conforming" \
michael@0: 'Posix time zone string.'
michael@0: done
michael@0: TZ_for_date=$TZ;;
michael@0: *)
michael@0: # Get list of names of countries in the continent or ocean.
michael@0: countries=$($AWK -F'\t' \
michael@0: -v continent="$continent" \
michael@0: -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
michael@0: '
michael@0: /^#/ { next }
michael@0: $3 ~ ("^" continent "/") {
michael@0: if (!cc_seen[$1]++) cc_list[++ccs] = $1
michael@0: }
michael@0: END {
michael@0: while (getline &2 'Please select a country.'
michael@0: select country in $countries
michael@0: do
michael@0: case $country in
michael@0: '') echo >&2 'Please enter a number in range.';;
michael@0: ?*) break
michael@0: esac
michael@0: done
michael@0:
michael@0: case $country in
michael@0: '') exit 1
michael@0: esac;;
michael@0: *)
michael@0: country=$countries
michael@0: esac
michael@0:
michael@0:
michael@0: # Get list of names of time zone rule regions in the country.
michael@0: regions=$($AWK -F'\t' \
michael@0: -v country="$country" \
michael@0: -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
michael@0: '
michael@0: BEGIN {
michael@0: cc = country
michael@0: while (getline &2 'Please select one of the following' \
michael@0: 'time zone regions.'
michael@0: select region in $regions
michael@0: do
michael@0: case $region in
michael@0: '') echo >&2 'Please enter a number in range.';;
michael@0: ?*) break
michael@0: esac
michael@0: done
michael@0: case $region in
michael@0: '') exit 1
michael@0: esac;;
michael@0: *)
michael@0: region=$regions
michael@0: esac
michael@0:
michael@0: # Determine TZ from country and region.
michael@0: TZ=$($AWK -F'\t' \
michael@0: -v country="$country" \
michael@0: -v region="$region" \
michael@0: -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
michael@0: '
michael@0: BEGIN {
michael@0: cc = country
michael@0: while (getline &2 "$0: time zone files are not set up correctly"
michael@0: exit 1
michael@0: }
michael@0: esac
michael@0:
michael@0:
michael@0: # Use the proposed TZ to output the current date relative to UTC.
michael@0: # Loop until they agree in seconds.
michael@0: # Give up after 8 unsuccessful tries.
michael@0:
michael@0: extra_info=
michael@0: for i in 1 2 3 4 5 6 7 8
michael@0: do
michael@0: TZdate=$(LANG=C TZ="$TZ_for_date" date)
michael@0: UTdate=$(LANG=C TZ=UTC0 date)
michael@0: TZsec=$(expr "$TZdate" : '.*:\([0-5][0-9]\)')
michael@0: UTsec=$(expr "$UTdate" : '.*:\([0-5][0-9]\)')
michael@0: case $TZsec in
michael@0: $UTsec)
michael@0: extra_info="
michael@0: Local time is now: $TZdate.
michael@0: Universal Time is now: $UTdate."
michael@0: break
michael@0: esac
michael@0: done
michael@0:
michael@0:
michael@0: # Output TZ info and ask the user to confirm.
michael@0:
michael@0: echo >&2 ""
michael@0: echo >&2 "The following information has been given:"
michael@0: echo >&2 ""
michael@0: case $country+$region in
michael@0: ?*+?*) echo >&2 " $country$newline $region";;
michael@0: ?*+) echo >&2 " $country";;
michael@0: +) echo >&2 " TZ='$TZ'"
michael@0: esac
michael@0: echo >&2 ""
michael@0: echo >&2 "Therefore TZ='$TZ' will be used.$extra_info"
michael@0: echo >&2 "Is the above information OK?"
michael@0:
michael@0: ok=
michael@0: select ok in Yes No
michael@0: do
michael@0: case $ok in
michael@0: '') echo >&2 'Please enter 1 for Yes, or 2 for No.';;
michael@0: ?*) break
michael@0: esac
michael@0: done
michael@0: case $ok in
michael@0: '') exit 1;;
michael@0: Yes) break
michael@0: esac
michael@0: do :
michael@0: done
michael@0:
michael@0: case $SHELL in
michael@0: *csh) file=.login line="setenv TZ '$TZ'";;
michael@0: *) file=.profile line="TZ='$TZ'; export TZ"
michael@0: esac
michael@0:
michael@0: echo >&2 "
michael@0: You can make this change permanent for yourself by appending the line
michael@0: $line
michael@0: to the file '$file' in your home directory; then log out and log in again.
michael@0:
michael@0: Here is that TZ value again, this time on standard output so that you
michael@0: can use the $0 command in shell scripts:"
michael@0:
michael@0: echo "$TZ"