tzselect.ksh 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. #!/bin/bash
  2. # Ask the user about the time zone, and output the resulting TZ value to stdout.
  3. # Interact with the user via stderr and stdin.
  4. PKGVERSION='(tzcode) '
  5. TZVERSION=see_Makefile
  6. REPORT_BUGS_TO=tz@iana.org
  7. # Contributed by Paul Eggert. This file is in the public domain.
  8. # Porting notes:
  9. #
  10. # This script requires a Posix-like shell and prefers the extension of a
  11. # 'select' statement. The 'select' statement was introduced in the
  12. # Korn shell and is available in Bash and other shell implementations.
  13. # If your host lacks both Bash and the Korn shell, you can get their
  14. # source from one of these locations:
  15. #
  16. # Bash <https://www.gnu.org/software/bash/>
  17. # Korn Shell <http://www.kornshell.com/>
  18. # MirBSD Korn Shell <http://www.mirbsd.org/mksh.htm>
  19. #
  20. # For portability to Solaris 10 /bin/sh (supported by Oracle through
  21. # January 2025) this script avoids some POSIX features and common
  22. # extensions, such as $(...) (which works sometimes but not others),
  23. # $((...)), ! CMD, ${#ID}, ${ID##PAT}, ${ID%%PAT}, and $10.
  24. #
  25. # This script also uses several features of modern awk programs.
  26. # If your host lacks awk, or has an old awk that does not conform to Posix,
  27. # you can use either of the following free programs instead:
  28. #
  29. # Gawk (GNU awk) <https://www.gnu.org/software/gawk/>
  30. # mawk <https://invisible-island.net/mawk/>
  31. # nawk <https://github.com/onetrueawk/awk>
  32. # Specify default values for environment variables if they are unset.
  33. : ${AWK=awk}
  34. : ${TZDIR=`pwd`}
  35. # Output one argument as-is to standard output, with trailing newline.
  36. # Safer than 'echo', which can mishandle '\' or leading '-'.
  37. say() {
  38. printf '%s\n' "$1"
  39. }
  40. # Check for awk Posix compliance.
  41. ($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
  42. [ $? = 123 ] || {
  43. say >&2 "$0: Sorry, your '$AWK' program is not Posix compatible."
  44. exit 1
  45. }
  46. coord=
  47. location_limit=10
  48. zonetabtype=zone1970
  49. usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
  50. Select a timezone interactively.
  51. Options:
  52. -c COORD
  53. Instead of asking for continent and then country and then city,
  54. ask for selection from time zones whose largest cities
  55. are closest to the location with geographical coordinates COORD.
  56. COORD should use ISO 6709 notation, for example, '-c +4852+00220'
  57. for Paris (in degrees and minutes, North and East), or
  58. '-c -35-058' for Buenos Aires (in degrees, South and West).
  59. -n LIMIT
  60. Display at most LIMIT locations when -c is used (default $location_limit).
  61. --version
  62. Output version information.
  63. --help
  64. Output this help.
  65. Report bugs to $REPORT_BUGS_TO."
  66. # Ask the user to select from the function's arguments,
  67. # and assign the selected argument to the variable 'select_result'.
  68. # Exit on EOF or I/O error. Use the shell's nicer 'select' builtin if
  69. # available, falling back on a portable substitute otherwise.
  70. if
  71. case $BASH_VERSION in
  72. ?*) : ;;
  73. '')
  74. # '; exit' should be redundant, but Dash doesn't properly fail without it.
  75. (eval 'set --; select x; do break; done; exit') </dev/null 2>/dev/null
  76. esac
  77. then
  78. # Do this inside 'eval', as otherwise the shell might exit when parsing it
  79. # even though it is never executed.
  80. eval '
  81. doselect() {
  82. select select_result
  83. do
  84. case $select_result in
  85. "") echo >&2 "Please enter a number in range." ;;
  86. ?*) break
  87. esac
  88. done || exit
  89. }
  90. '
  91. else
  92. doselect() {
  93. # Field width of the prompt numbers.
  94. select_width=`expr $# : '.*'`
  95. select_i=
  96. while :
  97. do
  98. case $select_i in
  99. '')
  100. select_i=0
  101. for select_word
  102. do
  103. select_i=`expr $select_i + 1`
  104. printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word"
  105. done ;;
  106. *[!0-9]*)
  107. echo >&2 'Please enter a number in range.' ;;
  108. *)
  109. if test 1 -le $select_i && test $select_i -le $#; then
  110. shift `expr $select_i - 1`
  111. select_result=$1
  112. break
  113. fi
  114. echo >&2 'Please enter a number in range.'
  115. esac
  116. # Prompt and read input.
  117. printf >&2 %s "${PS3-#? }"
  118. read select_i || exit
  119. done
  120. }
  121. fi
  122. while getopts c:n:t:-: opt
  123. do
  124. case $opt$OPTARG in
  125. c*)
  126. coord=$OPTARG ;;
  127. n*)
  128. location_limit=$OPTARG ;;
  129. t*) # Undocumented option, used for developer testing.
  130. zonetabtype=$OPTARG ;;
  131. -help)
  132. exec echo "$usage" ;;
  133. -version)
  134. exec echo "tzselect $PKGVERSION$TZVERSION" ;;
  135. -*)
  136. say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;;
  137. *)
  138. say >&2 "$0: try '$0 --help'"; exit 1 ;;
  139. esac
  140. done
  141. shift `expr $OPTIND - 1`
  142. case $# in
  143. 0) ;;
  144. *) say >&2 "$0: $1: unknown argument"; exit 1 ;;
  145. esac
  146. # Make sure the tables are readable.
  147. TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
  148. TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab
  149. for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
  150. do
  151. <"$f" || {
  152. say >&2 "$0: time zone files are not set up correctly"
  153. exit 1
  154. }
  155. done
  156. # If the current locale does not support UTF-8, convert data to current
  157. # locale's format if possible, as the shell aligns columns better that way.
  158. # Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI.
  159. $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) != 1 }' || {
  160. { tmp=`(mktemp -d) 2>/dev/null` || {
  161. tmp=${TMPDIR-/tmp}/tzselect.$$ &&
  162. (umask 77 && mkdir -- "$tmp")
  163. };} &&
  164. trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM &&
  165. (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \
  166. 2>/dev/null &&
  167. TZ_COUNTRY_TABLE=$tmp/iso3166.tab &&
  168. iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab &&
  169. TZ_ZONE_TABLE=$tmp/$zonetabtype.tab
  170. }
  171. newline='
  172. '
  173. IFS=$newline
  174. # Awk script to output a country list.
  175. output_country_list='
  176. BEGIN { FS = "\t" }
  177. /^#$/ { next }
  178. /^#[^@]/ { next }
  179. {
  180. commentary = $0 ~ /^#@/
  181. if (commentary) {
  182. col1ccs = substr($1, 3)
  183. conts = $2
  184. } else {
  185. col1ccs = $1
  186. conts = $3
  187. }
  188. ncc = split(col1ccs, cc, /,/)
  189. ncont = split(conts, cont, /,/)
  190. for (i = 1; i <= ncc; i++) {
  191. elsewhere = commentary
  192. for (ci = 1; ci <= ncont; ci++) {
  193. if (cont[ci] ~ continent_re) {
  194. if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i]
  195. elsewhere = 0
  196. }
  197. }
  198. if (elsewhere) {
  199. for (i = 1; i <= ncc; i++) {
  200. cc_elsewhere[cc[i]] = 1
  201. }
  202. }
  203. }
  204. }
  205. END {
  206. while (getline <TZ_COUNTRY_TABLE) {
  207. if ($0 !~ /^#/) cc_name[$1] = $2
  208. }
  209. for (i = 1; i <= ccs; i++) {
  210. country = cc_list[i]
  211. if (cc_elsewhere[country]) continue
  212. if (cc_name[country]) {
  213. country = cc_name[country]
  214. }
  215. print country
  216. }
  217. }
  218. '
  219. # Awk script to read a time zone table and output the same table,
  220. # with each row preceded by its distance from 'here'.
  221. # If output_times is set, each row is instead preceded by its local time
  222. # and any apostrophes are escaped for the shell.
  223. output_distances_or_times='
  224. BEGIN {
  225. FS = "\t"
  226. if (!output_times) {
  227. while (getline <TZ_COUNTRY_TABLE)
  228. if ($0 ~ /^[^#]/)
  229. country[$1] = $2
  230. country["US"] = "US" # Otherwise the strings get too long.
  231. }
  232. }
  233. function abs(x) {
  234. return x < 0 ? -x : x;
  235. }
  236. function min(x, y) {
  237. return x < y ? x : y;
  238. }
  239. function convert_coord(coord, deg, minute, ilen, sign, sec) {
  240. if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9][0-9][0-9]([^0-9]|$)/) {
  241. degminsec = coord
  242. intdeg = degminsec < 0 ? -int(-degminsec / 10000) : int(degminsec / 10000)
  243. minsec = degminsec - intdeg * 10000
  244. intmin = minsec < 0 ? -int(-minsec / 100) : int(minsec / 100)
  245. sec = minsec - intmin * 100
  246. deg = (intdeg * 3600 + intmin * 60 + sec) / 3600
  247. } else if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9]([^0-9]|$)/) {
  248. degmin = coord
  249. intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100)
  250. minute = degmin - intdeg * 100
  251. deg = (intdeg * 60 + minute) / 60
  252. } else
  253. deg = coord
  254. return deg * 0.017453292519943296
  255. }
  256. function convert_latitude(coord) {
  257. match(coord, /..*[-+]/)
  258. return convert_coord(substr(coord, 1, RLENGTH - 1))
  259. }
  260. function convert_longitude(coord) {
  261. match(coord, /..*[-+]/)
  262. return convert_coord(substr(coord, RLENGTH))
  263. }
  264. # Great-circle distance between points with given latitude and longitude.
  265. # Inputs and output are in radians. This uses the great-circle special
  266. # case of the Vicenty formula for distances on ellipsoids.
  267. function gcdist(lat1, long1, lat2, long2, dlong, x, y, num, denom) {
  268. dlong = long2 - long1
  269. x = cos(lat2) * sin(dlong)
  270. y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlong)
  271. num = sqrt(x * x + y * y)
  272. denom = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(dlong)
  273. return atan2(num, denom)
  274. }
  275. # Parallel distance between points with given latitude and longitude.
  276. # This is the product of the longitude difference and the cosine
  277. # of the latitude of the point that is further from the equator.
  278. # I.e., it considers longitudes to be further apart if they are
  279. # nearer the equator.
  280. function pardist(lat1, long1, lat2, long2) {
  281. return abs(long1 - long2) * min(cos(lat1), cos(lat2))
  282. }
  283. # The distance function is the sum of the great-circle distance and
  284. # the parallel distance. It could be weighted.
  285. function dist(lat1, long1, lat2, long2) {
  286. return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2)
  287. }
  288. BEGIN {
  289. coord_lat = convert_latitude(coord)
  290. coord_long = convert_longitude(coord)
  291. }
  292. /^[^#]/ {
  293. inline[inlines++] = $0
  294. ncc = split($1, cc, /,/)
  295. for (i = 1; i <= ncc; i++)
  296. cc_used[cc[i]]++
  297. }
  298. END {
  299. for (h = 0; h < inlines; h++) {
  300. $0 = inline[h]
  301. line = $1 "\t" $2 "\t" $3
  302. sep = "\t"
  303. ncc = split($1, cc, /,/)
  304. split("", item_seen)
  305. item_seen[""] = 1
  306. for (i = 1; i <= ncc; i++) {
  307. item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4
  308. if (item_seen[item]++) continue
  309. line = line sep item
  310. sep = "; "
  311. }
  312. if (output_times) {
  313. fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n"
  314. gsub(/'\''/, "&\\\\&&", line)
  315. printf fmt, $3, h, line
  316. } else {
  317. here_lat = convert_latitude($2)
  318. here_long = convert_longitude($2)
  319. printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
  320. }
  321. }
  322. }
  323. '
  324. # Begin the main loop. We come back here if the user wants to retry.
  325. while
  326. echo >&2 'Please identify a location' \
  327. 'so that time zone rules can be set correctly.'
  328. continent=
  329. country=
  330. region=
  331. case $coord in
  332. ?*)
  333. continent=coord;;
  334. '')
  335. # Ask the user for continent or ocean.
  336. echo >&2 'Please select a continent, ocean, "coord", "TZ", or "time".'
  337. quoted_continents=`
  338. $AWK '
  339. function handle_entry(entry) {
  340. entry = substr(entry, 1, index(entry, "/") - 1)
  341. if (entry == "America")
  342. entry = entry "s"
  343. if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/)
  344. entry = entry " Ocean"
  345. printf "'\''%s'\''\n", entry
  346. }
  347. BEGIN { FS = "\t" }
  348. /^[^#]/ {
  349. handle_entry($3)
  350. }
  351. /^#@/ {
  352. ncont = split($2, cont, /,/)
  353. for (ci = 1; ci <= ncont; ci++) {
  354. handle_entry(cont[ci])
  355. }
  356. }
  357. ' <"$TZ_ZONE_TABLE" |
  358. sort -u |
  359. tr '\n' ' '
  360. echo ''
  361. `
  362. eval '
  363. doselect '"$quoted_continents"' \
  364. "coord - I want to use geographical coordinates." \
  365. "TZ - I want to specify the timezone using the Posix TZ format." \
  366. "time - I know local time already."
  367. continent=$select_result
  368. case $continent in
  369. Americas) continent=America;;
  370. *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''`
  371. esac
  372. '
  373. esac
  374. case $continent in
  375. TZ)
  376. # Ask the user for a Posix TZ string. Check that it conforms.
  377. while
  378. echo >&2 'Please enter the desired value' \
  379. 'of the TZ environment variable.'
  380. echo >&2 'For example, AEST-10 is abbreviated' \
  381. 'AEST and is 10 hours'
  382. echo >&2 'ahead (east) of Greenwich,' \
  383. 'with no daylight saving time.'
  384. read TZ
  385. $AWK -v TZ="$TZ" 'BEGIN {
  386. tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})"
  387. time = "(2[0-4]|[0-1]?[0-9])" \
  388. "(:[0-5][0-9](:[0-5][0-9])?)?"
  389. offset = "[-+]?" time
  390. mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]"
  391. jdate = "((J[1-9]|[0-9]|J?[1-9][0-9]" \
  392. "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])"
  393. datetime = ",(" mdate "|" jdate ")(/" time ")?"
  394. tzpattern = "^(:.*|" tzname offset "(" tzname \
  395. "(" offset ")?(" datetime datetime ")?)?)$"
  396. if (TZ ~ tzpattern) exit 1
  397. exit 0
  398. }'
  399. do
  400. say >&2 "'$TZ' is not a conforming Posix timezone string."
  401. done
  402. TZ_for_date=$TZ;;
  403. *)
  404. case $continent in
  405. coord)
  406. case $coord in
  407. '')
  408. echo >&2 'Please enter coordinates' \
  409. 'in ISO 6709 notation.'
  410. echo >&2 'For example, +4042-07403 stands for'
  411. echo >&2 '40 degrees 42 minutes north,' \
  412. '74 degrees 3 minutes west.'
  413. read coord;;
  414. esac
  415. distance_table=`$AWK \
  416. -v coord="$coord" \
  417. -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
  418. "$output_distances_or_times" <"$TZ_ZONE_TABLE" |
  419. sort -n |
  420. sed "${location_limit}q"
  421. `
  422. regions=`$AWK \
  423. -v distance_table="$distance_table" '
  424. BEGIN {
  425. nlines = split(distance_table, line, /\n/)
  426. for (nr = 1; nr <= nlines; nr++) {
  427. nf = split(line[nr], f, /\t/)
  428. print f[nf]
  429. }
  430. }
  431. '`
  432. echo >&2 'Please select one of the following timezones,'
  433. echo >&2 'listed roughly in increasing order' \
  434. "of distance from $coord".
  435. doselect $regions
  436. region=$select_result
  437. TZ=`$AWK \
  438. -v distance_table="$distance_table" \
  439. -v region="$region" '
  440. BEGIN {
  441. nlines = split(distance_table, line, /\n/)
  442. for (nr = 1; nr <= nlines; nr++) {
  443. nf = split(line[nr], f, /\t/)
  444. if (f[nf] == region) {
  445. print f[4]
  446. }
  447. }
  448. }
  449. '`
  450. ;;
  451. *)
  452. case $continent in
  453. time)
  454. minute_format='%a %b %d %H:%M'
  455. old_minute=`TZ=UTC0 date +"$minute_format"`
  456. for i in 1 2 3
  457. do
  458. time_table_command=`
  459. $AWK -v output_times=1 \
  460. "$output_distances_or_times" <"$TZ_ZONE_TABLE"
  461. `
  462. time_table=`eval "$time_table_command"`
  463. new_minute=`TZ=UTC0 date +"$minute_format"`
  464. case $old_minute in
  465. "$new_minute") break;;
  466. esac
  467. old_minute=$new_minute
  468. done
  469. echo >&2 "The system says Universal Time is $new_minute."
  470. echo >&2 "Assuming that's correct, what is the local time?"
  471. eval doselect `
  472. say "$time_table" |
  473. sort -k2n -k2,5 -k1n |
  474. $AWK '{
  475. line = $6 " " $7 " " $4 " " $5
  476. if (line == oldline) next
  477. oldline = line
  478. gsub(/'\''/, "&\\\\&&", line)
  479. printf "'\''%s'\''\n", line
  480. }'
  481. `
  482. time=$select_result
  483. zone_table=`
  484. say "$time_table" |
  485. $AWK -v time="$time" '{
  486. if ($6 " " $7 " " $4 " " $5 == time) {
  487. sub(/[^\t]*\t/, "")
  488. print
  489. }
  490. }'
  491. `
  492. countries=`
  493. say "$zone_table" |
  494. $AWK \
  495. -v continent_re='' \
  496. -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
  497. "$output_country_list" |
  498. sort -f
  499. `
  500. ;;
  501. *)
  502. zone_table=file
  503. # Get list of names of countries in the continent or ocean.
  504. countries=`$AWK \
  505. -v continent_re="^$continent/" \
  506. -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
  507. "$output_country_list" \
  508. <"$TZ_ZONE_TABLE" | sort -f
  509. `;;
  510. esac
  511. # If there's more than one country, ask the user which one.
  512. case $countries in
  513. *"$newline"*)
  514. echo >&2 'Please select a country' \
  515. 'whose clocks agree with yours.'
  516. doselect $countries
  517. country_result=$select_result
  518. country=$select_result;;
  519. *)
  520. country=$countries
  521. esac
  522. # Get list of timezones in the country.
  523. regions=`
  524. case $zone_table in
  525. file) cat -- "$TZ_ZONE_TABLE";;
  526. *) say "$zone_table";;
  527. esac |
  528. $AWK \
  529. -v country="$country" \
  530. -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
  531. '
  532. BEGIN {
  533. FS = "\t"
  534. cc = country
  535. while (getline <TZ_COUNTRY_TABLE) {
  536. if ($0 !~ /^#/ && country == $2) {
  537. cc = $1
  538. break
  539. }
  540. }
  541. }
  542. /^#/ { next }
  543. $1 ~ cc { print $4 }
  544. '
  545. `
  546. # If there's more than one region, ask the user which one.
  547. case $regions in
  548. *"$newline"*)
  549. echo >&2 'Please select one of the following timezones.'
  550. doselect $regions
  551. region=$select_result
  552. esac
  553. # Determine TZ from country and region.
  554. TZ=`
  555. case $zone_table in
  556. file) cat -- "$TZ_ZONE_TABLE";;
  557. *) say "$zone_table";;
  558. esac |
  559. $AWK \
  560. -v country="$country" \
  561. -v region="$region" \
  562. -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
  563. '
  564. BEGIN {
  565. FS = "\t"
  566. cc = country
  567. while (getline <TZ_COUNTRY_TABLE) {
  568. if ($0 !~ /^#/ && country == $2) {
  569. cc = $1
  570. break
  571. }
  572. }
  573. }
  574. /^#/ { next }
  575. $1 ~ cc && ($4 == region || !region) { print $3 }
  576. '
  577. `;;
  578. esac
  579. # Make sure the corresponding zoneinfo file exists.
  580. TZ_for_date=$TZDIR/$TZ
  581. <"$TZ_for_date" || {
  582. say >&2 "$0: time zone files are not set up correctly"
  583. exit 1
  584. }
  585. esac
  586. # Use the proposed TZ to output the current date relative to UTC.
  587. # Loop until they agree in seconds.
  588. # Give up after 8 unsuccessful tries.
  589. extra_info=
  590. for i in 1 2 3 4 5 6 7 8
  591. do
  592. TZdate=`LANG=C TZ="$TZ_for_date" date`
  593. UTdate=`LANG=C TZ=UTC0 date`
  594. TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'`
  595. UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'`
  596. case $TZsec in
  597. $UTsec)
  598. extra_info="
  599. Selected time is now: $TZdate.
  600. Universal Time is now: $UTdate."
  601. break
  602. esac
  603. done
  604. # Output TZ info and ask the user to confirm.
  605. echo >&2 ""
  606. echo >&2 "Based on the following information:"
  607. echo >&2 ""
  608. case $time%$country_result%$region%$coord in
  609. ?*%?*%?*%)
  610. say >&2 " $time$newline $country_result$newline $region";;
  611. ?*%?*%%|?*%%?*%) say >&2 " $time$newline $country_result$region";;
  612. ?*%%%) say >&2 " $time";;
  613. %?*%?*%) say >&2 " $country_result$newline $region";;
  614. %?*%%) say >&2 " $country_result";;
  615. %%?*%?*) say >&2 " coord $coord$newline $region";;
  616. %%%?*) say >&2 " coord $coord";;
  617. *) say >&2 " TZ='$TZ'"
  618. esac
  619. say >&2 ""
  620. say >&2 "TZ='$TZ' will be used.$extra_info"
  621. say >&2 "Is the above information OK?"
  622. doselect Yes No
  623. ok=$select_result
  624. case $ok in
  625. Yes) break
  626. esac
  627. do coord=
  628. done
  629. case $SHELL in
  630. *csh) file=.login line="setenv TZ '$TZ'";;
  631. *) file=.profile line="TZ='$TZ'; export TZ"
  632. esac
  633. test -t 1 && say >&2 "
  634. You can make this change permanent for yourself by appending the line
  635. $line
  636. to the file '$file' in your home directory; then log out and log in again.
  637. Here is that TZ value again, this time on standard output so that you
  638. can use the $0 command in shell scripts:"
  639. say "$TZ"