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