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