# Desc: Baltakatei's verbose date command
# Usage: bkdatev [args]
# Example: bkdatev --date="2001-09-11T09:02:59-04"
-# Version: 0.2.2
+# Version: 0.3.1
# 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
# * 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`.
+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;
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')
+ # 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: $@
+ # 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_3 s_4;
+ local s_1 s_2 s_2_tz s_3 s_4;
s_1="$id";
- s_2="$(date "$@" "$fs_1")";
- s_3="$(date "$@" "$fs_2")";
- s_4="$(date "$@" "$fs_3")";
+ 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 LC_TIME; # Fall back to time zone-specific locale settings.
# format strings
- fs_iso8601="+%Y-%m-%dT%H:%M:%S%:::z"; # typical ISO-8601
+ 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
}; # main program
main "$@";
+
+# Author: Steven Baltakatei Sandoval
+# License: GPLv3+