2 # Desc: Baltakatei's verbose date command
3 # Usage: bkdatev [args]
4 # Example: bkdatev --date="2001-09-11T09:02:59-04"
6 # Depends: GNU Coreutils 8.32, Bash 3.2.57
7 # Ref/Attrib: [1] "ISO 8601". Wikipedia. https://en.wikipedia.org/wiki/ISO_8601
8 # [2] "Changing the Locale in Wine" https://stackoverflow.com/a/16428951
9 # [3] "Shanghai vs Beijing" https://bugs.launchpad.net/ubuntu/+source/libgweather/+bug/228554
10 # [4] “Understanding the "right" time zone database” https://kenta.blogspot.com/2016/03/sqfzcxay-understanding-right-time-zone.html
11 # Notes: * Check `ls -R /usr/share/zoneinfo` for time zone names.
12 # * Check `cat /usr/share/i18n/SUPPORTED` for supported locales.
13 # * For list of valid locales, see: https://manpages.ubuntu.com/manpages/bionic/man3/DateTime::Locale::Catalog.3pm.html
14 # * Locations chosen for population, personal signifiance, and spatial coverage.
15 # * For International Atomic Time (TAI), use offsets from UTC provided in `/usr/share/zoneinfo/leap-seconds.list`.
16 # * Compatibility with macOS may be limited if any arguments
17 # are provided when running `bkdatev`; e.g. passing a
18 # `--date` option to `bkdatev` will fail.
20 yell
() { echo "$0: $*" >&2; }
21 die
() { yell
"$*"; exit 111; }
22 must
() { "$@" || die
"cannot $*"; }
24 # Desc: Inserts a string into another string at a specified position.
25 # Input: arg1: str str_rec String to receive insertion
26 # arg2: int pos Insertion position (0 = append to front)
27 # arg3: str str_ins String to be inserted
28 # Output: stdout: Combined string
30 # Depends: BK-2020-03: yell(), die(), must()
31 # Ref/Attrib: * BK-2020-03: https://gitlab.com/baltakatei/baltakatei-exdev/
32 # * Cooper, Mendel. “Advanced Bash-Scripting Guide: Manipulating Strings”. tldp.org https://tldp.org/LDP/abs/html/string-manipulation.html
34 local str_rec pos str_ins re len_str_rec
;
35 local pfx_pos_start pfx_len pfx
;
36 local sfx_pos_start sfx_len sfx
;
39 if [[ $# -ne 3 ]]; then
40 yell
"ERROR:Invalid argument count:$#";
43 if [[ ! "$2" =~
$re ]]; then
44 yell
"ERROR:Not an int:$2";
50 # Calculate string stats
51 len_str_rec
="${#str_rec}";
56 pfx
="${str_rec:$pfx_pos_start:$pfx_len}";
59 sfx_pos_start
="$(( pos ))";
60 sfx_len
="$(( len_str_rec - pos ))";
61 sfx
="${str_rec:$sfx_pos_start:$sfx_len}";
63 # Print output to stdout
64 printf "%s%s%s\n" "$pfx" "$str_ins" "$sfx";
65 }; # Insert string provided at indicated position via stdout
70 if ! ((n_ln
% "$skip_every")); then
74 }; # periodically print separating blank line
76 # Desc: Get from 'date' the timezone UTC offset in a way
77 # compatible with both GNU Coreutils and BSD versions.
78 # Input: env var: TZ (time zone for date; e.g. 'America/Denver')
79 # args: $@ # passed onto `date`
80 # Depends: date (GNU Coreutils 8.32 or BSD), rev
81 local ntz ntz ntz_out
;
84 # Get numeric time zone string in way compatible with GNU Coreutils and BSD
85 ntz
="$(date "+%z
" "$@
")"; # e.g. "+0530"
87 # Check if last two characters are trailing zeros that can be removed.
88 last2
="${ntz:3:2}"; # assumes $ntz is 5 characters (i.e. "±HHMM")
89 #last2="$(rev <<< $ntz)" && last2="${last2:0:2}" && last2="$(rev <<< "$last2")";
90 if [[ "$last2" == "00" ]]; then
91 ## ntz_out is truncated by 2 characters
93 len_ntz_out
="$(( len_ntz - 2 ))";
94 ntz_out
=""${ntz:0:$len_ntz_out};
96 ## ntz_out is ntz with semicolon inserted after HH
97 ntz_out
="$(insertStr "$ntz" 3 ":" )";
101 printf "%s" "$ntz_out";
102 }; # Format numeric time zone (for BSD date compatibility)
108 # args: $@ # passed on to `date`
109 # env var: TZ (time zone for date; e.g. 'America/Denver')
111 # Depends: printf, date
113 # Ref/Attrib: * Truncate string in printf https://stackoverflow.com/a/46812677
114 local s_1 s_2 s_2_tz s_3 s_4
;
117 s_2
="$(date "$@
" "$fs_1")"; # ISO-8601 without numeric timezone
118 s_3
="$(date "$@
" "$fs_2")"; # Alternate ISO-8601 expressions
119 s_4
="$(date "$@
" "$fs_3")"; # locale-specific date strings
121 # Append numeric timezone to $s_2 with appropriate format
122 # (e.g. '-07' for 'Arizona', '+05:45' for 'Asia/Kathmandu')
123 s_2_tz
="$(get_tz_offset "$@
")";
124 s_2
="$( printf "%s
%s
" "$s_2" "$s_2_tz" )";
126 printf "%-10.10s %-25.25s (%-20.20s) (%s)" "$s_1" "$s_2" "$s_3" "$s_4";
129 unset fs_1 fs_2 fs_3 fs_4
;
130 }; # print line of dates
132 n_ln
=0; # for line_sep()
133 unset LC_TIME
; # Fall back to time zone-specific locale settings.
136 fs_iso8601
="+%Y-%m-%dT%H:%M:%S"; # typical ISO-8601 without timezone
137 fs_iso8601_etc
="+%G-W%V-%u, %Y-%j"; # alternate ISO-8601 dates
138 fs_locale
="+%Z; %A; %c"; # locale-specific date strings
140 # vars for print_dateline()
142 fs_2
="$fs_iso8601_etc";
145 # UTC (pop. (2021): 7,837,000,000)
149 date_string
="$(date -Is)";
150 right_epoch
="$(TZ=right/UTC date --date="$date_string" +%s;)"; # see [4]
151 fs_3
="+%s seconds (${right_epoch} with leap seconds) since 1970-01-01T00:00+00";
158 export LANG
="haw-US.UTF8";
165 export TZ
=America
/Los_Angeles
;
166 export LANG
="en_US.UTF-8";
171 # Denver, USA (pop. (2021): 711,463)
173 export TZ
=America
/Denver
;
174 export LANG
="en_US.UTF-8";
179 # Chicago, USA (pop. (2021): 711,463)
181 export TZ
=America
/Chicago
;
182 export LANG
="en_US.UTF-8";
187 # Mexico City, Mexico (pop. (2018): 21,804,515)
189 export TZ
=America
/Mexico_City
;
190 export LANG
="es_MX.UTF8";
195 # Panama City, Panama
197 export TZ
=America
/Panama
;
198 export LANG
="es_PA.UTF8";
203 # New York, USA (pop. (2018): 20,140,470)
205 export TZ
=America
/New_York
;
206 export LANG
="en_US.UTF-8";
213 export TZ
=America
/Sao_Paulo
;
214 export LANG
="pt_BR.UTF8";
221 export TZ
=America
/Argentina
/Buenos_Aires
;
222 export LANG
="es_AR.UTF8";
229 export TZ
=Europe
/London
;
230 export LANG
="en_GB.UTF-8";
237 export TZ
=Africa
/Kinshasa
;
238 export LANG
="ln_CD.UTF8";
245 export TZ
=Africa
/Lagos
;
246 export LANG
="en_NG.UTF8";
253 export TZ
=Europe
/Paris
;
254 export LANG
="fr_FR.UTF8";
261 export TZ
=Europe
/Stockholm
;
262 export LANG
="sv_SE.UTF8";
269 export TZ
=Europe
/Helsinki
;
270 export LANG
="fi_FI.UTF8";
277 export TZ
=Africa
/Cairo
;
278 export LANG
="ar_EG.UTF8";
283 # Gaza City, Palestine (pop. (2017): 590,481)
286 export LANG
="ar_JO.UTF-8"; # ar_PS is missing as of 2023-10-18, using ar_JO as closest substitute.
291 # Tel Aviv, Israel (pop. (2021): 467,875)
293 export TZ
=Asia
/Tel_Aviv
;
294 export LANG
="he_IL.UTF-8";
299 # Athens (pop. (2020): 3,526,887)
301 export TZ
=Europe
/Athens
;
302 export LANG
="el_GR.UTF8";
307 # Istanbul (pop. (2020): 13,719,061)
309 export TZ
=Asia
/Istanbul
;
310 export LANG
="tr_TR.UTF8";
317 export TZ
=Asia
/Tehran
;
318 export LANG
="fa_IR.UTF8";
325 export TZ
=Europe
/Moscow
;
326 export LANG
="ru_RU.UTF-8";
331 # Kyiv, Ukraine (pop. (2021): 2,962,180)
333 export TZ
=Europe
/Kyiv
;
334 export LANG
="uk_UA.UTF-8";
339 # Delhi, India (pop. (2018): 29,000,000)
341 export TZ
=Asia
/Kolkata
;
342 export LANG
="hi_IN.UTF-8";
347 # Jakarta, Indonesia (pop. (2018): 33,430,285)
349 export TZ
=Asia
/Jakarta
;
350 export LANG
="id_ID.UTF8";
355 # Singapore, Singapore (pop (2018): 5,792,000)
357 export TZ
=Asia
/Singapore
;
358 export LANG
="en_SG.UTF-8";
363 # Beijing, China (pop. (2018): 19,618,000)
365 export TZ
=Asia
/Shanghai
; # [3]
366 export LANG
="zh_CN.UTF-8";
371 # Taipei, Taiwan (pop (2019): 7,034,084)
373 export TZ
=Asia
/Taipei
; # [3]
374 export LANG
="zh_TW.UTF-8";
379 # Tokyo, Japan (pop. (2018): 37,274,000)
381 export TZ
=Asia
/Tokyo
;
382 export LANG
="ja_JP.UTF8";
387 # Seoul, South Korea (pop. (2018): 25,514,000)
389 export TZ
=Asia
/Seoul
;
390 export LANG
="ko_KR.UTF8";
395 # Pyongyang, North Korea
397 export TZ
=Asia
/Pyongyang
;
398 export LANG
="ko_KP.UTF8";
405 export TZ
=Australia
/Sydney
;
406 export LANG
="en_AU.UTF8";
413 export TZ
=Pacific
/Guam
;
414 export LANG
="en_GU.UTF8";
419 # Auckland, New Zealand
421 export TZ
=Pacific
/Auckland
;
422 export LANG
="en_NZ.UTF8";
432 # Author: Steven Baltakatei Sandoval