tzselect.ksh revision 1.16 1 1.8 christos #! /bin/bash
2 1.2 perry #
3 1.16 christos # $NetBSD: tzselect.ksh,v 1.16 2017/10/24 17:38:17 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.15 christos # Contributed by Paul Eggert. This file is in the public domain.
13 1.1 jtc
14 1.1 jtc # Porting notes:
15 1.1 jtc #
16 1.10 christos # This script requires a Posix-like shell and prefers 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.16 christos # Bash <https://www.gnu.org/software/bash/>
23 1.8 christos # Korn Shell <http://www.kornshell.com/>
24 1.16 christos # MirBSD Korn Shell <https://www.mirbsd.org/mksh.htm>
25 1.1 jtc #
26 1.10 christos # For portability to Solaris 9 /bin/sh this script avoids some POSIX
27 1.10 christos # features and common extensions, such as $(...) (which works sometimes
28 1.10 christos # but not others), $((...)), and $10.
29 1.10 christos #
30 1.1 jtc # This script also uses several features of modern awk programs.
31 1.8 christos # If your host lacks awk, or has an old awk that does not conform to Posix,
32 1.1 jtc # you can use either of the following free programs instead:
33 1.1 jtc #
34 1.16 christos # Gawk (GNU awk) <https://www.gnu.org/software/gawk/>
35 1.16 christos # mawk <https://invisible-island.net/mawk/>
36 1.1 jtc
37 1.1 jtc
38 1.1 jtc # Specify default values for environment variables if they are unset.
39 1.1 jtc : ${AWK=awk}
40 1.10 christos : ${TZDIR=`pwd`}
41 1.1 jtc
42 1.14 christos # Output one argument as-is to standard output.
43 1.14 christos # Safer than 'echo', which can mishandle '\' or leading '-'.
44 1.14 christos say() {
45 1.14 christos printf '%s\n' "$1"
46 1.14 christos }
47 1.14 christos
48 1.1 jtc # Check for awk Posix compliance.
49 1.1 jtc ($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
50 1.1 jtc [ $? = 123 ] || {
51 1.14 christos say >&2 "$0: Sorry, your '$AWK' program is not Posix compatible."
52 1.1 jtc exit 1
53 1.1 jtc }
54 1.1 jtc
55 1.9 christos coord=
56 1.9 christos location_limit=10
57 1.11 christos zonetabtype=zone1970
58 1.9 christos
59 1.9 christos usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
60 1.6 mlelstv Select a time zone interactively.
61 1.6 mlelstv
62 1.9 christos Options:
63 1.9 christos
64 1.9 christos -c COORD
65 1.9 christos Instead of asking for continent and then country and then city,
66 1.9 christos ask for selection from time zones whose largest cities
67 1.9 christos are closest to the location with geographical coordinates COORD.
68 1.9 christos COORD should use ISO 6709 notation, for example, '-c +4852+00220'
69 1.9 christos for Paris (in degrees and minutes, North and East), or
70 1.9 christos '-c -35-058' for Buenos Aires (in degrees, South and West).
71 1.9 christos
72 1.9 christos -n LIMIT
73 1.9 christos Display at most LIMIT locations when -c is used (default $location_limit).
74 1.9 christos
75 1.9 christos --version
76 1.9 christos Output version information.
77 1.9 christos
78 1.9 christos --help
79 1.9 christos Output this help.
80 1.9 christos
81 1.9 christos Report bugs to $REPORT_BUGS_TO."
82 1.9 christos
83 1.10 christos # Ask the user to select from the function's arguments,
84 1.10 christos # and assign the selected argument to the variable 'select_result'.
85 1.10 christos # Exit on EOF or I/O error. Use the shell's 'select' builtin if available,
86 1.10 christos # falling back on a less-nice but portable substitute otherwise.
87 1.10 christos if
88 1.10 christos case $BASH_VERSION in
89 1.10 christos ?*) : ;;
90 1.10 christos '')
91 1.10 christos # '; exit' should be redundant, but Dash doesn't properly fail without it.
92 1.11 christos (eval 'set --; select x; do break; done; exit') </dev/null 2>/dev/null
93 1.10 christos esac
94 1.10 christos then
95 1.10 christos # Do this inside 'eval', as otherwise the shell might exit when parsing it
96 1.10 christos # even though it is never executed.
97 1.10 christos eval '
98 1.10 christos doselect() {
99 1.10 christos select select_result
100 1.10 christos do
101 1.10 christos case $select_result in
102 1.10 christos "") echo >&2 "Please enter a number in range." ;;
103 1.10 christos ?*) break
104 1.10 christos esac
105 1.10 christos done || exit
106 1.10 christos }
107 1.10 christos
108 1.10 christos # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
109 1.10 christos case $BASH_VERSION in
110 1.10 christos [01].*)
111 1.10 christos case `echo 1 | (select x in x; do break; done) 2>/dev/null` in
112 1.10 christos ?*) PS3=
113 1.10 christos esac
114 1.10 christos esac
115 1.10 christos '
116 1.10 christos else
117 1.10 christos doselect() {
118 1.10 christos # Field width of the prompt numbers.
119 1.10 christos select_width=`expr $# : '.*'`
120 1.10 christos
121 1.10 christos select_i=
122 1.10 christos
123 1.10 christos while :
124 1.10 christos do
125 1.10 christos case $select_i in
126 1.10 christos '')
127 1.10 christos select_i=0
128 1.10 christos for select_word
129 1.10 christos do
130 1.10 christos select_i=`expr $select_i + 1`
131 1.10 christos printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word"
132 1.10 christos done ;;
133 1.10 christos *[!0-9]*)
134 1.10 christos echo >&2 'Please enter a number in range.' ;;
135 1.10 christos *)
136 1.10 christos if test 1 -le $select_i && test $select_i -le $#; then
137 1.10 christos shift `expr $select_i - 1`
138 1.10 christos select_result=$1
139 1.10 christos break
140 1.10 christos fi
141 1.10 christos echo >&2 'Please enter a number in range.'
142 1.10 christos esac
143 1.10 christos
144 1.10 christos # Prompt and read input.
145 1.10 christos printf >&2 %s "${PS3-#? }"
146 1.10 christos read select_i || exit
147 1.10 christos done
148 1.10 christos }
149 1.10 christos fi
150 1.10 christos
151 1.12 christos while getopts c:n:t:-: opt
152 1.9 christos do
153 1.9 christos case $opt$OPTARG in
154 1.9 christos c*)
155 1.9 christos coord=$OPTARG ;;
156 1.9 christos n*)
157 1.9 christos location_limit=$OPTARG ;;
158 1.11 christos t*) # Undocumented option, used for developer testing.
159 1.11 christos zonetabtype=$OPTARG ;;
160 1.9 christos -help)
161 1.9 christos exec echo "$usage" ;;
162 1.9 christos -version)
163 1.9 christos exec echo "tzselect $PKGVERSION$TZVERSION" ;;
164 1.9 christos -*)
165 1.14 christos say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;;
166 1.9 christos *)
167 1.14 christos say >&2 "$0: try '$0 --help'"; exit 1 ;;
168 1.9 christos esac
169 1.9 christos done
170 1.9 christos
171 1.10 christos shift `expr $OPTIND - 1`
172 1.9 christos case $# in
173 1.9 christos 0) ;;
174 1.14 christos *) say >&2 "$0: $1: unknown argument"; exit 1 ;;
175 1.9 christos esac
176 1.6 mlelstv
177 1.1 jtc # Make sure the tables are readable.
178 1.1 jtc TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
179 1.11 christos TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab
180 1.1 jtc for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
181 1.1 jtc do
182 1.11 christos <"$f" || {
183 1.14 christos say >&2 "$0: time zone files are not set up correctly"
184 1.1 jtc exit 1
185 1.1 jtc }
186 1.1 jtc done
187 1.1 jtc
188 1.14 christos # If the current locale does not support UTF-8, convert data to current
189 1.14 christos # locale's format if possible, as the shell aligns columns better that way.
190 1.14 christos # Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI.
191 1.14 christos ! $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) != 1 }' &&
192 1.14 christos { tmp=`(mktemp -d) 2>/dev/null` || {
193 1.14 christos tmp=${TMPDIR-/tmp}/tzselect.$$ &&
194 1.14 christos (umask 77 && mkdir -- "$tmp")
195 1.14 christos };} &&
196 1.14 christos trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM &&
197 1.14 christos (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \
198 1.14 christos 2>/dev/null &&
199 1.14 christos TZ_COUNTRY_TABLE=$tmp/iso3166.tab &&
200 1.14 christos iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab &&
201 1.14 christos TZ_ZONE_TABLE=$tmp/$zonetabtype.tab
202 1.14 christos
203 1.1 jtc newline='
204 1.1 jtc '
205 1.1 jtc IFS=$newline
206 1.1 jtc
207 1.1 jtc
208 1.9 christos # Awk script to read a time zone table and output the same table,
209 1.9 christos # with each column preceded by its distance from 'here'.
210 1.9 christos output_distances='
211 1.9 christos BEGIN {
212 1.9 christos FS = "\t"
213 1.9 christos while (getline <TZ_COUNTRY_TABLE)
214 1.9 christos if ($0 ~ /^[^#]/)
215 1.9 christos country[$1] = $2
216 1.9 christos country["US"] = "US" # Otherwise the strings get too long.
217 1.9 christos }
218 1.12 christos function abs(x) {
219 1.12 christos return x < 0 ? -x : x;
220 1.12 christos }
221 1.12 christos function min(x, y) {
222 1.12 christos return x < y ? x : y;
223 1.12 christos }
224 1.12 christos function convert_coord(coord, deg, minute, ilen, sign, sec) {
225 1.9 christos if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9][0-9][0-9]([^0-9]|$)/) {
226 1.9 christos degminsec = coord
227 1.9 christos intdeg = degminsec < 0 ? -int(-degminsec / 10000) : int(degminsec / 10000)
228 1.9 christos minsec = degminsec - intdeg * 10000
229 1.9 christos intmin = minsec < 0 ? -int(-minsec / 100) : int(minsec / 100)
230 1.9 christos sec = minsec - intmin * 100
231 1.9 christos deg = (intdeg * 3600 + intmin * 60 + sec) / 3600
232 1.9 christos } else if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9]([^0-9]|$)/) {
233 1.9 christos degmin = coord
234 1.9 christos intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100)
235 1.12 christos minute = degmin - intdeg * 100
236 1.12 christos deg = (intdeg * 60 + minute) / 60
237 1.9 christos } else
238 1.9 christos deg = coord
239 1.9 christos return deg * 0.017453292519943296
240 1.9 christos }
241 1.9 christos function convert_latitude(coord) {
242 1.9 christos match(coord, /..*[-+]/)
243 1.9 christos return convert_coord(substr(coord, 1, RLENGTH - 1))
244 1.9 christos }
245 1.9 christos function convert_longitude(coord) {
246 1.9 christos match(coord, /..*[-+]/)
247 1.9 christos return convert_coord(substr(coord, RLENGTH))
248 1.9 christos }
249 1.9 christos # Great-circle distance between points with given latitude and longitude.
250 1.9 christos # Inputs and output are in radians. This uses the great-circle special
251 1.9 christos # case of the Vicenty formula for distances on ellipsoids.
252 1.12 christos function gcdist(lat1, long1, lat2, long2, dlong, x, y, num, denom) {
253 1.9 christos dlong = long2 - long1
254 1.13 christos x = cos(lat2) * sin(dlong)
255 1.13 christos y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlong)
256 1.13 christos num = sqrt(x * x + y * y)
257 1.13 christos denom = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(dlong)
258 1.9 christos return atan2(num, denom)
259 1.9 christos }
260 1.12 christos # Parallel distance between points with given latitude and longitude.
261 1.12 christos # This is the product of the longitude difference and the cosine
262 1.12 christos # of the latitude of the point that is further from the equator.
263 1.12 christos # I.e., it considers longitudes to be further apart if they are
264 1.12 christos # nearer the equator.
265 1.12 christos function pardist(lat1, long1, lat2, long2) {
266 1.13 christos return abs(long1 - long2) * min(cos(lat1), cos(lat2))
267 1.12 christos }
268 1.12 christos # The distance function is the sum of the great-circle distance and
269 1.12 christos # the parallel distance. It could be weighted.
270 1.12 christos function dist(lat1, long1, lat2, long2) {
271 1.13 christos return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2)
272 1.12 christos }
273 1.9 christos BEGIN {
274 1.9 christos coord_lat = convert_latitude(coord)
275 1.9 christos coord_long = convert_longitude(coord)
276 1.9 christos }
277 1.9 christos /^[^#]/ {
278 1.9 christos here_lat = convert_latitude($2)
279 1.9 christos here_long = convert_longitude($2)
280 1.11 christos line = $1 "\t" $2 "\t" $3
281 1.11 christos sep = "\t"
282 1.11 christos ncc = split($1, cc, /,/)
283 1.11 christos for (i = 1; i <= ncc; i++) {
284 1.11 christos line = line sep country[cc[i]]
285 1.11 christos sep = ", "
286 1.11 christos }
287 1.9 christos if (NF == 4)
288 1.9 christos line = line " - " $4
289 1.9 christos printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
290 1.9 christos }
291 1.9 christos '
292 1.1 jtc
293 1.1 jtc # Begin the main loop. We come back here if the user wants to retry.
294 1.1 jtc while
295 1.1 jtc
296 1.1 jtc echo >&2 'Please identify a location' \
297 1.1 jtc 'so that time zone rules can be set correctly.'
298 1.1 jtc
299 1.1 jtc continent=
300 1.1 jtc country=
301 1.1 jtc region=
302 1.1 jtc
303 1.9 christos case $coord in
304 1.9 christos ?*)
305 1.9 christos continent=coord;;
306 1.9 christos '')
307 1.1 jtc
308 1.1 jtc # Ask the user for continent or ocean.
309 1.1 jtc
310 1.9 christos echo >&2 'Please select a continent, ocean, "coord", or "TZ".'
311 1.1 jtc
312 1.10 christos quoted_continents=`
313 1.10 christos $AWK '
314 1.10 christos BEGIN { FS = "\t" }
315 1.9 christos /^[^#]/ {
316 1.9 christos entry = substr($3, 1, index($3, "/") - 1)
317 1.9 christos if (entry == "America")
318 1.9 christos entry = entry "s"
319 1.9 christos if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/)
320 1.9 christos entry = entry " Ocean"
321 1.9 christos printf "'\''%s'\''\n", entry
322 1.9 christos }
323 1.11 christos ' <"$TZ_ZONE_TABLE" |
324 1.9 christos sort -u |
325 1.9 christos tr '\n' ' '
326 1.9 christos echo ''
327 1.10 christos `
328 1.9 christos
329 1.9 christos eval '
330 1.10 christos doselect '"$quoted_continents"' \
331 1.9 christos "coord - I want to use geographical coordinates." \
332 1.9 christos "TZ - I want to specify the time zone using the Posix TZ format."
333 1.10 christos continent=$select_result
334 1.10 christos case $continent in
335 1.10 christos Americas) continent=America;;
336 1.10 christos *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''`
337 1.10 christos esac
338 1.9 christos '
339 1.9 christos esac
340 1.9 christos
341 1.1 jtc case $continent in
342 1.9 christos TZ)
343 1.1 jtc # Ask the user for a Posix TZ string. Check that it conforms.
344 1.1 jtc while
345 1.1 jtc echo >&2 'Please enter the desired value' \
346 1.1 jtc 'of the TZ environment variable.'
347 1.1 jtc echo >&2 'For example, GST-10 is a zone named GST' \
348 1.1 jtc 'that is 10 hours ahead (east) of UTC.'
349 1.1 jtc read TZ
350 1.1 jtc $AWK -v TZ="$TZ" 'BEGIN {
351 1.15 christos tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})"
352 1.15 christos time = "(2[0-4]|[0-1]?[0-9])" \
353 1.15 christos "(:[0-5][0-9](:[0-5][0-9])?)?"
354 1.1 jtc offset = "[-+]?" time
355 1.15 christos mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]"
356 1.15 christos jdate = "((J[1-9]|[0-9]|J?[1-9][0-9]" \
357 1.15 christos "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])"
358 1.15 christos datetime = ",(" mdate "|" jdate ")(/" time ")?"
359 1.1 jtc tzpattern = "^(:.*|" tzname offset "(" tzname \
360 1.1 jtc "(" offset ")?(" datetime datetime ")?)?)$"
361 1.1 jtc if (TZ ~ tzpattern) exit 1
362 1.1 jtc exit 0
363 1.1 jtc }'
364 1.1 jtc do
365 1.14 christos say >&2 "'$TZ' is not a conforming Posix time zone string."
366 1.1 jtc done
367 1.1 jtc TZ_for_date=$TZ;;
368 1.1 jtc *)
369 1.9 christos case $continent in
370 1.9 christos coord)
371 1.9 christos case $coord in
372 1.9 christos '')
373 1.9 christos echo >&2 'Please enter coordinates' \
374 1.9 christos 'in ISO 6709 notation.'
375 1.9 christos echo >&2 'For example, +4042-07403 stands for'
376 1.9 christos echo >&2 '40 degrees 42 minutes north,' \
377 1.9 christos '74 degrees 3 minutes west.'
378 1.9 christos read coord;;
379 1.9 christos esac
380 1.10 christos distance_table=`$AWK \
381 1.9 christos -v coord="$coord" \
382 1.9 christos -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
383 1.11 christos "$output_distances" <"$TZ_ZONE_TABLE" |
384 1.9 christos sort -n |
385 1.9 christos sed "${location_limit}q"
386 1.10 christos `
387 1.14 christos regions=`say "$distance_table" | $AWK '
388 1.9 christos BEGIN { FS = "\t" }
389 1.9 christos { print $NF }
390 1.10 christos '`
391 1.9 christos echo >&2 'Please select one of the following' \
392 1.9 christos 'time zone regions,'
393 1.9 christos echo >&2 'listed roughly in increasing order' \
394 1.9 christos "of distance from $coord".
395 1.10 christos doselect $regions
396 1.10 christos region=$select_result
397 1.14 christos TZ=`say "$distance_table" | $AWK -v region="$region" '
398 1.9 christos BEGIN { FS="\t" }
399 1.9 christos $NF == region { print $4 }
400 1.10 christos '`
401 1.9 christos ;;
402 1.9 christos *)
403 1.1 jtc # Get list of names of countries in the continent or ocean.
404 1.10 christos countries=`$AWK \
405 1.1 jtc -v continent="$continent" \
406 1.1 jtc -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
407 1.1 jtc '
408 1.10 christos BEGIN { FS = "\t" }
409 1.1 jtc /^#/ { next }
410 1.1 jtc $3 ~ ("^" continent "/") {
411 1.11 christos ncc = split($1, cc, /,/)
412 1.11 christos for (i = 1; i <= ncc; i++)
413 1.11 christos if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i]
414 1.1 jtc }
415 1.1 jtc END {
416 1.1 jtc while (getline <TZ_COUNTRY_TABLE) {
417 1.1 jtc if ($0 !~ /^#/) cc_name[$1] = $2
418 1.1 jtc }
419 1.1 jtc for (i = 1; i <= ccs; i++) {
420 1.1 jtc country = cc_list[i]
421 1.1 jtc if (cc_name[country]) {
422 1.1 jtc country = cc_name[country]
423 1.1 jtc }
424 1.1 jtc print country
425 1.1 jtc }
426 1.1 jtc }
427 1.11 christos ' <"$TZ_ZONE_TABLE" | sort -f`
428 1.1 jtc
429 1.1 jtc
430 1.1 jtc # If there's more than one country, ask the user which one.
431 1.1 jtc case $countries in
432 1.1 jtc *"$newline"*)
433 1.9 christos echo >&2 'Please select a country' \
434 1.9 christos 'whose clocks agree with yours.'
435 1.10 christos doselect $countries
436 1.10 christos country=$select_result;;
437 1.1 jtc *)
438 1.1 jtc country=$countries
439 1.1 jtc esac
440 1.1 jtc
441 1.1 jtc
442 1.1 jtc # Get list of names of time zone rule regions in the country.
443 1.10 christos regions=`$AWK \
444 1.1 jtc -v country="$country" \
445 1.1 jtc -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
446 1.1 jtc '
447 1.1 jtc BEGIN {
448 1.10 christos FS = "\t"
449 1.1 jtc cc = country
450 1.1 jtc while (getline <TZ_COUNTRY_TABLE) {
451 1.1 jtc if ($0 !~ /^#/ && country == $2) {
452 1.1 jtc cc = $1
453 1.1 jtc break
454 1.1 jtc }
455 1.1 jtc }
456 1.1 jtc }
457 1.14 christos /^#/ { next }
458 1.11 christos $1 ~ cc { print $4 }
459 1.11 christos ' <"$TZ_ZONE_TABLE"`
460 1.1 jtc
461 1.1 jtc
462 1.1 jtc # If there's more than one region, ask the user which one.
463 1.1 jtc case $regions in
464 1.1 jtc *"$newline"*)
465 1.1 jtc echo >&2 'Please select one of the following' \
466 1.1 jtc 'time zone regions.'
467 1.10 christos doselect $regions
468 1.10 christos region=$select_result;;
469 1.1 jtc *)
470 1.1 jtc region=$regions
471 1.1 jtc esac
472 1.1 jtc
473 1.1 jtc # Determine TZ from country and region.
474 1.10 christos TZ=`$AWK \
475 1.1 jtc -v country="$country" \
476 1.1 jtc -v region="$region" \
477 1.1 jtc -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
478 1.1 jtc '
479 1.1 jtc BEGIN {
480 1.10 christos FS = "\t"
481 1.1 jtc cc = country
482 1.1 jtc while (getline <TZ_COUNTRY_TABLE) {
483 1.1 jtc if ($0 !~ /^#/ && country == $2) {
484 1.1 jtc cc = $1
485 1.1 jtc break
486 1.1 jtc }
487 1.1 jtc }
488 1.1 jtc }
489 1.14 christos /^#/ { next }
490 1.11 christos $1 ~ cc && $4 == region { print $3 }
491 1.11 christos ' <"$TZ_ZONE_TABLE"`
492 1.9 christos esac
493 1.1 jtc
494 1.1 jtc # Make sure the corresponding zoneinfo file exists.
495 1.1 jtc TZ_for_date=$TZDIR/$TZ
496 1.11 christos <"$TZ_for_date" || {
497 1.14 christos say >&2 "$0: time zone files are not set up correctly"
498 1.1 jtc exit 1
499 1.1 jtc }
500 1.1 jtc esac
501 1.1 jtc
502 1.1 jtc
503 1.1 jtc # Use the proposed TZ to output the current date relative to UTC.
504 1.1 jtc # Loop until they agree in seconds.
505 1.1 jtc # Give up after 8 unsuccessful tries.
506 1.1 jtc
507 1.1 jtc extra_info=
508 1.1 jtc for i in 1 2 3 4 5 6 7 8
509 1.1 jtc do
510 1.10 christos TZdate=`LANG=C TZ="$TZ_for_date" date`
511 1.10 christos UTdate=`LANG=C TZ=UTC0 date`
512 1.10 christos TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'`
513 1.10 christos UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'`
514 1.1 jtc case $TZsec in
515 1.1 jtc $UTsec)
516 1.1 jtc extra_info="
517 1.15 christos Selected time is now: $TZdate.
518 1.1 jtc Universal Time is now: $UTdate."
519 1.1 jtc break
520 1.1 jtc esac
521 1.1 jtc done
522 1.1 jtc
523 1.1 jtc
524 1.1 jtc # Output TZ info and ask the user to confirm.
525 1.1 jtc
526 1.1 jtc echo >&2 ""
527 1.1 jtc echo >&2 "The following information has been given:"
528 1.1 jtc echo >&2 ""
529 1.9 christos case $country%$region%$coord in
530 1.14 christos ?*%?*%) say >&2 " $country$newline $region";;
531 1.14 christos ?*%%) say >&2 " $country";;
532 1.14 christos %?*%?*) say >&2 " coord $coord$newline $region";;
533 1.14 christos %%?*) say >&2 " coord $coord";;
534 1.14 christos *) say >&2 " TZ='$TZ'"
535 1.1 jtc esac
536 1.14 christos say >&2 ""
537 1.14 christos say >&2 "Therefore TZ='$TZ' will be used.$extra_info"
538 1.14 christos say >&2 "Is the above information OK?"
539 1.1 jtc
540 1.10 christos doselect Yes No
541 1.10 christos ok=$select_result
542 1.1 jtc case $ok in
543 1.1 jtc Yes) break
544 1.1 jtc esac
545 1.9 christos do coord=
546 1.1 jtc done
547 1.1 jtc
548 1.5 kleink case $SHELL in
549 1.5 kleink *csh) file=.login line="setenv TZ '$TZ'";;
550 1.5 kleink *) file=.profile line="TZ='$TZ'; export TZ"
551 1.5 kleink esac
552 1.5 kleink
553 1.15 christos test -t 1 && say >&2 "
554 1.5 kleink You can make this change permanent for yourself by appending the line
555 1.5 kleink $line
556 1.5 kleink to the file '$file' in your home directory; then log out and log in again.
557 1.5 kleink
558 1.5 kleink Here is that TZ value again, this time on standard output so that you
559 1.5 kleink can use the $0 command in shell scripts:"
560 1.5 kleink
561 1.14 christos say "$TZ"
562