Home | History | Annotate | Line # | Download | only in time
tzselect.ksh revision 1.23.2.1
      1  1.23.2.1  perseant #!/bin/bash
      2      1.18  christos # Ask the user about the time zone, and output the resulting TZ value to stdout.
      3      1.18  christos # Interact with the user via stderr and stdin.
      4  1.23.2.1  perseant 
      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.15  christos # Contributed by Paul Eggert.  This file is in the public domain.
     10       1.1       jtc 
     11       1.1       jtc # Porting notes:
     12       1.1       jtc #
     13  1.23.2.1  perseant # This script requires a POSIX-like shell and prefers the extension of a
     14       1.8  christos # 'select' statement.  The 'select' statement was introduced in the
     15       1.8  christos # Korn shell and is available in Bash and other shell implementations.
     16       1.8  christos # If your host lacks both Bash and the Korn shell, you can get their
     17       1.8  christos # source from one of these locations:
     18       1.1       jtc #
     19      1.16  christos #	Bash <https://www.gnu.org/software/bash/>
     20       1.8  christos #	Korn Shell <http://www.kornshell.com/>
     21      1.19  christos #	MirBSD Korn Shell <http://www.mirbsd.org/mksh.htm>
     22       1.1       jtc #
     23  1.23.2.1  perseant # This script also uses several features of POSIX awk.
     24  1.23.2.1  perseant # If your host lacks awk, or has an old awk that does not conform to POSIX,
     25  1.23.2.1  perseant # you can use any of the following free programs instead:
     26       1.1       jtc #
     27      1.16  christos #	Gawk (GNU awk) <https://www.gnu.org/software/gawk/>
     28      1.16  christos #	mawk <https://invisible-island.net/mawk/>
     29      1.21  christos #	nawk <https://github.com/onetrueawk/awk>
     30  1.23.2.1  perseant #
     31  1.23.2.1  perseant # Because 'awk "VAR=VALUE" ...' and 'awk -v "VAR=VALUE" ...' are not portable
     32  1.23.2.1  perseant # if VALUE contains \, ", or newline, awk scripts in this file use:
     33  1.23.2.1  perseant #   awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE"
     34  1.23.2.1  perseant # The substr avoids problems when VALUE is of the form X=Y and would be
     35  1.23.2.1  perseant # misinterpreted as an assignment.
     36       1.1       jtc 
     37      1.23  christos # This script does not want path expansion.
     38      1.23  christos set -f
     39      1.23  christos 
     40       1.1       jtc # Specify default values for environment variables if they are unset.
     41       1.1       jtc : ${AWK=awk}
     42      1.23  christos : ${TZDIR=$PWD}
     43       1.1       jtc 
     44      1.22  christos # Output one argument as-is to standard output, with trailing newline.
     45      1.14  christos # Safer than 'echo', which can mishandle '\' or leading '-'.
     46      1.14  christos say() {
     47  1.23.2.1  perseant   printf '%s\n' "$1"
     48       1.1       jtc }
     49       1.1       jtc 
     50       1.9  christos coord=
     51       1.9  christos location_limit=10
     52      1.11  christos zonetabtype=zone1970
     53       1.9  christos 
     54       1.9  christos usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
     55      1.18  christos Select a timezone interactively.
     56       1.6   mlelstv 
     57       1.9  christos Options:
     58       1.9  christos 
     59       1.9  christos   -c COORD
     60       1.9  christos     Instead of asking for continent and then country and then city,
     61       1.9  christos     ask for selection from time zones whose largest cities
     62       1.9  christos     are closest to the location with geographical coordinates COORD.
     63       1.9  christos     COORD should use ISO 6709 notation, for example, '-c +4852+00220'
     64       1.9  christos     for Paris (in degrees and minutes, North and East), or
     65       1.9  christos     '-c -35-058' for Buenos Aires (in degrees, South and West).
     66       1.9  christos 
     67       1.9  christos   -n LIMIT
     68       1.9  christos     Display at most LIMIT locations when -c is used (default $location_limit).
     69       1.9  christos 
     70       1.9  christos   --version
     71       1.9  christos     Output version information.
     72       1.9  christos 
     73       1.9  christos   --help
     74       1.9  christos     Output this help.
     75       1.9  christos 
     76       1.9  christos Report bugs to $REPORT_BUGS_TO."
     77       1.9  christos 
     78      1.10  christos # Ask the user to select from the function's arguments,
     79      1.10  christos # and assign the selected argument to the variable 'select_result'.
     80      1.22  christos # Exit on EOF or I/O error.  Use the shell's nicer 'select' builtin if
     81      1.22  christos # available, falling back on a portable substitute otherwise.
     82      1.10  christos if
     83      1.10  christos   case $BASH_VERSION in
     84  1.23.2.1  perseant   ?*) :;;
     85      1.10  christos   '')
     86      1.10  christos     # '; exit' should be redundant, but Dash doesn't properly fail without it.
     87  1.23.2.1  perseant     (eval 'set --; select x; do break; done; exit') <>/dev/null 2>&0
     88      1.10  christos   esac
     89      1.10  christos then
     90      1.10  christos   # Do this inside 'eval', as otherwise the shell might exit when parsing it
     91      1.10  christos   # even though it is never executed.
     92      1.10  christos   eval '
     93      1.10  christos     doselect() {
     94      1.10  christos       select select_result
     95      1.10  christos       do
     96      1.10  christos 	case $select_result in
     97  1.23.2.1  perseant 	"") echo >&2 "Please enter a number in range.";;
     98      1.10  christos 	?*) break
     99      1.10  christos 	esac
    100      1.10  christos       done || exit
    101      1.10  christos     }
    102      1.10  christos   '
    103      1.10  christos else
    104      1.10  christos   doselect() {
    105      1.10  christos     # Field width of the prompt numbers.
    106  1.23.2.1  perseant     select_width=${##}
    107      1.10  christos 
    108      1.10  christos     select_i=
    109      1.10  christos 
    110      1.10  christos     while :
    111      1.10  christos     do
    112      1.10  christos       case $select_i in
    113      1.10  christos       '')
    114      1.10  christos 	select_i=0
    115      1.10  christos 	for select_word
    116      1.10  christos 	do
    117  1.23.2.1  perseant 	  select_i=$(($select_i + 1))
    118      1.10  christos 	  printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word"
    119  1.23.2.1  perseant 	done;;
    120      1.10  christos       *[!0-9]*)
    121  1.23.2.1  perseant 	echo >&2 'Please enter a number in range.';;
    122      1.10  christos       *)
    123      1.10  christos 	if test 1 -le $select_i && test $select_i -le $#; then
    124  1.23.2.1  perseant 	  shift $(($select_i - 1))
    125      1.10  christos 	  select_result=$1
    126      1.10  christos 	  break
    127      1.10  christos 	fi
    128      1.10  christos 	echo >&2 'Please enter a number in range.'
    129      1.10  christos       esac
    130      1.10  christos 
    131      1.10  christos       # Prompt and read input.
    132      1.10  christos       printf >&2 %s "${PS3-#? }"
    133      1.10  christos       read select_i || exit
    134      1.10  christos     done
    135      1.10  christos   }
    136      1.10  christos fi
    137      1.10  christos 
    138      1.12  christos while getopts c:n:t:-: opt
    139       1.9  christos do
    140  1.23.2.1  perseant   case $opt$OPTARG in
    141  1.23.2.1  perseant   c*)
    142  1.23.2.1  perseant     coord=$OPTARG;;
    143  1.23.2.1  perseant   n*)
    144  1.23.2.1  perseant     location_limit=$OPTARG;;
    145  1.23.2.1  perseant   t*) # Undocumented option, used for developer testing.
    146  1.23.2.1  perseant     zonetabtype=$OPTARG;;
    147  1.23.2.1  perseant   -help)
    148  1.23.2.1  perseant     exec echo "$usage";;
    149  1.23.2.1  perseant   -version)
    150  1.23.2.1  perseant     exec echo "tzselect $PKGVERSION$TZVERSION";;
    151  1.23.2.1  perseant   -*)
    152  1.23.2.1  perseant     say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1;;
    153  1.23.2.1  perseant   *)
    154  1.23.2.1  perseant     say >&2 "$0: try '$0 --help'"; exit 1
    155  1.23.2.1  perseant   esac
    156       1.9  christos done
    157       1.9  christos 
    158  1.23.2.1  perseant shift $(($OPTIND - 1))
    159       1.9  christos case $# in
    160       1.9  christos 0) ;;
    161  1.23.2.1  perseant *) say >&2 "$0: $1: unknown argument"; exit 1
    162       1.9  christos esac
    163       1.6   mlelstv 
    164  1.23.2.1  perseant # translit=true to try transliteration.
    165  1.23.2.1  perseant # This is false if U+12345 CUNEIFORM SIGN URU TIMES KI has length 1
    166  1.23.2.1  perseant # which means the shell and (presumably) awk do not need transliteration.
    167  1.23.2.1  perseant # It is true if the byte string has some other length in characters, or
    168  1.23.2.1  perseant # if this is a POSIX.1-2017 or earlier shell that does not support $'...'.
    169  1.23.2.1  perseant CUNEIFORM_SIGN_URU_TIMES_KI=$'\360\222\215\205'
    170  1.23.2.1  perseant if test ${#CUNEIFORM_SIGN_URU_TIMES_KI} = 1
    171  1.23.2.1  perseant then translit=false
    172  1.23.2.1  perseant else translit=true
    173  1.23.2.1  perseant fi
    174       1.1       jtc 
    175  1.23.2.1  perseant # Read into shell variable $1 the contents of file $2.
    176  1.23.2.1  perseant # Convert to the current locale's encoding if possible,
    177  1.23.2.1  perseant # as the shell aligns columns better that way.
    178  1.23.2.1  perseant # If GNU iconv's //TRANSLIT does not work, fall back on POSIXish iconv;
    179  1.23.2.1  perseant # if that does not work, fall back on 'cat'.
    180  1.23.2.1  perseant read_file() {
    181  1.23.2.1  perseant   { $translit && {
    182  1.23.2.1  perseant     eval "$1=\$( (iconv -f UTF-8 -t //TRANSLIT) 2>/dev/null <\"\$2\")" ||
    183  1.23.2.1  perseant     eval "$1=\$( (iconv -f UTF-8) 2>/dev/null <\"\$2\")"
    184  1.23.2.1  perseant   }; } ||
    185  1.23.2.1  perseant   eval "$1=\$(cat <\"\$2\")" || {
    186  1.23.2.1  perseant     say >&2 "$0: time zone files are not set up correctly"
    187  1.23.2.1  perseant     exit 1
    188  1.23.2.1  perseant   }
    189      1.19  christos }
    190  1.23.2.1  perseant read_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab"
    191  1.23.2.1  perseant read_file TZ_ZONETABTYPE_TABLE "$TZDIR/$zonetabtype.tab"
    192  1.23.2.1  perseant TZ_ZONENOW_TABLE=
    193      1.14  christos 
    194       1.1       jtc newline='
    195       1.1       jtc '
    196       1.1       jtc IFS=$newline
    197       1.1       jtc 
    198      1.22  christos # Awk script to output a country list.
    199      1.22  christos output_country_list='
    200  1.23.2.1  perseant   BEGIN {
    201  1.23.2.1  perseant     continent_re = substr(ARGV[1], 2)
    202  1.23.2.1  perseant     TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
    203  1.23.2.1  perseant     TZ_ZONE_TABLE = substr(ARGV[3], 2)
    204  1.23.2.1  perseant     ARGV[1] = ARGV[2] = ARGV[3] = ""
    205  1.23.2.1  perseant     FS = "\t"
    206  1.23.2.1  perseant     nlines = split(TZ_ZONE_TABLE, line, /\n/)
    207  1.23.2.1  perseant     for (iline = 1; iline <= nlines; iline++) {
    208  1.23.2.1  perseant       $0 = line[iline]
    209  1.23.2.1  perseant       commentary = $0 ~ /^#@/
    210  1.23.2.1  perseant       if (commentary) {
    211  1.23.2.1  perseant 	if ($0 !~ /^#@/)
    212  1.23.2.1  perseant 	  continue
    213  1.23.2.1  perseant 	col1ccs = substr($1, 3)
    214  1.23.2.1  perseant 	conts = $2
    215  1.23.2.1  perseant       } else {
    216  1.23.2.1  perseant 	col1ccs = $1
    217  1.23.2.1  perseant 	conts = $3
    218      1.22  christos       }
    219  1.23.2.1  perseant       ncc = split(col1ccs, cc, /,/)
    220  1.23.2.1  perseant       ncont = split(conts, cont, /,/)
    221  1.23.2.1  perseant       for (i = 1; i <= ncc; i++) {
    222  1.23.2.1  perseant 	elsewhere = commentary
    223  1.23.2.1  perseant 	for (ci = 1; ci <= ncont; ci++) {
    224  1.23.2.1  perseant 	  if (cont[ci] ~ continent_re) {
    225  1.23.2.1  perseant 	    if (!cc_seen[cc[i]]++)
    226  1.23.2.1  perseant 	      cc_list[++ccs] = cc[i]
    227  1.23.2.1  perseant 	    elsewhere = 0
    228  1.23.2.1  perseant 	  }
    229      1.22  christos 	}
    230  1.23.2.1  perseant 	if (elsewhere)
    231  1.23.2.1  perseant 	  for (i = 1; i <= ncc; i++)
    232  1.23.2.1  perseant 	    cc_elsewhere[cc[i]] = 1
    233      1.22  christos       }
    234      1.22  christos     }
    235  1.23.2.1  perseant     nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
    236  1.23.2.1  perseant     for (i = 1; i <= nlines; i++) {
    237  1.23.2.1  perseant       $0 = line[i]
    238  1.23.2.1  perseant       if ($0 !~ /^#/)
    239  1.23.2.1  perseant 	cc_name[$1] = $2
    240  1.23.2.1  perseant     }
    241  1.23.2.1  perseant     for (i = 1; i <= ccs; i++) {
    242  1.23.2.1  perseant       country = cc_list[i]
    243  1.23.2.1  perseant       if (cc_elsewhere[country])
    244  1.23.2.1  perseant 	continue
    245  1.23.2.1  perseant       if (cc_name[country])
    246  1.23.2.1  perseant 	country = cc_name[country]
    247  1.23.2.1  perseant       print country
    248  1.23.2.1  perseant     }
    249      1.22  christos   }
    250      1.22  christos '
    251       1.1       jtc 
    252  1.23.2.1  perseant # Awk script to process a time zone table and output the same table,
    253      1.22  christos # with each row preceded by its distance from 'here'.
    254      1.22  christos # If output_times is set, each row is instead preceded by its local time
    255      1.22  christos # and any apostrophes are escaped for the shell.
    256      1.22  christos output_distances_or_times='
    257       1.9  christos   BEGIN {
    258  1.23.2.1  perseant     coord = substr(ARGV[1], 2)
    259  1.23.2.1  perseant     TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
    260  1.23.2.1  perseant     TZ_ZONE_TABLE = substr(ARGV[3], 2)
    261  1.23.2.1  perseant     ARGV[1] = ARGV[2] = ARGV[3] = ""
    262       1.9  christos     FS = "\t"
    263      1.22  christos     if (!output_times) {
    264  1.23.2.1  perseant       nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
    265  1.23.2.1  perseant       for (i = 1; i <= nlines; i++) {
    266  1.23.2.1  perseant 	$0 = line[i]
    267  1.23.2.1  perseant 	if ($0 ~ /^#/)
    268  1.23.2.1  perseant 	  continue
    269  1.23.2.1  perseant 	country[$1] = $2
    270  1.23.2.1  perseant       }
    271      1.22  christos       country["US"] = "US" # Otherwise the strings get too long.
    272      1.22  christos     }
    273       1.9  christos   }
    274      1.12  christos   function abs(x) {
    275      1.12  christos     return x < 0 ? -x : x;
    276      1.12  christos   }
    277      1.12  christos   function min(x, y) {
    278      1.12  christos     return x < y ? x : y;
    279      1.12  christos   }
    280      1.12  christos   function convert_coord(coord, deg, minute, ilen, sign, sec) {
    281       1.9  christos     if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9][0-9][0-9]([^0-9]|$)/) {
    282       1.9  christos       degminsec = coord
    283       1.9  christos       intdeg = degminsec < 0 ? -int(-degminsec / 10000) : int(degminsec / 10000)
    284       1.9  christos       minsec = degminsec - intdeg * 10000
    285       1.9  christos       intmin = minsec < 0 ? -int(-minsec / 100) : int(minsec / 100)
    286       1.9  christos       sec = minsec - intmin * 100
    287       1.9  christos       deg = (intdeg * 3600 + intmin * 60 + sec) / 3600
    288       1.9  christos     } else if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9]([^0-9]|$)/) {
    289       1.9  christos       degmin = coord
    290       1.9  christos       intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100)
    291      1.12  christos       minute = degmin - intdeg * 100
    292      1.12  christos       deg = (intdeg * 60 + minute) / 60
    293       1.9  christos     } else
    294       1.9  christos       deg = coord
    295       1.9  christos     return deg * 0.017453292519943296
    296       1.9  christos   }
    297       1.9  christos   function convert_latitude(coord) {
    298       1.9  christos     match(coord, /..*[-+]/)
    299       1.9  christos     return convert_coord(substr(coord, 1, RLENGTH - 1))
    300       1.9  christos   }
    301       1.9  christos   function convert_longitude(coord) {
    302       1.9  christos     match(coord, /..*[-+]/)
    303       1.9  christos     return convert_coord(substr(coord, RLENGTH))
    304       1.9  christos   }
    305       1.9  christos   # Great-circle distance between points with given latitude and longitude.
    306       1.9  christos   # Inputs and output are in radians.  This uses the great-circle special
    307       1.9  christos   # case of the Vicenty formula for distances on ellipsoids.
    308      1.12  christos   function gcdist(lat1, long1, lat2, long2, dlong, x, y, num, denom) {
    309       1.9  christos     dlong = long2 - long1
    310      1.13  christos     x = cos(lat2) * sin(dlong)
    311      1.13  christos     y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlong)
    312      1.13  christos     num = sqrt(x * x + y * y)
    313      1.13  christos     denom = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(dlong)
    314       1.9  christos     return atan2(num, denom)
    315       1.9  christos   }
    316      1.12  christos   # Parallel distance between points with given latitude and longitude.
    317      1.12  christos   # This is the product of the longitude difference and the cosine
    318      1.12  christos   # of the latitude of the point that is further from the equator.
    319      1.12  christos   # I.e., it considers longitudes to be further apart if they are
    320      1.12  christos   # nearer the equator.
    321      1.12  christos   function pardist(lat1, long1, lat2, long2) {
    322      1.13  christos     return abs(long1 - long2) * min(cos(lat1), cos(lat2))
    323      1.12  christos   }
    324      1.12  christos   # The distance function is the sum of the great-circle distance and
    325      1.12  christos   # the parallel distance.  It could be weighted.
    326      1.12  christos   function dist(lat1, long1, lat2, long2) {
    327      1.13  christos     return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2)
    328      1.12  christos   }
    329       1.9  christos   BEGIN {
    330       1.9  christos     coord_lat = convert_latitude(coord)
    331       1.9  christos     coord_long = convert_longitude(coord)
    332  1.23.2.1  perseant     nlines = split(TZ_ZONE_TABLE, line, /\n/)
    333  1.23.2.1  perseant     for (h = 1; h <= nlines; h++) {
    334  1.23.2.1  perseant       $0 = line[h]
    335  1.23.2.1  perseant       if ($0 ~ /^#/)
    336  1.23.2.1  perseant 	continue
    337  1.23.2.1  perseant       inline[inlines++] = $0
    338  1.23.2.1  perseant       ncc = split($1, cc, /,/)
    339  1.23.2.1  perseant       for (i = 1; i <= ncc; i++)
    340  1.23.2.1  perseant 	cc_used[cc[i]]++
    341      1.22  christos     }
    342  1.23.2.1  perseant     for (h = 0; h < inlines; h++) {
    343  1.23.2.1  perseant       $0 = inline[h]
    344  1.23.2.1  perseant       outline = $1 "\t" $2 "\t" $3
    345  1.23.2.1  perseant       sep = "\t"
    346  1.23.2.1  perseant       ncc = split($1, cc, /,/)
    347  1.23.2.1  perseant       split("", item_seen)
    348  1.23.2.1  perseant       item_seen[""] = 1
    349  1.23.2.1  perseant       for (i = 1; i <= ncc; i++) {
    350  1.23.2.1  perseant 	item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4
    351  1.23.2.1  perseant 	if (item_seen[item]++)
    352  1.23.2.1  perseant 	  continue
    353  1.23.2.1  perseant 	outline = outline sep item
    354  1.23.2.1  perseant 	sep = "; "
    355  1.23.2.1  perseant       }
    356  1.23.2.1  perseant       if (output_times) {
    357  1.23.2.1  perseant 	fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n"
    358  1.23.2.1  perseant 	gsub(/'\''/, "&\\\\&&", outline)
    359  1.23.2.1  perseant 	printf fmt, $3, h, outline
    360  1.23.2.1  perseant       } else {
    361  1.23.2.1  perseant 	here_lat = convert_latitude($2)
    362  1.23.2.1  perseant 	here_long = convert_longitude($2)
    363  1.23.2.1  perseant 	printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), \
    364  1.23.2.1  perseant 	  outline
    365  1.23.2.1  perseant       }
    366      1.11  christos     }
    367       1.9  christos   }
    368       1.9  christos '
    369       1.1       jtc 
    370       1.1       jtc # Begin the main loop.  We come back here if the user wants to retry.
    371       1.1       jtc while
    372       1.1       jtc 
    373  1.23.2.1  perseant   echo >&2 'Please identify a location' \
    374  1.23.2.1  perseant     'so that time zone rules can be set correctly.'
    375       1.1       jtc 
    376  1.23.2.1  perseant   continent=
    377  1.23.2.1  perseant   country=
    378  1.23.2.1  perseant   country_result=
    379  1.23.2.1  perseant   region=
    380  1.23.2.1  perseant   time=
    381  1.23.2.1  perseant   TZ_ZONE_TABLE=$TZ_ZONETABTYPE_TABLE
    382  1.23.2.1  perseant 
    383  1.23.2.1  perseant   case $coord in
    384  1.23.2.1  perseant   ?*)
    385  1.23.2.1  perseant     continent=coord;;
    386  1.23.2.1  perseant   '')
    387       1.1       jtc 
    388  1.23.2.1  perseant     # Ask the user for continent or ocean.
    389       1.1       jtc 
    390  1.23.2.1  perseant     echo >&2 \
    391  1.23.2.1  perseant       'Please select a continent, ocean, "coord", "TZ", "time", or "now".'
    392       1.1       jtc 
    393  1.23.2.1  perseant     quoted_continents=$(
    394  1.23.2.1  perseant       $AWK '
    395  1.23.2.1  perseant 	function handle_entry(entry) {
    396  1.23.2.1  perseant 	  entry = substr(entry, 1, index(entry, "/") - 1)
    397  1.23.2.1  perseant 	  if (entry == "America")
    398  1.23.2.1  perseant 	    entry = entry "s"
    399  1.23.2.1  perseant 	  if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/)
    400  1.23.2.1  perseant 	    entry = entry " Ocean"
    401  1.23.2.1  perseant 	  printf "'\''%s'\''\n", entry
    402  1.23.2.1  perseant 	}
    403  1.23.2.1  perseant 	BEGIN {
    404  1.23.2.1  perseant 	  TZ_ZONETABTYPE_TABLE = substr(ARGV[1], 2)
    405  1.23.2.1  perseant 	  ARGV[1] = ""
    406  1.23.2.1  perseant 	  FS = "\t"
    407  1.23.2.1  perseant 	  nlines = split(TZ_ZONETABTYPE_TABLE, line, /\n/)
    408  1.23.2.1  perseant 	  for (i = 1; i <= nlines; i++) {
    409  1.23.2.1  perseant 	    $0 = line[i]
    410  1.23.2.1  perseant 	    if ($0 ~ /^[^#]/)
    411  1.23.2.1  perseant 	      handle_entry($3)
    412  1.23.2.1  perseant 	    else if ($0 ~ /^#@/) {
    413  1.23.2.1  perseant 	      ncont = split($2, cont, /,/)
    414  1.23.2.1  perseant 	      for (ci = 1; ci <= ncont; ci++)
    415  1.23.2.1  perseant 		handle_entry(cont[ci])
    416  1.23.2.1  perseant 	    }
    417  1.23.2.1  perseant 	  }
    418  1.23.2.1  perseant 	}
    419  1.23.2.1  perseant       ' ="$TZ_ZONETABTYPE_TABLE" |
    420  1.23.2.1  perseant       sort -u |
    421  1.23.2.1  perseant       tr '\n' ' '
    422  1.23.2.1  perseant       echo ''
    423  1.23.2.1  perseant     )
    424  1.23.2.1  perseant 
    425  1.23.2.1  perseant     eval '
    426  1.23.2.1  perseant       doselect '"$quoted_continents"' \
    427  1.23.2.1  perseant 	"coord - I want to use geographical coordinates." \
    428  1.23.2.1  perseant 	"TZ - I want to specify the timezone using a proleptic TZ string." \
    429  1.23.2.1  perseant 	"time - I know local time already." \
    430  1.23.2.1  perseant 	"now - Like \"time\", but configure only for timestamps from now on."
    431  1.23.2.1  perseant       continent=$select_result
    432  1.23.2.1  perseant       case $continent in
    433  1.23.2.1  perseant       Americas) continent=America;;
    434  1.23.2.1  perseant       *)
    435  1.23.2.1  perseant 	# Get the first word of $continent.  Path expansion is disabled
    436  1.23.2.1  perseant 	# so this works even with "*", which should not happen.
    437  1.23.2.1  perseant 	IFS=" "
    438  1.23.2.1  perseant 	for continent in $continent ""; do break; done
    439  1.23.2.1  perseant 	IFS=$newline;;
    440  1.23.2.1  perseant       esac
    441  1.23.2.1  perseant       case $zonetabtype,$continent in
    442  1.23.2.1  perseant       zonenow,*) ;;
    443  1.23.2.1  perseant       *,now)
    444  1.23.2.1  perseant 	${TZ_ZONENOW_TABLE:+:} read_file TZ_ZONENOW_TABLE "$TZDIR/zonenow.tab"
    445  1.23.2.1  perseant 	TZ_ZONE_TABLE=$TZ_ZONENOW_TABLE
    446  1.23.2.1  perseant       esac
    447  1.23.2.1  perseant     '
    448  1.23.2.1  perseant   esac
    449       1.1       jtc 
    450  1.23.2.1  perseant   case $continent in
    451  1.23.2.1  perseant   TZ)
    452  1.23.2.1  perseant     # Ask the user for a proleptic TZ string.  Check that it conforms.
    453  1.23.2.1  perseant     check_POSIX_TZ_string='
    454  1.23.2.1  perseant       BEGIN {
    455  1.23.2.1  perseant 	tz = substr(ARGV[1], 2)
    456  1.23.2.1  perseant 	ARGV[1] = ""
    457  1.23.2.1  perseant 	tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \
    458  1.23.2.1  perseant 		  "|[[:alpha:]][[:alpha:]][[:alpha:]]+)")
    459  1.23.2.1  perseant 	sign = "[-+]?"
    460  1.23.2.1  perseant 	hhmm = "(:[0-5][0-9](:[0-5][0-9])?)?"
    461  1.23.2.1  perseant 	offset = sign "(2[0-4]|[0-1]?[0-9])" hhmm
    462  1.23.2.1  perseant 	time = sign "(16[0-7]|(1[0-5]|[0-9]?)[0-9])" hhmm
    463  1.23.2.1  perseant 	mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]"
    464  1.23.2.1  perseant 	jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \
    465  1.23.2.1  perseant 		 "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])")
    466  1.23.2.1  perseant 	datetime = ",(" mdate "|" jdate ")(/" time ")?"
    467  1.23.2.1  perseant 	tzpattern = ("^(:.*|" tzname offset "(" tzname \
    468  1.23.2.1  perseant 		     "(" offset ")?(" datetime datetime ")?)?)$")
    469  1.23.2.1  perseant 	exit tz ~ tzpattern
    470  1.23.2.1  perseant       }
    471  1.23.2.1  perseant     '
    472  1.23.2.1  perseant 
    473  1.23.2.1  perseant     while
    474  1.23.2.1  perseant       echo >&2 'Please enter the desired value' \
    475  1.23.2.1  perseant 	'of the TZ environment variable.'
    476  1.23.2.1  perseant       echo >&2 'For example, AEST-10 is abbreviated' \
    477  1.23.2.1  perseant 	'AEST and is 10 hours'
    478  1.23.2.1  perseant       echo >&2 'ahead (east) of Greenwich,' \
    479  1.23.2.1  perseant 	'with no daylight saving time.'
    480  1.23.2.1  perseant       read tz
    481  1.23.2.1  perseant       $AWK "$check_POSIX_TZ_string" ="$tz"
    482  1.23.2.1  perseant     do
    483  1.23.2.1  perseant       say >&2 "'$tz' is not a conforming POSIX proleptic TZ string."
    484  1.23.2.1  perseant     done
    485  1.23.2.1  perseant     TZ_for_date=$tz;;
    486  1.23.2.1  perseant   *)
    487  1.23.2.1  perseant     case $continent in
    488  1.23.2.1  perseant     coord)
    489  1.23.2.1  perseant       case $coord in
    490  1.23.2.1  perseant       '')
    491  1.23.2.1  perseant 	echo >&2 'Please enter coordinates' \
    492  1.23.2.1  perseant 	  'in ISO 6709 notation.'
    493  1.23.2.1  perseant 	echo >&2 'For example, +4042-07403 stands for'
    494  1.23.2.1  perseant 	echo >&2 '40 degrees 42 minutes north,' \
    495  1.23.2.1  perseant 	  '74 degrees 3 minutes west.'
    496  1.23.2.1  perseant 	read coord
    497  1.23.2.1  perseant       esac
    498  1.23.2.1  perseant       distance_table=$(
    499  1.23.2.1  perseant 	$AWK \
    500  1.23.2.1  perseant 	  "$output_distances_or_times" \
    501  1.23.2.1  perseant 	  ="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" |
    502  1.23.2.1  perseant 	sort -n |
    503  1.23.2.1  perseant 	$AWK "{print} NR == $location_limit { exit }"
    504  1.23.2.1  perseant       )
    505  1.23.2.1  perseant       regions=$(
    506  1.23.2.1  perseant 	$AWK '
    507  1.23.2.1  perseant 	  BEGIN {
    508  1.23.2.1  perseant 	    distance_table = substr(ARGV[1], 2)
    509  1.23.2.1  perseant 	    ARGV[1] = ""
    510  1.23.2.1  perseant 	    nlines = split(distance_table, line, /\n/)
    511  1.23.2.1  perseant 	    for (nr = 1; nr <= nlines; nr++) {
    512  1.23.2.1  perseant 	      nf = split(line[nr], f, /\t/)
    513  1.23.2.1  perseant 	      print f[nf]
    514  1.23.2.1  perseant 	    }
    515  1.23.2.1  perseant 	  }
    516  1.23.2.1  perseant 	' ="$distance_table"
    517  1.23.2.1  perseant       )
    518  1.23.2.1  perseant       echo >&2 'Please select one of the following timezones,'
    519  1.23.2.1  perseant       echo >&2 'listed roughly in increasing order' \
    520  1.23.2.1  perseant 	"of distance from $coord".
    521  1.23.2.1  perseant       doselect $regions
    522  1.23.2.1  perseant       region=$select_result
    523  1.23.2.1  perseant       tz=$(
    524  1.23.2.1  perseant 	$AWK '
    525  1.23.2.1  perseant 	  BEGIN {
    526  1.23.2.1  perseant 	    distance_table = substr(ARGV[1], 2)
    527  1.23.2.1  perseant 	    region = substr(ARGV[2], 2)
    528  1.23.2.1  perseant 	    ARGV[1] = ARGV[2] = ""
    529  1.23.2.1  perseant 	    nlines = split(distance_table, line, /\n/)
    530  1.23.2.1  perseant 	    for (nr = 1; nr <= nlines; nr++) {
    531  1.23.2.1  perseant 	      nf = split(line[nr], f, /\t/)
    532  1.23.2.1  perseant 	      if (f[nf] == region)
    533  1.23.2.1  perseant 		print f[4]
    534  1.23.2.1  perseant 	    }
    535  1.23.2.1  perseant 	  }
    536  1.23.2.1  perseant 	' ="$distance_table" ="$region"
    537  1.23.2.1  perseant       );;
    538  1.23.2.1  perseant     *)
    539  1.23.2.1  perseant       case $continent in
    540  1.23.2.1  perseant       now|time)
    541  1.23.2.1  perseant 	minute_format='%a %b %d %H:%M'
    542  1.23.2.1  perseant 	old_minute=$(TZ=UTC0 date +"$minute_format")
    543  1.23.2.1  perseant 	for i in 1 2 3
    544  1.23.2.1  perseant 	do
    545  1.23.2.1  perseant 	  time_table_command=$(
    546  1.23.2.1  perseant 	    $AWK \
    547  1.23.2.1  perseant 	      -v output_times=1 \
    548  1.23.2.1  perseant 	      "$output_distances_or_times" \
    549  1.23.2.1  perseant 	      = = ="$TZ_ZONE_TABLE"
    550  1.23.2.1  perseant 	  )
    551  1.23.2.1  perseant 	  time_table=$(eval "$time_table_command")
    552  1.23.2.1  perseant 	  new_minute=$(TZ=UTC0 date +"$minute_format")
    553  1.23.2.1  perseant 	  case $old_minute in
    554  1.23.2.1  perseant 	  "$new_minute") break
    555  1.23.2.1  perseant 	  esac
    556  1.23.2.1  perseant 	  old_minute=$new_minute
    557  1.23.2.1  perseant 	done
    558  1.23.2.1  perseant 	echo >&2 "The system says Universal Time is $new_minute."
    559  1.23.2.1  perseant 	echo >&2 "Assuming that's correct, what is the local time?"
    560  1.23.2.1  perseant 	sorted_table=$(say "$time_table" | sort -k2n -k2,5 -k1n) || {
    561  1.23.2.1  perseant 	  say >&2 "$0: cannot sort time table"
    562  1.23.2.1  perseant 	  exit 1
    563  1.23.2.1  perseant 	}
    564  1.23.2.1  perseant 	eval doselect $(
    565      1.10  christos 	  $AWK '
    566  1.23.2.1  perseant 	    BEGIN {
    567  1.23.2.1  perseant 	      sorted_table = substr(ARGV[1], 2)
    568  1.23.2.1  perseant 	      ARGV[1] = ""
    569  1.23.2.1  perseant 	      nlines = split(sorted_table, line, /\n/)
    570  1.23.2.1  perseant 	      for (i = 1; i <= nlines; i++) {
    571  1.23.2.1  perseant 		$0 = line[i]
    572  1.23.2.1  perseant 		outline = $6 " " $7 " " $4 " " $5
    573  1.23.2.1  perseant 		if (outline == oldline)
    574  1.23.2.1  perseant 		  continue
    575  1.23.2.1  perseant 		oldline = outline
    576  1.23.2.1  perseant 		gsub(/'\''/, "&\\\\&&", outline)
    577  1.23.2.1  perseant 		printf "'\''%s'\''\n", outline
    578  1.23.2.1  perseant 	      }
    579      1.20  christos 	    }
    580  1.23.2.1  perseant 	  ' ="$sorted_table"
    581  1.23.2.1  perseant 	)
    582  1.23.2.1  perseant 	time=$select_result
    583  1.23.2.1  perseant 	continent_re='^'
    584  1.23.2.1  perseant 	zone_table=$(
    585  1.23.2.1  perseant 	  $AWK '
    586  1.23.2.1  perseant 	    BEGIN {
    587  1.23.2.1  perseant 	      time = substr(ARGV[1], 2)
    588  1.23.2.1  perseant 	      time_table = substr(ARGV[2], 2)
    589  1.23.2.1  perseant 	      ARGV[1] = ARGV[2] = ""
    590  1.23.2.1  perseant 	      nlines = split(time_table, line, /\n/)
    591  1.23.2.1  perseant 	      for (i = 1; i <= nlines; i++) {
    592  1.23.2.1  perseant 		$0 = line[i]
    593  1.23.2.1  perseant 		if ($6 " " $7 " " $4 " " $5 == time) {
    594  1.23.2.1  perseant 		  sub(/[^\t]*\t/, "")
    595  1.23.2.1  perseant 		  print
    596  1.23.2.1  perseant 		}
    597      1.20  christos 	      }
    598      1.20  christos 	    }
    599  1.23.2.1  perseant 	  ' ="$time" ="$time_table"
    600  1.23.2.1  perseant 	)
    601  1.23.2.1  perseant 	countries=$(
    602  1.23.2.1  perseant 	  $AWK \
    603  1.23.2.1  perseant 	    "$output_country_list" \
    604  1.23.2.1  perseant 	    ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" |
    605  1.23.2.1  perseant 	  sort -f
    606  1.23.2.1  perseant 	)
    607  1.23.2.1  perseant 	;;
    608  1.23.2.1  perseant       *)
    609  1.23.2.1  perseant 	continent_re="^$continent/"
    610  1.23.2.1  perseant 	zone_table=$TZ_ZONE_TABLE
    611  1.23.2.1  perseant       esac
    612       1.9  christos 
    613  1.23.2.1  perseant       # Get list of names of countries in the continent or ocean.
    614  1.23.2.1  perseant       countries=$(
    615  1.23.2.1  perseant 	$AWK \
    616  1.23.2.1  perseant 	  "$output_country_list" \
    617  1.23.2.1  perseant 	  ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" |
    618  1.23.2.1  perseant 	sort -f
    619  1.23.2.1  perseant       )
    620  1.23.2.1  perseant       # If all zone table entries have comments, and there are
    621  1.23.2.1  perseant       # at most 22 entries, asked based on those comments.
    622  1.23.2.1  perseant       # This fits the prompt onto old-fashioned 24-line screens.
    623  1.23.2.1  perseant       regions=$(
    624  1.23.2.1  perseant 	$AWK '
    625  1.23.2.1  perseant 	  BEGIN {
    626  1.23.2.1  perseant 	    TZ_ZONE_TABLE = substr(ARGV[1], 2)
    627  1.23.2.1  perseant 	    ARGV[1] = ""
    628  1.23.2.1  perseant 	    FS = "\t"
    629  1.23.2.1  perseant 	    nlines = split(TZ_ZONE_TABLE, line, /\n/)
    630  1.23.2.1  perseant 	    for (i = 1; i <= nlines; i++) {
    631  1.23.2.1  perseant 	      $0 = line[i]
    632  1.23.2.1  perseant 	      if ($0 ~ /^[^#]/ && !missing_comment) {
    633  1.23.2.1  perseant 		if ($4)
    634  1.23.2.1  perseant 		  comment[++inlines] = $4
    635  1.23.2.1  perseant 		else
    636  1.23.2.1  perseant 		  missing_comment = 1
    637  1.23.2.1  perseant 	      }
    638  1.23.2.1  perseant 	    }
    639  1.23.2.1  perseant 	    if (!missing_comment && inlines <= 22)
    640  1.23.2.1  perseant 	      for (i = 1; i <= inlines; i++)
    641  1.23.2.1  perseant 		print comment[i]
    642  1.23.2.1  perseant 	  }
    643  1.23.2.1  perseant 	' ="$zone_table"
    644  1.23.2.1  perseant       )
    645       1.1       jtc 
    646  1.23.2.1  perseant       # If there's more than one country, ask the user which one.
    647  1.23.2.1  perseant       case $countries in
    648  1.23.2.1  perseant       *"$newline"*)
    649  1.23.2.1  perseant 	echo >&2 'Please select a country' \
    650  1.23.2.1  perseant 	  'whose clocks agree with yours.'
    651  1.23.2.1  perseant 	doselect $countries
    652  1.23.2.1  perseant 	country_result=$select_result
    653  1.23.2.1  perseant 	country=$select_result;;
    654  1.23.2.1  perseant       *)
    655  1.23.2.1  perseant 	country=$countries
    656  1.23.2.1  perseant       esac
    657       1.1       jtc 
    658       1.1       jtc 
    659  1.23.2.1  perseant       # Get list of timezones in the country.
    660  1.23.2.1  perseant       regions=$(
    661  1.23.2.1  perseant 	$AWK '
    662  1.23.2.1  perseant 	  BEGIN {
    663  1.23.2.1  perseant 	    country = substr(ARGV[1], 2)
    664  1.23.2.1  perseant 	    TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
    665  1.23.2.1  perseant 	    TZ_ZONE_TABLE = substr(ARGV[3], 2)
    666  1.23.2.1  perseant 	    ARGV[1] = ARGV[2] = ARGV[3] = ""
    667  1.23.2.1  perseant 	    FS = "\t"
    668  1.23.2.1  perseant 	    cc = country
    669  1.23.2.1  perseant 	    nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
    670  1.23.2.1  perseant 	    for (i = 1; i <= nlines; i++) {
    671  1.23.2.1  perseant 	      $0 = line[i]
    672  1.23.2.1  perseant 	      if ($0 !~ /^#/  &&  country == $2) {
    673  1.23.2.1  perseant 		cc = $1
    674  1.23.2.1  perseant 		break
    675  1.23.2.1  perseant 	      }
    676  1.23.2.1  perseant 	    }
    677  1.23.2.1  perseant 	    nlines = split(TZ_ZONE_TABLE, line, /\n/)
    678  1.23.2.1  perseant 	    for (i = 1; i <= nlines; i++) {
    679  1.23.2.1  perseant 	      $0 = line[i]
    680  1.23.2.1  perseant 	      if ($0 ~ /^#/)
    681  1.23.2.1  perseant 		continue
    682  1.23.2.1  perseant 	      if ($1 ~ cc)
    683  1.23.2.1  perseant 		print $4
    684  1.23.2.1  perseant 	    }
    685  1.23.2.1  perseant 	  }
    686  1.23.2.1  perseant 	' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table"
    687  1.23.2.1  perseant       )
    688       1.1       jtc 
    689  1.23.2.1  perseant       # If there's more than one region, ask the user which one.
    690  1.23.2.1  perseant       case $regions in
    691  1.23.2.1  perseant       *"$newline"*)
    692  1.23.2.1  perseant 	echo >&2 'Please select one of the following timezones.'
    693  1.23.2.1  perseant 	doselect $regions
    694  1.23.2.1  perseant 	region=$select_result
    695  1.23.2.1  perseant       esac
    696       1.1       jtc 
    697  1.23.2.1  perseant       # Determine tz from country and region.
    698  1.23.2.1  perseant       tz=$(
    699  1.23.2.1  perseant 	$AWK '
    700  1.23.2.1  perseant 	  BEGIN {
    701  1.23.2.1  perseant 	    country = substr(ARGV[1], 2)
    702  1.23.2.1  perseant 	    region = substr(ARGV[2], 2)
    703  1.23.2.1  perseant 	    TZ_COUNTRY_TABLE = substr(ARGV[3], 2)
    704  1.23.2.1  perseant 	    TZ_ZONE_TABLE = substr(ARGV[4], 2)
    705  1.23.2.1  perseant 	    ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = ""
    706  1.23.2.1  perseant 	    FS = "\t"
    707  1.23.2.1  perseant 	    cc = country
    708  1.23.2.1  perseant 	    nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
    709  1.23.2.1  perseant 	    for (i = 1; i <= nlines; i++) {
    710  1.23.2.1  perseant 	      $0 = line[i]
    711  1.23.2.1  perseant 	      if ($0 !~ /^#/  &&  country == $2) {
    712  1.23.2.1  perseant 		cc = $1
    713  1.23.2.1  perseant 		break
    714  1.23.2.1  perseant 	      }
    715  1.23.2.1  perseant 	    }
    716  1.23.2.1  perseant 	    nlines = split(TZ_ZONE_TABLE, line, /\n/)
    717  1.23.2.1  perseant 	    for (i = 1; i <= nlines; i++) {
    718  1.23.2.1  perseant 	      $0 = line[i]
    719  1.23.2.1  perseant 	      if ($0 ~ /^#/)
    720  1.23.2.1  perseant 		continue
    721  1.23.2.1  perseant 	      if ($1 ~ cc && ($4 == region || !region))
    722  1.23.2.1  perseant 		print $3
    723  1.23.2.1  perseant 	    }
    724  1.23.2.1  perseant 	  }
    725  1.23.2.1  perseant 	' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table"
    726  1.23.2.1  perseant       )
    727  1.23.2.1  perseant     esac
    728       1.1       jtc 
    729  1.23.2.1  perseant     # Make sure the corresponding zoneinfo file exists.
    730  1.23.2.1  perseant     TZ_for_date=$TZDIR/$tz
    731  1.23.2.1  perseant     <"$TZ_for_date" || {
    732  1.23.2.1  perseant       say >&2 "$0: time zone files are not set up correctly"
    733  1.23.2.1  perseant       exit 1
    734  1.23.2.1  perseant     }
    735  1.23.2.1  perseant   esac
    736  1.23.2.1  perseant 
    737  1.23.2.1  perseant 
    738  1.23.2.1  perseant   # Use the proposed TZ to output the current date relative to UTC.
    739  1.23.2.1  perseant   # Loop until they agree in seconds.
    740  1.23.2.1  perseant   # Give up after 8 unsuccessful tries.
    741  1.23.2.1  perseant 
    742  1.23.2.1  perseant   extra_info=
    743  1.23.2.1  perseant   for i in 1 2 3 4 5 6 7 8
    744  1.23.2.1  perseant   do
    745  1.23.2.1  perseant     TZdate=$(LANG=C TZ="$TZ_for_date" date)
    746  1.23.2.1  perseant     UTdate=$(LANG=C TZ=UTC0 date)
    747  1.23.2.1  perseant     TZsecsetc=${TZdate##*[0-5][0-9]:}
    748  1.23.2.1  perseant     UTsecsetc=${UTdate##*[0-5][0-9]:}
    749  1.23.2.1  perseant     if test "${TZsecsetc%%[!0-9]*}" = "${UTsecsetc%%[!0-9]*}"
    750  1.23.2.1  perseant     then
    751  1.23.2.1  perseant       extra_info="
    752  1.23.2.1  perseant Selected time is now:	$TZdate.
    753  1.23.2.1  perseant Universal Time is now:	$UTdate."
    754  1.23.2.1  perseant       break
    755  1.23.2.1  perseant     fi
    756  1.23.2.1  perseant   done
    757  1.23.2.1  perseant 
    758  1.23.2.1  perseant 
    759  1.23.2.1  perseant   # Output TZ info and ask the user to confirm.
    760  1.23.2.1  perseant 
    761  1.23.2.1  perseant   echo >&2 ""
    762  1.23.2.1  perseant   echo >&2 "Based on the following information:"
    763  1.23.2.1  perseant   echo >&2 ""
    764  1.23.2.1  perseant   case $time%$country_result%$region%$coord in
    765  1.23.2.1  perseant   ?*%?*%?*%)
    766  1.23.2.1  perseant     say >&2 "	$time$newline	$country_result$newline	$region";;
    767  1.23.2.1  perseant   ?*%?*%%|?*%%?*%) say >&2 "	$time$newline	$country_result$region";;
    768  1.23.2.1  perseant   ?*%%%)	say >&2 "	$time";;
    769  1.23.2.1  perseant   %?*%?*%)	say >&2 "	$country_result$newline	$region";;
    770  1.23.2.1  perseant   %?*%%)	say >&2 "	$country_result";;
    771  1.23.2.1  perseant   %%?*%?*)	say >&2 "	coord $coord$newline	$region";;
    772  1.23.2.1  perseant   %%%?*)	say >&2 "	coord $coord";;
    773  1.23.2.1  perseant   *)		say >&2 "	TZ='$tz'"
    774  1.23.2.1  perseant   esac
    775  1.23.2.1  perseant   say >&2 ""
    776  1.23.2.1  perseant   say >&2 "TZ='$tz' will be used.$extra_info"
    777  1.23.2.1  perseant   say >&2 "Is the above information OK?"
    778  1.23.2.1  perseant 
    779  1.23.2.1  perseant   doselect Yes No
    780  1.23.2.1  perseant   ok=$select_result
    781  1.23.2.1  perseant   case $ok in
    782  1.23.2.1  perseant   Yes) break
    783  1.23.2.1  perseant   esac
    784       1.9  christos do coord=
    785       1.1       jtc done
    786       1.1       jtc 
    787       1.5    kleink case $SHELL in
    788  1.23.2.1  perseant *csh) file=.login line="setenv TZ '$tz'";;
    789  1.23.2.1  perseant *)    file=.profile line="export TZ='$tz'"
    790       1.5    kleink esac
    791       1.5    kleink 
    792      1.15  christos test -t 1 && say >&2 "
    793       1.5    kleink You can make this change permanent for yourself by appending the line
    794       1.5    kleink 	$line
    795       1.5    kleink to the file '$file' in your home directory; then log out and log in again.
    796       1.5    kleink 
    797       1.5    kleink Here is that TZ value again, this time on standard output so that you
    798       1.5    kleink can use the $0 command in shell scripts:"
    799       1.5    kleink 
    800  1.23.2.1  perseant say "$tz"
    801