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