doc(user/bkdatev):Add comment warning about macOS compatibility
[BK-2020-03.git] / user / bkdatev
1 #!/usr/bin/env bash
2 # Desc: Baltakatei's verbose date command
3 # Usage: bkdatev [args]
4 # Example: bkdatev --date="2001-09-11T09:02:59-04"
5 # Version: 0.3.2
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 # Notes: * Check `ls -R /usr/share/zoneinfo` for time zone names.
11 # * Check `cat /usr/share/i18n/SUPPORTED` for supported locales.
12 # * For list of valid locales, see: https://manpages.ubuntu.com/manpages/bionic/man3/DateTime::Locale::Catalog.3pm.html
13 # * Locations chosen for population, personal signifiance, and spatial coverage.
14 # * For International Atomic Time (TAI), use offsets from UTC provided in `/usr/share/zoneinfo/leap-seconds.list`.
15 # * Compatibility with macOS may be limited if any arguments
16 # are provided when running `bkdatev`; e.g. passing a
17 # `--date` option to `bkdatev` will fail.
18
19 yell() { echo "$0: $*" >&2; }
20 die() { yell "$*"; exit 111; }
21 must() { "$@" || die "cannot $*"; }
22 insertStr() {
23 # Desc: Inserts a string into another string at a specified position.
24 # Input: arg1: str str_rec String to receive insertion
25 # arg2: int pos Insertion position (0 = append to front)
26 # arg3: str str_ins String to be inserted
27 # Output: stdout: Combined string
28 # Version: 0.0.2
29 # Depends: BK-2020-03: yell(), die(), must()
30 # Ref/Attrib: * BK-2020-03: https://gitlab.com/baltakatei/baltakatei-exdev/
31 # * Cooper, Mendel. “Advanced Bash-Scripting Guide: Manipulating Strings”. tldp.org https://tldp.org/LDP/abs/html/string-manipulation.html
32
33 local str_rec pos str_ins re len_str_rec;
34 local pfx_pos_start pfx_len pfx;
35 local sfx_pos_start sfx_len sfx;
36
37 # Check args
38 if [[ $# -ne 3 ]]; then
39 yell "ERROR:Invalid argument count:$#";
40 return 1; fi;
41 re='^[0-9]+$';
42 if [[ ! "$2" =~ $re ]]; then
43 yell "ERROR:Not an int:$2";
44 return 1; fi;
45 str_rec="$1";
46 pos="$2";
47 str_ins="$3";
48
49 # Calculate string stats
50 len_str_rec="${#str_rec}";
51
52 # Form prefix
53 pfx_pos_start="0";
54 pfx_len="$pos";
55 pfx="${str_rec:$pfx_pos_start:$pfx_len}";
56
57 # Form suffix
58 sfx_pos_start="$(( pos ))";
59 sfx_len="$(( len_str_rec - pos ))";
60 sfx="${str_rec:$sfx_pos_start:$sfx_len}";
61
62 # Print output to stdout
63 printf "%s%s%s\n" "$pfx" "$str_ins" "$sfx";
64 }; # Insert string provided at indicated position via stdout
65 line_sep() {
66 # Input: var: n_ln
67 local skip_every=4;
68 ((n_ln++));
69 if ! ((n_ln % "$skip_every")); then
70 printf "\n";
71 fi;
72 return 0;
73 }; # periodically print separating blank line
74 get_tz_offset() {
75 # Desc: Get from 'date' the timezone UTC offset in a way
76 # compatible with both GNU Coreutils and BSD versions.
77 # Input: env var: TZ (time zone for date; e.g. 'America/Denver')
78 # Depends: date (GNU Coreutils 8.32 or BSD), rev
79 local ntz ntz ntz_out;
80 local last2;
81
82 # Get numeric time zone string in way compatible with GNU Coreutils and BSD
83 ntz="$(date "+%z")"; # e.g. "+0530"
84
85 # Check if last two characters are trailing zeros that can be removed.
86 last2="${ntz:3:2}"; # assumes $ntz is 5 characters (i.e. "±HHMM")
87 #last2="$(rev <<< $ntz)" && last2="${last2:0:2}" && last2="$(rev <<< "$last2")";
88 if [[ "$last2" == "00" ]]; then
89 ## ntz_out is truncated by 2 characters
90 len_ntz="${#ntz}";
91 len_ntz_out="$(( len_ntz - 2 ))";
92 ntz_out=""${ntz:0:$len_ntz_out};
93 else
94 ## ntz_out is ntz with semicolon inserted after HH
95 ntz_out="$(insertStr "$ntz" 3 ":" )";
96 fi;
97
98 # Output via stdout
99 printf "%s" "$ntz_out";
100 }; # Format numeric time zone (for BSD date compatibility)
101 print_dateline() {
102 # Input: var: $id
103 # var: $fs_1
104 # var: $fs_2
105 # var: $fs_3
106 # args: $@
107 # env var: TZ (time zone for date; e.g. 'America/Denver')
108 # Output: stdout
109 # Depends: printf, date
110 # get_tz_offset()
111 # Ref/Attrib: * Truncate string in printf https://stackoverflow.com/a/46812677
112 local s_1 s_2 s_2_tz s_3 s_4;
113
114 s_1="$id";
115 s_2="$(date "$@" "$fs_1")"; # ISO-8601 without numeric timezone
116 s_3="$(date "$@" "$fs_2")"; # Alternate ISO-8601 expressions
117 s_4="$(date "$@" "$fs_3")"; # locale-specific date strings
118
119 # Append numeric timezone to $s_2 with appropriate format
120 # (e.g. '-07' for 'Arizona', '+05:45' for 'Asia/Kathmandu')
121 s_2_tz="$(get_tz_offset)";
122 s_2="$( printf "%s%s" "$s_2" "$s_2_tz" )";
123
124 printf "%-10.10s %-25.25s (%-20.20s) (%s)" "$s_1" "$s_2" "$s_3" "$s_4";
125 printf "\n";
126
127 unset fs_1 fs_2 fs_3 fs_4;
128 }; # print line of dates
129 main() {
130 n_ln=0; # for line_sep()
131 unset LC_TIME; # Fall back to time zone-specific locale settings.
132
133 # format strings
134 fs_iso8601="+%Y-%m-%dT%H:%M:%S"; # typical ISO-8601 without timezone
135 fs_iso8601_etc="+%G-W%V-%u, %Y-%j"; # alternate ISO-8601 dates
136 fs_locale="+%Z; %A; %c"; # locale-specific date strings
137
138 # vars for print_dateline()
139 fs_1="$fs_iso8601";
140 fs_2="$fs_iso8601_etc";
141 fs_3="$fs_locale";
142
143 # UTC (pop. (2021): 7,837,000,000)
144 (
145 export TZ=UTC;
146 id="UTC";
147 fs_3="+%s seconds since 1970-01-01T00:00+00";
148 print_dateline "$@";
149 ); line_sep;
150
151 # Hawaii
152 (
153 export TZ=US/Hawaii;
154 export LANG="haw-US.UTF8";
155 id="HAWAII";
156 print_dateline "$@";
157 ); line_sep;
158
159 # Los Angeles, USA
160 (
161 export TZ=America/Los_Angeles;
162 export LANG="en_US.UTF-8";
163 id="LOS ANGELES";
164 print_dateline "$@";
165 ); line_sep;
166
167 # Denver, USA (pop. (2021): 711,463)
168 (
169 export TZ=America/Denver;
170 export LANG="en_US.UTF-8";
171 id="DENVER";
172 print_dateline "$@";
173 ); line_sep;
174
175 # Chicago, USA (pop. (2021): 711,463)
176 (
177 export TZ=America/Chicago;
178 export LANG="en_US.UTF-8";
179 id="CHICAGO";
180 print_dateline "$@";
181 ); line_sep;
182
183 # Mexico City, Mexico (pop. (2018): 21,804,515)
184 (
185 export TZ=America/Mexico_City;
186 export LANG="es_MX.UTF8";
187 id="MEXICO CITY";
188 print_dateline "$@";
189 ); line_sep;
190
191 # Panama City, Panama
192 (
193 export TZ=America/Panama;
194 export LANG="es_PA.UTF8";
195 id="PANAMA CITY";
196 print_dateline "$@";
197 ); line_sep;
198
199 # New York, USA (pop. (2018): 20,140,470)
200 (
201 export TZ=America/New_York;
202 export LANG="en_US.UTF-8";
203 id="NEW YORK";
204 print_dateline "$@";
205 ); line_sep;
206
207 # São Paulo, Brazil
208 (
209 export TZ=America/Sao_Paulo;
210 export LANG="pt_BR.UTF8";
211 id="SAO PAULO";
212 print_dateline "$@";
213 ); line_sep;
214
215 # Buenos Aires
216 (
217 export TZ=America/Argentina/Buenos_Aires;
218 export LANG="es_AR.UTF8";
219 id="BUENOS AIRES";
220 print_dateline "$@";
221 ); line_sep;
222
223 # London, England
224 (
225 export TZ=Europe/London;
226 export LANG="en_GB.UTF-8";
227 id="LONDON";
228 print_dateline "$@";
229 ); line_sep;
230
231 # Kinshasa, Africa
232 (
233 export TZ=Africa/Kinshasa;
234 export LANG="ln_CD.UTF8";
235 id="KINSHASA";
236 print_dateline "$@";
237 ); line_sep;
238
239 # Lagos, Africa
240 (
241 export TZ=Africa/Lagos;
242 export LANG="en_NG.UTF8";
243 id="LAGOS";
244 print_dateline "$@";
245 ); line_sep;
246
247 # Paris, France
248 (
249 export TZ=Europe/Paris;
250 export LANG="fr_FR.UTF8";
251 id="PARIS";
252 print_dateline "$@";
253 ); line_sep;
254
255 # Stockholm, Sweden
256 (
257 export TZ=Europe/Stockholm;
258 export LANG="sv_SE.UTF8";
259 id="STOCKHOLM";
260 print_dateline "$@";
261 ); line_sep;
262
263 # Cairo, Egypt
264 (
265 export TZ=Africa/Cairo;
266 export LANG="ar_EG.UTF8";
267 id="CAIRO";
268 print_dateline "$@";
269 ); line_sep;
270
271 # Athens (pop. (2020): 3,526,887)
272 (
273 export TZ=Europe/Athens;
274 export LANG="el_GR.UTF8";
275 id="ATHENS";
276 print_dateline "$@";
277 ); line_sep;
278
279 # Istanbul (pop. (2020): 13,719,061)
280 (
281 export TZ=Asia/Istanbul;
282 export LANG="tr_TR.UTF8";
283 id="ISTANBUL";
284 print_dateline "$@";
285 ); line_sep;
286
287 # Tehran, Iran
288 (
289 export TZ=Asia/Tehran;
290 export LANG="fa_IR.UTF8";
291 id="TEHRAN";
292 print_dateline "$@";
293 ); line_sep;
294
295 # Moscow, Russia
296 (
297 export TZ=Europe/Moscow;
298 export LANG="ru_RU.UTF-8";
299 id="MOSCOW";
300 print_dateline "$@";
301 ); line_sep;
302
303 # Kyiv, Ukraine (pop. (2021): 2,962,180)
304 (
305 export TZ=Europe/Kyiv;
306 export LANG="uk_UA.UTF-8";
307 id="KYIV";
308 print_dateline "$@";
309 ); line_sep;
310
311 # Delhi, India (pop. (2018): 29,000,000)
312 (
313 export TZ=Asia/Kolkata;
314 export LANG="hi_IN.UTF-8";
315 id="DELHI";
316 print_dateline "$@";
317 ); line_sep;
318
319 # Jakarta, Indonesia (pop. (2018): 33,430,285)
320 (
321 export TZ=Asia/Jakarta;
322 export LANG="id_ID.UTF8";
323 id="JAKARTA";
324 print_dateline "$@";
325 ); line_sep;
326
327 # Singapore, Singapore (pop (2018): 5,792,000)
328 (
329 export TZ=Asia/Singapore;
330 export LANG="en_SG.UTF-8";
331 id="SINGAPORE";
332 print_dateline "$@";
333 ); line_sep;
334
335 # Beijing, China (pop. (2018): 19,618,000)
336 (
337 export TZ=Asia/Shanghai; # [3]
338 export LANG="zh_CN.UTF-8";
339 id="BEIJING";
340 print_dateline "$@";
341 ); line_sep;
342
343 # Taipei, Taiwan (pop (2019): 7,034,084)
344 (
345 export TZ=Asia/Taipei; # [3]
346 export LANG="zh_TW.UTF-8";
347 id="TAIPEI";
348 print_dateline "$@";
349 ); line_sep;
350
351 # Tokyo, Japan (pop. (2018): 37,274,000)
352 (
353 export TZ=Asia/Tokyo;
354 export LANG="ja_JP.UTF8";
355 id="TOKYO";
356 print_dateline "$@";
357 ); line_sep;
358
359 # Seoul, South Korea (pop. (2018): 25,514,000)
360 (
361 export TZ=Asia/Seoul;
362 export LANG="ko_KR.UTF8";
363 id="SEOUL";
364 print_dateline "$@";
365 ); line_sep;
366
367 # Pyongyang, North Korea
368 (
369 export TZ=Asia/Pyongyang;
370 export LANG="ko_KP.UTF8";
371 id="PYONGYANG";
372 print_dateline "$@";
373 ); line_sep;
374
375 # Sydney, Australia
376 (
377 export TZ=Australia/Sydney;
378 export LANG="en_AU.UTF8";
379 id="SYDNEY";
380 print_dateline "$@";
381 ); line_sep;
382
383 # Guam
384 (
385 export TZ=Pacific/Guam;
386 export LANG="en_GU.UTF8";
387 id="GUAM";
388 print_dateline "$@";
389 ); line_sep;
390
391 # Auckland, New Zealand
392 (
393 export TZ=Pacific/Auckland;
394 export LANG="en_NZ.UTF8";
395 id="AUCKLAND";
396 print_dateline "$@";
397 ); line_sep;
398
399 return 0;
400 }; # main program
401
402 main "$@";
403
404 # Author: Steven Baltakatei Sandoval
405 # License: GPLv3+