Home | History | Annotate | Line # | Download | only in time
tzselect.ksh revision 1.6
      1  1.1      jtc #! /bin/ksh
      2  1.2    perry #
      3  1.6  mlelstv #	$NetBSD: tzselect.ksh,v 1.6 2009/12/31 22:49:16 mlelstv Exp $
      4  1.2    perry #
      5  1.6  mlelstv VERSION='@(#)tzselect.ksh	8.2'
      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.6  mlelstv # Contributed by Paul Eggert.
     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.6  mlelstv if [ "$1" = "--help" ]; then
     51  1.6  mlelstv     cat <<EOF
     52  1.6  mlelstv Usage: tzselect
     53  1.6  mlelstv Select a time zone interactively.
     54  1.6  mlelstv 
     55  1.6  mlelstv Report bugs to tz (at] elsie.nci.nih.gov.
     56  1.6  mlelstv EOF
     57  1.6  mlelstv     exit 0
     58  1.6  mlelstv elif [ "$1" = "--version" ]; then
     59  1.6  mlelstv     cat <<EOF
     60  1.6  mlelstv tzselect $VERSION
     61  1.6  mlelstv EOF
     62  1.6  mlelstv     exit 0
     63  1.6  mlelstv fi
     64  1.6  mlelstv 
     65  1.1      jtc # Make sure the tables are readable.
     66  1.1      jtc TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
     67  1.1      jtc TZ_ZONE_TABLE=$TZDIR/zone.tab
     68  1.1      jtc for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
     69  1.1      jtc do
     70  1.1      jtc 	<$f || {
     71  1.1      jtc 		echo >&2 "$0: time zone files are not set up correctly"
     72  1.1      jtc 		exit 1
     73  1.1      jtc 	}
     74  1.1      jtc done
     75  1.1      jtc 
     76  1.1      jtc newline='
     77  1.1      jtc '
     78  1.1      jtc IFS=$newline
     79  1.1      jtc 
     80  1.1      jtc 
     81  1.2    perry # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
     82  1.1      jtc case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in
     83  1.1      jtc ?*) PS3=
     84  1.1      jtc esac
     85  1.1      jtc 
     86  1.1      jtc 
     87  1.1      jtc # Begin the main loop.  We come back here if the user wants to retry.
     88  1.1      jtc while
     89  1.1      jtc 
     90  1.1      jtc 	echo >&2 'Please identify a location' \
     91  1.1      jtc 		'so that time zone rules can be set correctly.'
     92  1.1      jtc 
     93  1.1      jtc 	continent=
     94  1.1      jtc 	country=
     95  1.1      jtc 	region=
     96  1.1      jtc 
     97  1.1      jtc 
     98  1.1      jtc 	# Ask the user for continent or ocean.
     99  1.1      jtc 
    100  1.1      jtc 	echo >&2 'Please select a continent or ocean.'
    101  1.1      jtc 
    102  1.1      jtc 	select continent in \
    103  1.1      jtc 	    Africa \
    104  1.1      jtc 	    Americas \
    105  1.1      jtc 	    Antarctica \
    106  1.1      jtc 	    'Arctic Ocean' \
    107  1.1      jtc 	    Asia \
    108  1.1      jtc 	    'Atlantic Ocean' \
    109  1.1      jtc 	    Australia \
    110  1.1      jtc 	    Europe \
    111  1.1      jtc 	    'Indian Ocean' \
    112  1.1      jtc 	    'Pacific Ocean' \
    113  1.1      jtc 	    'none - I want to specify the time zone using the Posix TZ format.'
    114  1.1      jtc 	do
    115  1.1      jtc 	    case $continent in
    116  1.1      jtc 	    '')
    117  1.1      jtc 		echo >&2 'Please enter a number in range.';;
    118  1.1      jtc 	    ?*)
    119  1.1      jtc 		case $continent in
    120  1.1      jtc 		Americas) continent=America;;
    121  1.1      jtc 		*' '*) continent=$(expr "$continent" : '\([^ ]*\)')
    122  1.1      jtc 		esac
    123  1.1      jtc 		break
    124  1.1      jtc 	    esac
    125  1.1      jtc 	done
    126  1.1      jtc 	case $continent in
    127  1.1      jtc 	'')
    128  1.1      jtc 		exit 1;;
    129  1.1      jtc 	none)
    130  1.1      jtc 		# Ask the user for a Posix TZ string.  Check that it conforms.
    131  1.1      jtc 		while
    132  1.1      jtc 			echo >&2 'Please enter the desired value' \
    133  1.1      jtc 				'of the TZ environment variable.'
    134  1.1      jtc 			echo >&2 'For example, GST-10 is a zone named GST' \
    135  1.1      jtc 				'that is 10 hours ahead (east) of UTC.'
    136  1.1      jtc 			read TZ
    137  1.1      jtc 			$AWK -v TZ="$TZ" 'BEGIN {
    138  1.1      jtc 				tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+"
    139  1.1      jtc 				time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?"
    140  1.1      jtc 				offset = "[-+]?" time
    141  1.1      jtc 				date = "(J?[0-9]+|M[0-9]+\.[0-9]+\.[0-9]+)"
    142  1.1      jtc 				datetime = "," date "(/" time ")?"
    143  1.1      jtc 				tzpattern = "^(:.*|" tzname offset "(" tzname \
    144  1.1      jtc 				  "(" offset ")?(" datetime datetime ")?)?)$"
    145  1.1      jtc 				if (TZ ~ tzpattern) exit 1
    146  1.1      jtc 				exit 0
    147  1.1      jtc 			}'
    148  1.1      jtc 		do
    149  1.1      jtc 			echo >&2 "\`$TZ' is not a conforming" \
    150  1.1      jtc 				'Posix time zone string.'
    151  1.1      jtc 		done
    152  1.1      jtc 		TZ_for_date=$TZ;;
    153  1.1      jtc 	*)
    154  1.1      jtc 		# Get list of names of countries in the continent or ocean.
    155  1.1      jtc 		countries=$($AWK -F'\t' \
    156  1.1      jtc 			-v continent="$continent" \
    157  1.1      jtc 			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
    158  1.1      jtc 		'
    159  1.1      jtc 			/^#/ { next }
    160  1.1      jtc 			$3 ~ ("^" continent "/") {
    161  1.1      jtc 				if (!cc_seen[$1]++) cc_list[++ccs] = $1
    162  1.1      jtc 			}
    163  1.1      jtc 			END {
    164  1.1      jtc 				while (getline <TZ_COUNTRY_TABLE) {
    165  1.1      jtc 					if ($0 !~ /^#/) cc_name[$1] = $2
    166  1.1      jtc 				}
    167  1.1      jtc 				for (i = 1; i <= ccs; i++) {
    168  1.1      jtc 					country = cc_list[i]
    169  1.1      jtc 					if (cc_name[country]) {
    170  1.1      jtc 					  country = cc_name[country]
    171  1.1      jtc 					}
    172  1.1      jtc 					print country
    173  1.1      jtc 				}
    174  1.1      jtc 			}
    175  1.1      jtc 		' <$TZ_ZONE_TABLE | sort -f)
    176  1.1      jtc 
    177  1.1      jtc 
    178  1.1      jtc 		# If there's more than one country, ask the user which one.
    179  1.1      jtc 		case $countries in
    180  1.1      jtc 		*"$newline"*)
    181  1.1      jtc 			echo >&2 'Please select a country.'
    182  1.1      jtc 			select country in $countries
    183  1.1      jtc 			do
    184  1.1      jtc 			    case $country in
    185  1.1      jtc 			    '') echo >&2 'Please enter a number in range.';;
    186  1.1      jtc 			    ?*) break
    187  1.1      jtc 			    esac
    188  1.1      jtc 			done
    189  1.1      jtc 
    190  1.1      jtc 			case $country in
    191  1.1      jtc 			'') exit 1
    192  1.1      jtc 			esac;;
    193  1.1      jtc 		*)
    194  1.1      jtc 			country=$countries
    195  1.1      jtc 		esac
    196  1.1      jtc 
    197  1.1      jtc 
    198  1.1      jtc 		# Get list of names of time zone rule regions in the country.
    199  1.1      jtc 		regions=$($AWK -F'\t' \
    200  1.1      jtc 			-v country="$country" \
    201  1.1      jtc 			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
    202  1.1      jtc 		'
    203  1.1      jtc 			BEGIN {
    204  1.1      jtc 				cc = country
    205  1.1      jtc 				while (getline <TZ_COUNTRY_TABLE) {
    206  1.1      jtc 					if ($0 !~ /^#/  &&  country == $2) {
    207  1.1      jtc 						cc = $1
    208  1.1      jtc 						break
    209  1.1      jtc 					}
    210  1.1      jtc 				}
    211  1.1      jtc 			}
    212  1.1      jtc 			$1 == cc { print $4 }
    213  1.1      jtc 		' <$TZ_ZONE_TABLE)
    214  1.1      jtc 
    215  1.1      jtc 
    216  1.1      jtc 		# If there's more than one region, ask the user which one.
    217  1.1      jtc 		case $regions in
    218  1.1      jtc 		*"$newline"*)
    219  1.1      jtc 			echo >&2 'Please select one of the following' \
    220  1.1      jtc 				'time zone regions.'
    221  1.1      jtc 			select region in $regions
    222  1.1      jtc 			do
    223  1.1      jtc 				case $region in
    224  1.1      jtc 				'') echo >&2 'Please enter a number in range.';;
    225  1.1      jtc 				?*) break
    226  1.1      jtc 				esac
    227  1.1      jtc 			done
    228  1.1      jtc 			case $region in
    229  1.1      jtc 			'') exit 1
    230  1.1      jtc 			esac;;
    231  1.1      jtc 		*)
    232  1.1      jtc 			region=$regions
    233  1.1      jtc 		esac
    234  1.1      jtc 
    235  1.1      jtc 		# Determine TZ from country and region.
    236  1.1      jtc 		TZ=$($AWK -F'\t' \
    237  1.1      jtc 			-v country="$country" \
    238  1.1      jtc 			-v region="$region" \
    239  1.1      jtc 			-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
    240  1.1      jtc 		'
    241  1.1      jtc 			BEGIN {
    242  1.1      jtc 				cc = country
    243  1.1      jtc 				while (getline <TZ_COUNTRY_TABLE) {
    244  1.1      jtc 					if ($0 !~ /^#/  &&  country == $2) {
    245  1.1      jtc 						cc = $1
    246  1.1      jtc 						break
    247  1.1      jtc 					}
    248  1.1      jtc 				}
    249  1.1      jtc 			}
    250  1.1      jtc 			$1 == cc && $4 == region { print $3 }
    251  1.1      jtc 		' <$TZ_ZONE_TABLE)
    252  1.1      jtc 
    253  1.1      jtc 		# Make sure the corresponding zoneinfo file exists.
    254  1.1      jtc 		TZ_for_date=$TZDIR/$TZ
    255  1.1      jtc 		<$TZ_for_date || {
    256  1.1      jtc 			echo >&2 "$0: time zone files are not set up correctly"
    257  1.1      jtc 			exit 1
    258  1.1      jtc 		}
    259  1.1      jtc 	esac
    260  1.1      jtc 
    261  1.1      jtc 
    262  1.1      jtc 	# Use the proposed TZ to output the current date relative to UTC.
    263  1.1      jtc 	# Loop until they agree in seconds.
    264  1.1      jtc 	# Give up after 8 unsuccessful tries.
    265  1.1      jtc 
    266  1.1      jtc 	extra_info=
    267  1.1      jtc 	for i in 1 2 3 4 5 6 7 8
    268  1.1      jtc 	do
    269  1.1      jtc 		TZdate=$(LANG=C TZ="$TZ_for_date" date)
    270  1.1      jtc 		UTdate=$(LANG=C TZ=UTC0 date)
    271  1.1      jtc 		TZsec=$(expr "$TZdate" : '.*:\([0-5][0-9]\)')
    272  1.1      jtc 		UTsec=$(expr "$UTdate" : '.*:\([0-5][0-9]\)')
    273  1.1      jtc 		case $TZsec in
    274  1.1      jtc 		$UTsec)
    275  1.1      jtc 			extra_info="
    276  1.1      jtc Local time is now:	$TZdate.
    277  1.1      jtc Universal Time is now:	$UTdate."
    278  1.1      jtc 			break
    279  1.1      jtc 		esac
    280  1.1      jtc 	done
    281  1.1      jtc 
    282  1.1      jtc 
    283  1.1      jtc 	# Output TZ info and ask the user to confirm.
    284  1.1      jtc 
    285  1.1      jtc 	echo >&2 ""
    286  1.1      jtc 	echo >&2 "The following information has been given:"
    287  1.1      jtc 	echo >&2 ""
    288  1.1      jtc 	case $country+$region in
    289  1.1      jtc 	?*+?*)	echo >&2 "	$country$newline	$region";;
    290  1.1      jtc 	?*+)	echo >&2 "	$country";;
    291  1.1      jtc 	+)	echo >&2 "	TZ='$TZ'"
    292  1.1      jtc 	esac
    293  1.1      jtc 	echo >&2 ""
    294  1.1      jtc 	echo >&2 "Therefore TZ='$TZ' will be used.$extra_info"
    295  1.1      jtc 	echo >&2 "Is the above information OK?"
    296  1.1      jtc 
    297  1.1      jtc 	ok=
    298  1.1      jtc 	select ok in Yes No
    299  1.1      jtc 	do
    300  1.1      jtc 	    case $ok in
    301  1.1      jtc 	    '') echo >&2 'Please enter 1 for Yes, or 2 for No.';;
    302  1.1      jtc 	    ?*) break
    303  1.1      jtc 	    esac
    304  1.1      jtc 	done
    305  1.1      jtc 	case $ok in
    306  1.1      jtc 	'') exit 1;;
    307  1.1      jtc 	Yes) break
    308  1.1      jtc 	esac
    309  1.1      jtc do :
    310  1.1      jtc done
    311  1.1      jtc 
    312  1.5   kleink case $SHELL in
    313  1.5   kleink *csh) file=.login line="setenv TZ '$TZ'";;
    314  1.5   kleink *) file=.profile line="TZ='$TZ'; export TZ"
    315  1.5   kleink esac
    316  1.5   kleink 
    317  1.5   kleink echo >&2 "
    318  1.5   kleink You can make this change permanent for yourself by appending the line
    319  1.5   kleink 	$line
    320  1.5   kleink to the file '$file' in your home directory; then log out and log in again.
    321  1.5   kleink 
    322  1.5   kleink Here is that TZ value again, this time on standard output so that you
    323  1.5   kleink can use the $0 command in shell scripts:"
    324  1.5   kleink 
    325  1.1      jtc echo "$TZ"
    326