3 # Desc: Template to indicate time duration in ISO-8601 format 
   5 yell
() { echo "$0: $*" >&2; } # Yell, Die, Try Three-Fingered Claw technique; # Ref/Attrib: https://stackoverflow.com/a/25515370 
   6 die
() { yell 
"$*"; exit 111; } 
   7 try
() { "$@" || die 
"cannot $*"; } 
   9     # Desc: Given seconds, output ISO-8601 duration string 
  10     # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information 
  11     # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2) 
  12     # Usage: timeDuration [1:seconds] ([2:precision]) 
  14     # Input: arg1: seconds as base 10 integer >= 0  (ex: 3601) 
  15     #        arg2: precision level (optional; default=2) 
  16     # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S") 
  17     #         exit code 0: success 
  18     #         exit code 1: error_input 
  19     #         exit code 2: error_unknown 
  20     # Example: 'timeDuration 111111 3' yields 'P1DT6H51M' 
  21     # Depends: date 8 (gnucoreutils), yell,  
  22     local returnState argSeconds argPrecision remainder precision witherPrecision
 
  23     local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
 
  24     local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
 
  25     local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
 
  27     argSeconds
="$1"; # read arg1 (seconds) 
  28     argPrecision
="$2"; # read arg2 (precision) 
  29     precision
=2; # set default precision 
  31     # Check that between one and two arguments is supplied 
  32     if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then 
  33         yell 
"ERROR:Invalid number of arguments:$# . Exiting."; 
  34         returnState
="error_input"; fi 
  36     # Check that argSeconds provided 
  37     if [[ $# -ge 1 ]]; then 
  38         ## Check that argSeconds is a positive integer 
  39         if [[ "$argSeconds" =~ ^
[[:digit
:]]+$ 
]]; then 
  42             yell 
"ERROR:argSeconds not a digit."; 
  43             returnState
="error_input"; 
  46         yell 
"ERROR:No argument provided. Exiting."; 
  50     # Consider whether argPrecision was provided 
  51     if  [[ $# -eq 2 ]]; then 
  52         # Check that argPrecision is a positive integer 
  53         if [[ "$argPrecision" =~ ^
[[:digit
:]]+$ 
]] && [[ "$argPrecision" -gt 0 ]]; then 
  54         precision
="$argPrecision"; 
  56             yell 
"ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early."; 
  57             returnState
="error_input"; 
  63     remainder
="$argSeconds" ; # seconds 
  64     ## Calculate full years Y, update remainder 
  65     fullYears
=$
(( remainder 
/ (365*24*60*60) )); 
  66     remainder
=$
(( remainder 
- (fullYears
*365*24*60*60) )); 
  67     ## Calculate full months M, update remainder 
  68     fullMonths
=$
(( remainder 
/ (30*24*60*60) )); 
  69     remainder
=$
(( remainder 
- (fullMonths
*30*24*60*60) )); 
  70     ## Calculate full days D, update remainder 
  71     fullDays
=$
(( remainder 
/ (24*60*60) )); 
  72     remainder
=$
(( remainder 
- (fullDays
*24*60*60) )); 
  73     ## Calculate full hours H, update remainder 
  74     fullHours
=$
(( remainder 
/ (60*60) )); 
  75     remainder
=$
(( remainder 
- (fullHours
*60*60) )); 
  76     ## Calculate full minutes M, update remainder 
  77     fullMinutes
=$
(( remainder 
/ (60) )); 
  78     remainder
=$
(( remainder 
- (fullMinutes
*60) )); 
  79     ## Calculate full seconds S, update remainder 
  80     fullSeconds
=$
(( remainder 
/ (1) )); 
  81     remainder
=$
(( remainder 
- (remainder
*1) )); 
  82     ## Check which fields filled 
  83     if [[ $fullYears -gt 0 ]]; then hasYears
="true"; else hasYears
="false"; fi 
  84     if [[ $fullMonths -gt 0 ]]; then hasMonths
="true"; else hasMonths
="false"; fi 
  85     if [[ $fullDays -gt 0 ]]; then hasDays
="true"; else hasDays
="false"; fi 
  86     if [[ $fullHours -gt 0 ]]; then hasHours
="true"; else hasHours
="false"; fi 
  87     if [[ $fullMinutes -gt 0 ]]; then hasMinutes
="true"; else hasMinutes
="false"; fi 
  88     if [[ $fullSeconds -gt 0 ]]; then hasSeconds
="true"; else hasSeconds
="false"; fi 
  90     ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2) 
  91     witherPrecision
="false" 
  94     if $hasYears && [[ $precision -gt 0 ]]; then 
  96         witherPrecision
="true"; 
 100     if $witherPrecision; then ((precision--
)); fi; 
 103     if $hasMonths && [[ $precision -gt 0 ]]; then 
 104         displayMonths
="true"; 
 105         witherPrecision
="true"; 
 107         displayMonths
="false"; 
 109     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 110         displayMonths
="true"; 
 112     if $witherPrecision; then ((precision--
)); fi; 
 115     if $hasDays && [[ $precision -gt 0 ]]; then 
 117         witherPrecision
="true"; 
 121     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 124     if $witherPrecision; then ((precision--
)); fi; 
 127     if $hasHours && [[ $precision -gt 0 ]]; then 
 129         witherPrecision
="true"; 
 131         displayHours
="false"; 
 133     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 136     if $witherPrecision; then ((precision--
)); fi; 
 139     if $hasMinutes && [[ $precision -gt 0 ]]; then 
 140         displayMinutes
="true"; 
 141         witherPrecision
="true"; 
 143         displayMinutes
="false"; 
 145     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 146         displayMinutes
="true"; 
 148     if $witherPrecision; then ((precision--
)); fi; 
 152     if $hasSeconds && [[ $precision -gt 0 ]]; then 
 153         displaySeconds
="true"; 
 154         witherPrecision
="true"; 
 156         displaySeconds
="false"; 
 158     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 159         displaySeconds
="true"; 
 161     if $witherPrecision; then ((precision--
)); fi; 
 163     ## Determine whether or not the "T" separator is needed to separate date and time elements 
 164     if ( $displayHours || 
$displayMinutes || 
$displaySeconds); then 
 165         displayDateTime
="true"; else displayDateTime
="false"; fi 
 167     ## Construct duration output string 
 169     if $displayYears; then 
 170         OUTPUT
=$OUTPUT$fullYears"Y"; fi 
 171     if $displayMonths; then 
 172         OUTPUT
=$OUTPUT$fullMonths"M"; fi 
 173     if $displayDays; then 
 174         OUTPUT
=$OUTPUT$fullDays"D"; fi 
 175     if $displayDateTime; then 
 176         OUTPUT
=$OUTPUT"T"; fi 
 177     if $displayHours; then 
 178         OUTPUT
=$OUTPUT$fullHours"H"; fi 
 179     if $displayMinutes; then 
 180         OUTPUT
=$OUTPUT$fullMinutes"M"; fi 
 181     if $displaySeconds; then 
 182         OUTPUT
=$OUTPUT$fullSeconds"S"; fi 
 184     ## Output duration string to stdout 
 185     echo "$OUTPUT" && returnState
="true"; 
 187     #===Determine function return code=== 
 188     if [ "$returnState" = "true" ]; then 
 190     elif [ "$returnState" = "error_input" ]; then 
 194         yell 
"ERROR:Unknown"; 
 198 } # Get duration (ex: PT10M4S ) 
 200 #==BEGIN sample code== 
 201 echo "Precision 6 duration:$(timeDuration "$
(date +%s
)" 6)" 
 202 echo "Precision 5 duration:$(timeDuration "$
(date +%s
)" 5)" 
 203 echo "Precision 4 duration:$(timeDuration "$
(date +%s
)" 4)" 
 204 echo "Precision 3 duration:$(timeDuration "$
(date +%s
)" 3)" 
 205 echo "Precision 2 duration:$(timeDuration "$
(date +%s
)" 2)" 
 206 echo "Precision 1 duration:$(timeDuration "$
(date +%s
)" 1)" 
 207 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)" 
 211 # Author: Steven Baltakatei Sandoval