Home | History | Annotate | Line # | Download | only in time
tzselect.ksh revision 1.1.1.4
      1      1.1     jtc #! /bin/ksh
      2      1.1     jtc # Ask the user about the time zone, and output the resulting TZ value to stdout.
      3      1.1     jtc # Interact with the user via stderr and stdin.
      4      1.1     jtc 
      5      1.1     jtc # Contributed by Paul Eggert <eggert (at] twinsun.com>.
      6      1.1     jtc 
      7      1.1     jtc # Porting notes:
      8      1.1     jtc #
      9      1.1     jtc # This script requires several features of the Korn shell.
     10      1.1     jtc # If your host lacks the Korn shell,
     11      1.1     jtc # you can use either of the following free programs instead:
     12      1.1     jtc #
     13  1.1.1.4  kleink #	<a href=ftp://ftp.gnu.org/pub/gnu/>
     14      1.1     jtc #	Bourne-Again shell (bash)
     15  1.1.1.3     jtc #	</a>
     16      1.1     jtc #
     17  1.1.1.3     jtc #	<a href=ftp://ftp.cs.mun.ca/pub/pdksh/pdksh.tar.gz>
     18      1.1     jtc #	Public domain ksh
     19  1.1.1.3     jtc #	</a>
     20      1.1     jtc #
     21      1.1     jtc # This script also uses several features of modern awk programs.
     22      1.1     jtc # If your host lacks awk, or has an old awk that does not conform to Posix.2,
     23      1.1     jtc # you can use either of the following free programs instead:
     24      1.1     jtc #
     25  1.1.1.4  kleink #	<a href=ftp://ftp.gnu.org/pub/gnu/>
     26      1.1     jtc #	GNU awk (gawk)
     27  1.1.1.3     jtc #	</a>
     28      1.1     jtc #
     29  1.1.1.3     jtc #	<a href=ftp://ftp.whidbey.net/pub/brennan/>
     30      1.1     jtc #	mawk
     31  1.1.1.3     jtc #	</a>
     32      1.1     jtc 
     33      1.1     jtc 
     34      1.1     jtc # Specify default values for environment variables if they are unset.
     35      1.1     jtc : ${AWK=awk}
     36      1.1     jtc : ${TZDIR=$(pwd)}
     37      1.1     jtc 
     38      1.1     jtc # Check for awk Posix compliance.
     39      1.1     jtc ($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
     40      1.1     jtc [ $? = 123 ] || {
     41      1.1     jtc 	echo >&2 "$0: Sorry, your \`$AWK' program is not Posix compatible."
     42      1.1     jtc 	exit 1
     43      1.1     jtc }
     44      1.1     jtc 
     45      1.1     jtc # Make sure the tables are readable.
     46      1.1     jtc TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
     47      1.1     jtc TZ_ZONE_TABLE=$TZDIR/zone.tab
     48      1.1     jtc for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
     49      1.1     jtc do
     50      1.1     jtc 	<$f || {
     51      1.1     jtc 		echo >&2 "$0: time zone files are not set up correctly"
     52      1.1     jtc 		exit 1
     53      1.1     jtc 	}
     54      1.1     jtc done
     55      1.1     jtc 
     56      1.1     jtc newline='
     57      1.1     jtc '
     58      1.1     jtc IFS=$newline
     59      1.1     jtc 
     60      1.1     jtc 
     61  1.1.1.2     jtc # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
     62      1.1     jtc case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in
     63      1.1     jtc ?*) PS3=
     64      1.1     jtc esac
     65      1.1     jtc 
     66      1.1     jtc 
     67      1.1     jtc # Begin the main loop.  We come back here if the user wants to retry.
     68      1.1     jtc while
     69      1.1     jtc 
     70      1.1     jtc 	echo >&2 'Please identify a location' \
     71      1.1     jtc 		'so that time zone rules can be set correctly.'
     72      1.1     jtc 
     73      1.1     jtc 	continent=
     74      1.1     jtc 	country=
     75      1.1     jtc 	region=
     76      1.1     jtc 
     77      1.1     jtc 
     78      1.1     jtc 	# Ask the user for continent or ocean.
     79      1.1     jtc 
     80      1.1     jtc 	echo >&2 'Please select a continent or ocean.'
     81      1.1     jtc 
     82      1.1     jtc 	select continent in \
     83      1.1     jtc 	    Africa \
     84      1.1     jtc 	    Americas \
     85      1.1     jtc 	    Antarctica \
     86      1.1     jtc 	    'Arctic Ocean' \
     87      1.1     jtc 	    Asia \
     88      1.1     jtc 	    'Atlantic Ocean' \
     89      1.1     jtc 	    Australia \
     90      1.1     jtc 	    Europe \
     91      1.1     jtc 	    'Indian Ocean' \
     92      1.1     jtc 	    'Pacific Ocean' \
     93      1.1     jtc 	    'none - I want to specify the time zone using the Posix TZ format.'
     94      1.1     jtc 	do
     95      1.1     jtc 	    case $continent in
     96      1.1     jtc 	    '')
     97      1.1     jtc 		echo >&2 'Please enter a number in range.';;
     98      1.1     jtc 	    ?*)
     99      1.1     jtc 		case $continent in
    100      1.1     jtc 		Americas) continent=America;;
    101      1.1     jtc 		*' '*) continent=$(expr "$continent" : '\([^ ]*\)')
    102      1.1     jtc 		esac
    103      1.1     jtc 		break
    104      1.1     jtc 	    esac
    105      1.1     jtc 	done
    106      1.1     jtc 	case $continent in
    107      1.1     jtc 	'')
    108      1.1     jtc 		exit 1;;
    109      1.1     jtc 	none)
    110      1.1     jtc 		# Ask the user for a Posix TZ string.  Check that it conforms.
    111      1.1     jtc 		while
    112      1.1     jtc 			echo >&2 'Please enter the desired value' \
    113      1.1     jtc 				'of the TZ environment variable.'
    114      1.1     jtc 			echo >&2 'For example, GST-10 is a zone named GST' \
    115      1.1     jtc 				'that is 10 hours ahead (east) of UTC.'
    116      1.1     jtc 			read TZ
    117      1.1     jtc 			$AWK -v TZ="$TZ" 'BEGIN {
    118      1.1     jtc 				tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+"
    119      1.1     jtc 				time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?"
    120      1.1     jtc 				offset = "[-+]?" time
    121      1.1     jtc 				date = "(J?[0-9]+|M[0-9]+\.[0-9]+\.[0-9]+)"
    122      1.1     jtc 				datetime = "," date "(/" time ")?"
    123      1.1     jtc 				tzpattern = "^(:.*|" tzname offset "(" tzname \
    124      1.1     jtc 				  "(" offset ")?(" datetime datetime ")?)?)$"
    125      1.1     jtc 				if (TZ ~ tzpattern) exit 1
    126      1.1     jtc 				exit 0
    127      1.1     jtc 			}'
    128      1.1     jtc 		do
    129      1.1     jtc 			echo >&2 "\`$TZ' is not a conforming" \
    130      1.1     jtc 				'Posix time zone string.'
    131      1.1     jtc 		done
    132      1.1     jtc 		TZ_for_date=$TZ;;
    133      1.1     jtc 	*)
    134      1.1     jtc 		# Get list of names of countries in the continent or ocean.
    135      1.1     jtc 		countries=$($AWK -F'\t' \
    136      1.1     jtc 			-v continent="$continent" \
    137      1.1     jtc 			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
    138      1.1     jtc 		'
    139      1.1     jtc 			/^#/ { next }
    140      1.1     jtc 			$3 ~ ("^" continent "/") {
    141      1.1     jtc 				if (!cc_seen[$1]++) cc_list[++ccs] = $1
    142      1.1     jtc 			}
    143      1.1     jtc 			END {
    144      1.1     jtc 				while (getline <TZ_COUNTRY_TABLE) {
    145      1.1     jtc 					if ($0 !~ /^#/) cc_name[$1] = $2
    146      1.1     jtc 				}
    147      1.1     jtc 				for (i = 1; i <= ccs; i++) {
    148      1.1     jtc 					country = cc_list[i]
    149      1.1     jtc 					if (cc_name[country]) {
    150      1.1     jtc 					  country = cc_name[country]
    151      1.1     jtc 					}
    152      1.1     jtc 					print country
    153      1.1     jtc 				}
    154      1.1     jtc 			}
    155      1.1     jtc 		' <$TZ_ZONE_TABLE | sort -f)
    156      1.1     jtc 
    157      1.1     jtc 
    158      1.1     jtc 		# If there's more than one country, ask the user which one.
    159      1.1     jtc 		case $countries in
    160      1.1     jtc 		*"$newline"*)
    161      1.1     jtc 			echo >&2 'Please select a country.'
    162      1.1     jtc 			select country in $countries
    163      1.1     jtc 			do
    164      1.1     jtc 			    case $country in
    165      1.1     jtc 			    '') echo >&2 'Please enter a number in range.';;
    166      1.1     jtc 			    ?*) break
    167      1.1     jtc 			    esac
    168      1.1     jtc 			done
    169      1.1     jtc 
    170      1.1     jtc 			case $country in
    171      1.1     jtc 			'') exit 1
    172      1.1     jtc 			esac;;
    173      1.1     jtc 		*)
    174      1.1     jtc 			country=$countries
    175      1.1     jtc 		esac
    176      1.1     jtc 
    177      1.1     jtc 
    178      1.1     jtc 		# Get list of names of time zone rule regions in the country.
    179      1.1     jtc 		regions=$($AWK -F'\t' \
    180      1.1     jtc 			-v country="$country" \
    181      1.1     jtc 			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
    182      1.1     jtc 		'
    183      1.1     jtc 			BEGIN {
    184      1.1     jtc 				cc = country
    185      1.1     jtc 				while (getline <TZ_COUNTRY_TABLE) {
    186      1.1     jtc 					if ($0 !~ /^#/  &&  country == $2) {
    187      1.1     jtc 						cc = $1
    188      1.1     jtc 						break
    189      1.1     jtc 					}
    190      1.1     jtc 				}
    191      1.1     jtc 			}
    192      1.1     jtc 			$1 == cc { print $4 }
    193      1.1     jtc 		' <$TZ_ZONE_TABLE)
    194      1.1     jtc 
    195      1.1     jtc 
    196      1.1     jtc 		# If there's more than one region, ask the user which one.
    197      1.1     jtc 		case $regions in
    198      1.1     jtc 		*"$newline"*)
    199      1.1     jtc 			echo >&2 'Please select one of the following' \
    200      1.1     jtc 				'time zone regions.'
    201      1.1     jtc 			select region in $regions
    202      1.1     jtc 			do
    203      1.1     jtc 				case $region in
    204      1.1     jtc 				'') echo >&2 'Please enter a number in range.';;
    205      1.1     jtc 				?*) break
    206      1.1     jtc 				esac
    207      1.1     jtc 			done
    208      1.1     jtc 			case $region in
    209      1.1     jtc 			'') exit 1
    210      1.1     jtc 			esac;;
    211      1.1     jtc 		*)
    212      1.1     jtc 			region=$regions
    213      1.1     jtc 		esac
    214      1.1     jtc 
    215      1.1     jtc 		# Determine TZ from country and region.
    216      1.1     jtc 		TZ=$($AWK -F'\t' \
    217      1.1     jtc 			-v country="$country" \
    218      1.1     jtc 			-v region="$region" \
    219      1.1     jtc 			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
    220      1.1     jtc 		'
    221      1.1     jtc 			BEGIN {
    222      1.1     jtc 				cc = country
    223      1.1     jtc 				while (getline <TZ_COUNTRY_TABLE) {
    224      1.1     jtc 					if ($0 !~ /^#/  &&  country == $2) {
    225      1.1     jtc 						cc = $1
    226      1.1     jtc 						break
    227      1.1     jtc 					}
    228      1.1     jtc 				}
    229      1.1     jtc 			}
    230      1.1     jtc 			$1 == cc && $4 == region { print $3 }
    231      1.1     jtc 		' <$TZ_ZONE_TABLE)
    232      1.1     jtc 
    233      1.1     jtc 		# Make sure the corresponding zoneinfo file exists.
    234      1.1     jtc 		TZ_for_date=$TZDIR/$TZ
    235      1.1     jtc 		<$TZ_for_date || {
    236      1.1     jtc 			echo >&2 "$0: time zone files are not set up correctly"
    237      1.1     jtc 			exit 1
    238      1.1     jtc 		}
    239      1.1     jtc 	esac
    240      1.1     jtc 
    241      1.1     jtc 
    242      1.1     jtc 	# Use the proposed TZ to output the current date relative to UTC.
    243      1.1     jtc 	# Loop until they agree in seconds.
    244      1.1     jtc 	# Give up after 8 unsuccessful tries.
    245      1.1     jtc 
    246      1.1     jtc 	extra_info=
    247      1.1     jtc 	for i in 1 2 3 4 5 6 7 8
    248      1.1     jtc 	do
    249      1.1     jtc 		TZdate=$(LANG=C TZ="$TZ_for_date" date)
    250      1.1     jtc 		UTdate=$(LANG=C TZ=UTC0 date)
    251      1.1     jtc 		TZsec=$(expr "$TZdate" : '.*:\([0-5][0-9]\)')
    252      1.1     jtc 		UTsec=$(expr "$UTdate" : '.*:\([0-5][0-9]\)')
    253      1.1     jtc 		case $TZsec in
    254      1.1     jtc 		$UTsec)
    255      1.1     jtc 			extra_info="
    256      1.1     jtc Local time is now:	$TZdate.
    257      1.1     jtc Universal Time is now:	$UTdate."
    258      1.1     jtc 			break
    259      1.1     jtc 		esac
    260      1.1     jtc 	done
    261      1.1     jtc 
    262      1.1     jtc 
    263      1.1     jtc 	# Output TZ info and ask the user to confirm.
    264      1.1     jtc 
    265      1.1     jtc 	echo >&2 ""
    266      1.1     jtc 	echo >&2 "The following information has been given:"
    267      1.1     jtc 	echo >&2 ""
    268      1.1     jtc 	case $country+$region in
    269      1.1     jtc 	?*+?*)	echo >&2 "	$country$newline	$region";;
    270      1.1     jtc 	?*+)	echo >&2 "	$country";;
    271      1.1     jtc 	+)	echo >&2 "	TZ='$TZ'"
    272      1.1     jtc 	esac
    273      1.1     jtc 	echo >&2 ""
    274      1.1     jtc 	echo >&2 "Therefore TZ='$TZ' will be used.$extra_info"
    275      1.1     jtc 	echo >&2 "Is the above information OK?"
    276      1.1     jtc 
    277      1.1     jtc 	ok=
    278      1.1     jtc 	select ok in Yes No
    279      1.1     jtc 	do
    280      1.1     jtc 	    case $ok in
    281      1.1     jtc 	    '') echo >&2 'Please enter 1 for Yes, or 2 for No.';;
    282      1.1     jtc 	    ?*) break
    283      1.1     jtc 	    esac
    284      1.1     jtc 	done
    285      1.1     jtc 	case $ok in
    286      1.1     jtc 	'') exit 1;;
    287      1.1     jtc 	Yes) break
    288      1.1     jtc 	esac
    289      1.1     jtc do :
    290      1.1     jtc done
    291      1.1     jtc 
    292      1.1     jtc # Output the answer.
    293      1.1     jtc echo "$TZ"
    294