+ # Determine function return code
+ if [ "$returnState" = "true" ]; then
+ return 0;
+ fi
+} # Exports TZ environment variable
+timeUntilNextDay(){
+ # Desc: Report seconds until next day.
+ # Version: 1.0.0
+ # Output: stdout: integer seconds until next day
+ # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
+ # Usage: timeUntilNextDay
+ # Usage: if ! myTTL="$(timeUntilNextDay)"; then yell "ERROR in if statement"; exit 1; fi
+ # Depends: date 8, echo 8, yell, try
+
+ local returnState TIME_CURRENT TIME_NEXT_DAY SECONDS_UNTIL_NEXT_DAY
+
+ TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
+ TIME_NEXT_DAY="$(date -d "$TIME_CURRENT next day" --iso-8601=date)"; # Produce timestamp of beginning of tomorrow with resolution of 1 second.
+ SECONDS_UNTIL_NEXT_DAY="$(( $(date +%s -d "$TIME_NEXT_DAY") - $(date +%s -d "$TIME_CURRENT") ))" ; # Calculate seconds until closest future midnight (res. 1 second).
+ if [[ "$SECONDS_UNTIL_NEXT_DAY" -gt 0 ]]; then
+ returnState="true";
+ elif [[ "$SECONDS_UNTIL_NEXT_DAY" -eq 0 ]]; then
+ returnState="warning_zero";
+ yell "WARNING:Reported time until next day exactly zero.";
+ elif [[ "$SECONDS_UNTIL_NEXT_DAY" -lt 0 ]]; then
+ returnState="warning_negative";
+ yell "WARNING:Reported time until next day is negative.";
+ fi
+
+ try echo "$SECONDS_UNTIL_NEXT_DAY"; # Report
+
+ # Determine function return code
+ if [[ "$returnState" = "true" ]]; then
+ return 0;
+ elif [[ "$returnState" = "warning_zero" ]]; then
+ return 1;
+ elif [[ "$returnState" = "warning_negative" ]]; then
+ return 2;
+ fi
+} # Report seconds until next day
+timeUntilNextHour(){
+ # Desc: Report seconds until next hour
+ # Version 1.0.0
+ # Output: stdout: integer seconds until next hour
+ # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
+ # Usage: timeUntilNextHour
+ # Usage: if ! myTTL="$(timeUntilNextHour)"; then yell "ERROR in if statement"; exit 1; fi
+
+ local returnState TIME_CURRENT TIME_NEXT_HOUR SECONDS_UNTIL_NEXT_HOUR
+ TIME_CURRENT="$(date --iso-8601=seconds)"; # Produce `date`-parsable current timestamp with resolution of 1 second.
+ TIME_NEXT_HOUR="$(date -d "$TIME_CURRENT next hour" --iso-8601=hours)"; # Produce `date`-parsable current time stamp with resolution of 1 second.
+ SECONDS_UNTIL_NEXT_HOUR="$(( $(date +%s -d "$TIME_NEXT_HOUR") - $(date +%s -d "$TIME_CURRENT") ))"; # Calculate seconds until next hour (res. 1 second).
+ if [[ "$SECONDS_UNTIL_NEXT_HOUR" -gt 0 ]]; then
+ returnState="true";
+ elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -eq 0 ]]; then
+ returnState="warning_zero";
+ yell "WARNING:Reported time until next hour exactly zero.";
+ elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -lt 0 ]]; then
+ returnState="warning_negative";
+ yell "WARNING:Reported time until next hour is negative.";
+ fi
+
+ try echo "$SECONDS_UNTIL_NEXT_HOUR"; # Report
+
+ # Determine function return code
+ if [[ "$returnState" = "true" ]]; then
+ return 0;
+ elif [[ "$returnState" = "warning_zero" ]]; then
+ return 1;
+ elif [[ "$returnState" = "warning_negative" ]]; then
+ return 2;
+ fi
+} # Report seconds until next hour
+dateTimeShort(){
+ # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
+ # Usage: dateTimeShort ([str date])
+ # Version 1.1.0
+ # Input: arg1: 'date'-parsable timestamp string (optional)
+ # Output: stdout: timestamp (ISO-8601, no separators)
+ # Depends: yell
+ local TIME_CURRENT TIME_CURRENT_SHORT
+
+ argTime="$1";
+ # Get Current Time
+ TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
+ # Decide to parse current or supplied date
+ ## Check if time argument empty
+ if [[ -z "$argTime" ]]; then
+ ## T: Time argument empty, use current time
+ TIME_INPUT="$TIME_CURRENT";
+ else
+ ## F: Time argument exists, validate time
+ if date --date="$argTime" 1>/dev/null 2>&1; then
+ ### T: Time argument is valid; use it
+ TIME_INPUT="$argTime";
+ else
+ ### F: Time argument not valid; exit
+ yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
+ fi
+ fi
+ # Construct and deliver separator-les date string
+ TIME_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%dT%H%M%S%z)";
+ echo "$TIME_CURRENT_SHORT";
+} # Get YYYYmmddTHHMMSS±zzzz
+dateShort(){
+ # Desc: Date without separators (YYYYmmdd)
+ # Usage: dateShort ([str date])
+ # Version: 1.1.0
+ # Input: arg1: 'date'-parsable timestamp string (optional)
+ # Output: stdout: date (ISO-8601, no separators)
+ # Depends: yell
+ local TIME_CURRENT DATE_CURRENT_SHORT
+
+ argTime="$1";
+ # Get Current Time
+ TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
+ # Decide to parse current or supplied date
+ ## Check if time argument empty
+ if [[ -z "$argTime" ]]; then
+ ## T: Time argument empty, use current time
+ TIME_INPUT="$TIME_CURRENT";
+ else
+ ## F: Time argument exists, validate time
+ if date --date="$argTime" 1>/dev/null 2>&1; then
+ ### T: Time argument is valid; use it
+ TIME_INPUT="$argTime";
+ else
+ ### F: Time argument not valid; exit
+ yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
+ fi
+ fi
+ # Construct and deliver separator-les date string
+ DATE_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
+ echo "$DATE_CURRENT_SHORT";
+} # Get YYYYmmdd
+timeDuration(){
+ # Desc: Given seconds, output ISO-8601 duration string
+ # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
+ # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
+ # Usage: timeDuration [1:seconds] ([2:precision])
+ # Version: 1.0.3
+ # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
+ # arg2: precision level (optional; default=2)
+ # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
+ # exit code 0: success
+ # exit code 1: error_input
+ # exit code 2: error_unknown
+ # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
+ # Depends: date 8 (gnucoreutils), yell,
+ local returnState argSeconds argPrecision remainder precision witherPrecision
+ local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
+ local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
+ local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
+
+ argSeconds="$1"; # read arg1 (seconds)
+ argPrecision="$2"; # read arg2 (precision)
+ precision=2; # set default precision
+
+ # Check that between one and two arguments is supplied
+ if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
+ yell "ERROR:Invalid number of arguments:$# . Exiting.";
+ returnState="error_input"; fi
+
+ # Check that argSeconds provided
+ if [[ $# -ge 1 ]]; then
+ ## Check that argSeconds is a positive integer
+ if [[ "$argSeconds" =~ ^[[:digit:]]+$ ]]; then
+ :
+ else
+ yell "ERROR:argSeconds not a digit.";
+ returnState="error_input";
+ fi
+ else
+ yell "ERROR:No argument provided. Exiting.";
+ exit 1;
+ fi
+
+ # Consider whether argPrecision was provided
+ if [[ $# -eq 2 ]]; then
+ # Check that argPrecision is a positive integer
+ if [[ "$argPrecision" =~ ^[[:digit:]]+$ ]] && [[ "$argPrecision" -gt 0 ]]; then
+ precision="$argPrecision";
+ else
+ yell "ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
+ returnState="error_input";
+ fi;
+ else
+ :
+ fi;
+
+ remainder="$argSeconds" ; # seconds
+ ## Calculate full years Y, update remainder
+ fullYears=$(( remainder / (365*24*60*60) ));
+ remainder=$(( remainder - (fullYears*365*24*60*60) ));
+ ## Calculate full months M, update remainder
+ fullMonths=$(( remainder / (30*24*60*60) ));
+ remainder=$(( remainder - (fullMonths*30*24*60*60) ));
+ ## Calculate full days D, update remainder
+ fullDays=$(( remainder / (24*60*60) ));
+ remainder=$(( remainder - (fullDays*24*60*60) ));
+ ## Calculate full hours H, update remainder
+ fullHours=$(( remainder / (60*60) ));
+ remainder=$(( remainder - (fullHours*60*60) ));
+ ## Calculate full minutes M, update remainder
+ fullMinutes=$(( remainder / (60) ));
+ remainder=$(( remainder - (fullMinutes*60) ));
+ ## Calculate full seconds S, update remainder
+ fullSeconds=$(( remainder / (1) ));
+ remainder=$(( remainder - (remainder*1) ));
+ ## Check which fields filled
+ if [[ $fullYears -gt 0 ]]; then hasYears="true"; else hasYears="false"; fi
+ if [[ $fullMonths -gt 0 ]]; then hasMonths="true"; else hasMonths="false"; fi
+ if [[ $fullDays -gt 0 ]]; then hasDays="true"; else hasDays="false"; fi
+ if [[ $fullHours -gt 0 ]]; then hasHours="true"; else hasHours="false"; fi
+ if [[ $fullMinutes -gt 0 ]]; then hasMinutes="true"; else hasMinutes="false"; fi
+ if [[ $fullSeconds -gt 0 ]]; then hasSeconds="true"; else hasSeconds="false"; fi
+
+ ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
+ witherPrecision="false"
+
+ ### Years
+ if $hasYears && [[ $precision -gt 0 ]]; then
+ displayYears="true";
+ witherPrecision="true";
+ else
+ displayYears="false";
+ fi;
+ if $witherPrecision; then ((precision--)); fi;
+
+ ### Months
+ if $hasMonths && [[ $precision -gt 0 ]]; then
+ displayMonths="true";
+ witherPrecision="true";
+ else
+ displayMonths="false";
+ fi;
+ if $witherPrecision && [[ $precision -gt 0 ]]; then
+ displayMonths="true";
+ fi;
+ if $witherPrecision; then ((precision--)); fi;
+
+ ### Days
+ if $hasDays && [[ $precision -gt 0 ]]; then
+ displayDays="true";
+ witherPrecision="true";
+ else
+ displayDays="false";
+ fi;
+ if $witherPrecision && [[ $precision -gt 0 ]]; then
+ displayDays="true";
+ fi;
+ if $witherPrecision; then ((precision--)); fi;
+
+ ### Hours
+ if $hasHours && [[ $precision -gt 0 ]]; then
+ displayHours="true";
+ witherPrecision="true";
+ else
+ displayHours="false";
+ fi;
+ if $witherPrecision && [[ $precision -gt 0 ]]; then
+ displayHours="true";
+ fi;
+ if $witherPrecision; then ((precision--)); fi;