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