#!/bin/bash # Desc: Template to indicate time duration in ISO-8601 format yell() { echo "$0: $*" >&2; } # Yell, Die, Try Three-Fingered Claw technique; # Ref/Attrib: https://stackoverflow.com/a/25515370 die() { yell "$*"; exit 111; } try() { "$@" || die "cannot $*"; } 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.5 # 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, bash 5, yell, local argSeconds argPrecision precision returnState remainder local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds local witherPrecision output local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds 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 -ge 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; ### Minutes if $hasMinutes && [[ $precision -gt 0 ]]; then displayMinutes="true"; witherPrecision="true"; else displayMinutes="false"; fi; if $witherPrecision && [[ $precision -gt 0 ]]; then displayMinutes="true"; fi; if $witherPrecision; then ((precision--)); fi; ### Seconds if $hasSeconds && [[ $precision -gt 0 ]]; then displaySeconds="true"; witherPrecision="true"; else displaySeconds="false"; fi; if $witherPrecision && [[ $precision -gt 0 ]]; then displaySeconds="true"; fi; if $witherPrecision; then ((precision--)); fi; ## Determine whether or not the "T" separator is needed to separate date and time elements if ( $displayHours || $displayMinutes || $displaySeconds); then displayDateTime="true"; else displayDateTime="false"; fi ## Construct duration output string output="P" if $displayYears; then output=$output$fullYears"Y"; fi if $displayMonths; then output=$output$fullMonths"M"; fi if $displayDays; then output=$output$fullDays"D"; fi if $displayDateTime; then output=$output"T"; fi if $displayHours; then output=$output$fullHours"H"; fi if $displayMinutes; then output=$output$fullMinutes"M"; fi if $displaySeconds; then output=$output$fullSeconds"S"; fi ## Output duration string to stdout echo "$output" && returnState="true"; #===Determine function return code=== if [ "$returnState" = "true" ]; then return 0; elif [ "$returnState" = "error_input" ]; then yell "ERROR:input"; return 1; else yell "ERROR:Unknown"; return 2; fi } # Get duration (ex: PT10M4S ) #==BEGIN sample code== echo "Precision 6 duration:$(timeDuration "$(date +%s)" 6)" echo "Precision 5 duration:$(timeDuration "$(date +%s)" 5)" echo "Precision 4 duration:$(timeDuration "$(date +%s)" 4)" echo "Precision 3 duration:$(timeDuration "$(date +%s)" 3)" echo "Precision 2 duration:$(timeDuration "$(date +%s)" 2)" echo "Precision 1 duration:$(timeDuration "$(date +%s)" 1)" echo "Precision 6 duration:$(timeDuration $((60+60*60+60*60*24+60*60*24*30+60*60*24*365 - (60+60*60+60*60*24+60*60*24*30) )) 6)" echo "Zero seconds:$(timeDuration "0")"; echo "Parsing argument provided to script:\"$*\""; timeDuration "$@" #==END sample code== # Author: Steven Baltakatei Sandoval # License: GPLv3+