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