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