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