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"