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