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