tzselect.ksh revision 1.5 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 (at] 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.
51 TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
52 TZ_ZONE_TABLE=$TZDIR/zone.tab
53 for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
54 do
55 <$f || {
56 echo >&2 "$0: time zone files are not set up correctly"
57 exit 1
58 }
59 done
60
61 newline='
62 '
63 IFS=$newline
64
65
66 # Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
67 case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in
68 ?*) PS3=
69 esac
70
71
72 # Begin the main loop. We come back here if the user wants to retry.
73 while
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="
261 Local time is now: $TZdate.
262 Universal 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
294 do :
295 done
296
297 case $SHELL in
298 *csh) file=.login line="setenv TZ '$TZ'";;
299 *) file=.profile line="TZ='$TZ'; export TZ"
300 esac
301
302 echo >&2 "
303 You can make this change permanent for yourself by appending the line
304 $line
305 to the file '$file' in your home directory; then log out and log in again.
306
307 Here is that TZ value again, this time on standard output so that you
308 can use the $0 command in shell scripts:"
309
310 echo "$TZ"
311