#!/usr/bin/env bash # Desc: Baltakatei's verbose date command # Usage: bkdatev [args] # Example: bkdatev --date="2001-09-11T09:02:59-04" # Version: 1.0.0 # Depends: GNU Coreutils 8.32, Bash 3.2.57 # Ref/Attrib: [1] "ISO 8601". Wikipedia. https://en.wikipedia.org/wiki/ISO_8601 # [2] "Changing the Locale in Wine" https://stackoverflow.com/a/16428951 # [3] "Shanghai vs Beijing" https://bugs.launchpad.net/ubuntu/+source/libgweather/+bug/228554 # Notes: * Check `ls -R /usr/share/zoneinfo` for time zone names. # * Check `cat /usr/share/i18n/SUPPORTED` for supported locales. # * For list of valid locales, see: https://manpages.ubuntu.com/manpages/bionic/man3/DateTime::Locale::Catalog.3pm.html # * Locations chosen for population, personal signifiance, and spatial coverage. # * For International Atomic Time (TAI), use offsets from UTC provided in `/usr/share/zoneinfo/leap-seconds.list`. # * Compatibility with macOS may be limited if any arguments # are provided when running `bkdatev`; e.g. passing a # `--date` option to `bkdatev` will fail. yell() { echo "$0: $*" >&2; } die() { yell "$*"; exit 111; } must() { "$@" || die "cannot $*"; } insertStr() { # Desc: Inserts a string into another string at a specified position. # Input: arg1: str str_rec String to receive insertion # arg2: int pos Insertion position (0 = append to front) # arg3: str str_ins String to be inserted # Output: stdout: Combined string # Version: 0.0.2 # Depends: BK-2020-03: yell(), die(), must() # Ref/Attrib: * BK-2020-03: https://gitlab.com/baltakatei/baltakatei-exdev/ # * Cooper, Mendel. “Advanced Bash-Scripting Guide: Manipulating Strings”. tldp.org https://tldp.org/LDP/abs/html/string-manipulation.html local str_rec pos str_ins re len_str_rec; local pfx_pos_start pfx_len pfx; local sfx_pos_start sfx_len sfx; # Check args if [[ $# -ne 3 ]]; then yell "ERROR:Invalid argument count:$#"; return 1; fi; re='^[0-9]+$'; if [[ ! "$2" =~ $re ]]; then yell "ERROR:Not an int:$2"; return 1; fi; str_rec="$1"; pos="$2"; str_ins="$3"; # Calculate string stats len_str_rec="${#str_rec}"; # Form prefix pfx_pos_start="0"; pfx_len="$pos"; pfx="${str_rec:$pfx_pos_start:$pfx_len}"; # Form suffix sfx_pos_start="$(( pos ))"; sfx_len="$(( len_str_rec - pos ))"; sfx="${str_rec:$sfx_pos_start:$sfx_len}"; # Print output to stdout printf "%s%s%s\n" "$pfx" "$str_ins" "$sfx"; }; # Insert string provided at indicated position via stdout line_sep() { # Input: var: n_ln local skip_every=4; ((n_ln++)); if ! ((n_ln % "$skip_every")); then printf "\n"; fi; return 0; }; # periodically print separating blank line get_tz_offset() { # Desc: Get from 'date' the timezone UTC offset in a way # compatible with both GNU Coreutils and BSD versions. # Input: env var: TZ (time zone for date; e.g. 'America/Denver') # args: $@ # passed onto `date` # Depends: date (GNU Coreutils 8.32 or BSD), rev local ntz ntz ntz_out; local last2; # Get numeric time zone string in way compatible with GNU Coreutils and BSD ntz="$(date "+%z" "$@")"; # e.g. "+0530" # Check if last two characters are trailing zeros that can be removed. last2="${ntz:3:2}"; # assumes $ntz is 5 characters (i.e. "±HHMM") #last2="$(rev <<< $ntz)" && last2="${last2:0:2}" && last2="$(rev <<< "$last2")"; if [[ "$last2" == "00" ]]; then ## ntz_out is truncated by 2 characters len_ntz="${#ntz}"; len_ntz_out="$(( len_ntz - 2 ))"; ntz_out=""${ntz:0:$len_ntz_out}; else ## ntz_out is ntz with semicolon inserted after HH ntz_out="$(insertStr "$ntz" 3 ":" )"; fi; # Output via stdout printf "%s" "$ntz_out"; }; # Format numeric time zone (for BSD date compatibility) print_dateline() { # Input: var: $id # var: $fs_1 # var: $fs_2 # var: $fs_3 # args: $@ # passed on to `date` # env var: TZ (time zone for date; e.g. 'America/Denver') # Output: stdout # Depends: printf, date # get_tz_offset() # Ref/Attrib: * Truncate string in printf https://stackoverflow.com/a/46812677 local s_1 s_2 s_2_tz s_3 s_4; s_1="$id"; s_2="$(date "$@" "$fs_1")"; # ISO-8601 without numeric timezone s_3="$(date "$@" "$fs_2")"; # Alternate ISO-8601 expressions s_4="$(date "$@" "$fs_3")"; # locale-specific date strings # Append numeric timezone to $s_2 with appropriate format # (e.g. '-07' for 'Arizona', '+05:45' for 'Asia/Kathmandu') s_2_tz="$(get_tz_offset "$@")"; s_2="$( printf "%s%s" "$s_2" "$s_2_tz" )"; printf "%-10.10s %-25.25s (%-20.20s) (%s)" "$s_1" "$s_2" "$s_3" "$s_4"; printf "\n"; unset fs_1 fs_2 fs_3 fs_4; }; # print line of dates main() { n_ln=0; # for line_sep() unset LC_TIME; # Fall back to time zone-specific locale settings. # format strings fs_iso8601="+%Y-%m-%dT%H:%M:%S"; # typical ISO-8601 without timezone fs_iso8601_etc="+%G-W%V-%u, %Y-%j"; # alternate ISO-8601 dates fs_locale="+%Z; %A; %c"; # locale-specific date strings # vars for print_dateline() fs_1="$fs_iso8601"; fs_2="$fs_iso8601_etc"; fs_3="$fs_locale"; # UTC (pop. (2021): 7,837,000,000) ( export TZ=UTC; id="UTC"; fs_3="+%s seconds since 1970-01-01T00:00+00"; print_dateline "$@"; ); line_sep; # Hawaii ( export TZ=US/Hawaii; export LANG="haw-US.UTF8"; id="HAWAII"; print_dateline "$@"; ); line_sep; # Los Angeles, USA ( export TZ=America/Los_Angeles; export LANG="en_US.UTF-8"; id="LOS ANGELES"; print_dateline "$@"; ); line_sep; # Denver, USA (pop. (2021): 711,463) ( export TZ=America/Denver; export LANG="en_US.UTF-8"; id="DENVER"; print_dateline "$@"; ); line_sep; # Chicago, USA (pop. (2021): 711,463) ( export TZ=America/Chicago; export LANG="en_US.UTF-8"; id="CHICAGO"; print_dateline "$@"; ); line_sep; # Mexico City, Mexico (pop. (2018): 21,804,515) ( export TZ=America/Mexico_City; export LANG="es_MX.UTF8"; id="MEXICO CITY"; print_dateline "$@"; ); line_sep; # Panama City, Panama ( export TZ=America/Panama; export LANG="es_PA.UTF8"; id="PANAMA CITY"; print_dateline "$@"; ); line_sep; # New York, USA (pop. (2018): 20,140,470) ( export TZ=America/New_York; export LANG="en_US.UTF-8"; id="NEW YORK"; print_dateline "$@"; ); line_sep; # São Paulo, Brazil ( export TZ=America/Sao_Paulo; export LANG="pt_BR.UTF8"; id="SAO PAULO"; print_dateline "$@"; ); line_sep; # Buenos Aires ( export TZ=America/Argentina/Buenos_Aires; export LANG="es_AR.UTF8"; id="BUENOS AIRES"; print_dateline "$@"; ); line_sep; # London, England ( export TZ=Europe/London; export LANG="en_GB.UTF-8"; id="LONDON"; print_dateline "$@"; ); line_sep; # Kinshasa, Africa ( export TZ=Africa/Kinshasa; export LANG="ln_CD.UTF8"; id="KINSHASA"; print_dateline "$@"; ); line_sep; # Lagos, Africa ( export TZ=Africa/Lagos; export LANG="en_NG.UTF8"; id="LAGOS"; print_dateline "$@"; ); line_sep; # Paris, France ( export TZ=Europe/Paris; export LANG="fr_FR.UTF8"; id="PARIS"; print_dateline "$@"; ); line_sep; # Stockholm, Sweden ( export TZ=Europe/Stockholm; export LANG="sv_SE.UTF8"; id="STOCKHOLM"; print_dateline "$@"; ); line_sep; # Helsinki, Finland ( export TZ=Europe/Helsinki; export LANG="fi_FI.UTF8"; id="HELSINKI"; print_dateline "$@"; ); line_sep; # Cairo, Egypt ( export TZ=Africa/Cairo; export LANG="ar_EG.UTF8"; id="CAIRO"; print_dateline "$@"; ); line_sep; # Gaza City, Palestine (pop. (2017): 590,481) ( export TZ=Asia/Gaza; export LANG="ar_JO.UTF-8"; # ar_PS is missing as of 2023-10-18, using ar_JO as closest substitute. id="GAZA"; print_dateline "$@"; ); line_sep; # Tel Aviv, Israel (pop. (2021): 467,875) ( export TZ=Asia/Tel_Aviv; export LANG="he_IL.UTF-8"; id="TEL AVIV"; print_dateline "$@"; ); line_sep; # Athens (pop. (2020): 3,526,887) ( export TZ=Europe/Athens; export LANG="el_GR.UTF8"; id="ATHENS"; print_dateline "$@"; ); line_sep; # Istanbul (pop. (2020): 13,719,061) ( export TZ=Asia/Istanbul; export LANG="tr_TR.UTF8"; id="ISTANBUL"; print_dateline "$@"; ); line_sep; # Tehran, Iran ( export TZ=Asia/Tehran; export LANG="fa_IR.UTF8"; id="TEHRAN"; print_dateline "$@"; ); line_sep; # Moscow, Russia ( export TZ=Europe/Moscow; export LANG="ru_RU.UTF-8"; id="MOSCOW"; print_dateline "$@"; ); line_sep; # Kyiv, Ukraine (pop. (2021): 2,962,180) ( export TZ=Europe/Kyiv; export LANG="uk_UA.UTF-8"; id="KYIV"; print_dateline "$@"; ); line_sep; # Delhi, India (pop. (2018): 29,000,000) ( export TZ=Asia/Kolkata; export LANG="hi_IN.UTF-8"; id="DELHI"; print_dateline "$@"; ); line_sep; # Jakarta, Indonesia (pop. (2018): 33,430,285) ( export TZ=Asia/Jakarta; export LANG="id_ID.UTF8"; id="JAKARTA"; print_dateline "$@"; ); line_sep; # Singapore, Singapore (pop (2018): 5,792,000) ( export TZ=Asia/Singapore; export LANG="en_SG.UTF-8"; id="SINGAPORE"; print_dateline "$@"; ); line_sep; # Beijing, China (pop. (2018): 19,618,000) ( export TZ=Asia/Shanghai; # [3] export LANG="zh_CN.UTF-8"; id="BEIJING"; print_dateline "$@"; ); line_sep; # Taipei, Taiwan (pop (2019): 7,034,084) ( export TZ=Asia/Taipei; # [3] export LANG="zh_TW.UTF-8"; id="TAIPEI"; print_dateline "$@"; ); line_sep; # Tokyo, Japan (pop. (2018): 37,274,000) ( export TZ=Asia/Tokyo; export LANG="ja_JP.UTF8"; id="TOKYO"; print_dateline "$@"; ); line_sep; # Seoul, South Korea (pop. (2018): 25,514,000) ( export TZ=Asia/Seoul; export LANG="ko_KR.UTF8"; id="SEOUL"; print_dateline "$@"; ); line_sep; # Pyongyang, North Korea ( export TZ=Asia/Pyongyang; export LANG="ko_KP.UTF8"; id="PYONGYANG"; print_dateline "$@"; ); line_sep; # Sydney, Australia ( export TZ=Australia/Sydney; export LANG="en_AU.UTF8"; id="SYDNEY"; print_dateline "$@"; ); line_sep; # Guam ( export TZ=Pacific/Guam; export LANG="en_GU.UTF8"; id="GUAM"; print_dateline "$@"; ); line_sep; # Auckland, New Zealand ( export TZ=Pacific/Auckland; export LANG="en_NZ.UTF8"; id="AUCKLAND"; print_dateline "$@"; ); line_sep; return 0; }; # main program main "$@"; # Author: Steven Baltakatei Sandoval # License: GPLv3+