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