tzselect.ksh 6.9 KB

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