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 argSeconds argPrecision precision returnState remainder
23 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
24 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
25 local witherPrecision output
26 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
28 argSeconds
="$1"; # read arg1 (seconds)
29 argPrecision
="$2"; # read arg2 (precision)
30 precision
=2; # set default precision
32 # Check that between one and two arguments is supplied
33 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
34 yell
"ERROR:Invalid number of arguments:$# . Exiting.";
35 returnState
="error_input"; fi
37 # Check that argSeconds provided
38 if [[ $# -ge 1 ]]; then
39 ## Check that argSeconds is a positive integer
40 if [[ "$argSeconds" =~ ^
[[:digit
:]]+$
]]; then
43 yell
"ERROR:argSeconds not a digit.";
44 returnState
="error_input";
47 yell
"ERROR:No argument provided. Exiting.";
51 # Consider whether argPrecision was provided
52 if [[ $# -eq 2 ]]; then
53 # Check that argPrecision is a positive integer
54 if [[ "$argPrecision" =~ ^
[[:digit
:]]+$
]] && [[ "$argPrecision" -gt 0 ]]; then
55 precision
="$argPrecision";
57 yell
"ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
58 returnState
="error_input";
64 remainder
="$argSeconds" ; # seconds
65 ## Calculate full years Y, update remainder
66 fullYears
=$
(( remainder
/ (365*24*60*60) ));
67 remainder
=$
(( remainder
- (fullYears
*365*24*60*60) ));
68 ## Calculate full months M, update remainder
69 fullMonths
=$
(( remainder
/ (30*24*60*60) ));
70 remainder
=$
(( remainder
- (fullMonths
*30*24*60*60) ));
71 ## Calculate full days D, update remainder
72 fullDays
=$
(( remainder
/ (24*60*60) ));
73 remainder
=$
(( remainder
- (fullDays
*24*60*60) ));
74 ## Calculate full hours H, update remainder
75 fullHours
=$
(( remainder
/ (60*60) ));
76 remainder
=$
(( remainder
- (fullHours
*60*60) ));
77 ## Calculate full minutes M, update remainder
78 fullMinutes
=$
(( remainder
/ (60) ));
79 remainder
=$
(( remainder
- (fullMinutes
*60) ));
80 ## Calculate full seconds S, update remainder
81 fullSeconds
=$
(( remainder
/ (1) ));
82 remainder
=$
(( remainder
- (remainder
*1) ));
83 ## Check which fields filled
84 if [[ $fullYears -gt 0 ]]; then hasYears
="true"; else hasYears
="false"; fi
85 if [[ $fullMonths -gt 0 ]]; then hasMonths
="true"; else hasMonths
="false"; fi
86 if [[ $fullDays -gt 0 ]]; then hasDays
="true"; else hasDays
="false"; fi
87 if [[ $fullHours -gt 0 ]]; then hasHours
="true"; else hasHours
="false"; fi
88 if [[ $fullMinutes -gt 0 ]]; then hasMinutes
="true"; else hasMinutes
="false"; fi
89 if [[ $fullSeconds -gt 0 ]]; then hasSeconds
="true"; else hasSeconds
="false"; fi
91 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
92 witherPrecision
="false"
95 if $hasYears && [[ $precision -gt 0 ]]; then
97 witherPrecision
="true";
101 if $witherPrecision; then ((precision--
)); fi;
104 if $hasMonths && [[ $precision -gt 0 ]]; then
105 displayMonths
="true";
106 witherPrecision
="true";
108 displayMonths
="false";
110 if $witherPrecision && [[ $precision -gt 0 ]]; then
111 displayMonths
="true";
113 if $witherPrecision; then ((precision--
)); fi;
116 if $hasDays && [[ $precision -gt 0 ]]; then
118 witherPrecision
="true";
122 if $witherPrecision && [[ $precision -gt 0 ]]; then
125 if $witherPrecision; then ((precision--
)); fi;
128 if $hasHours && [[ $precision -gt 0 ]]; then
130 witherPrecision
="true";
132 displayHours
="false";
134 if $witherPrecision && [[ $precision -gt 0 ]]; then
137 if $witherPrecision; then ((precision--
)); fi;
140 if $hasMinutes && [[ $precision -gt 0 ]]; then
141 displayMinutes
="true";
142 witherPrecision
="true";
144 displayMinutes
="false";
146 if $witherPrecision && [[ $precision -gt 0 ]]; then
147 displayMinutes
="true";
149 if $witherPrecision; then ((precision--
)); fi;
153 if $hasSeconds && [[ $precision -gt 0 ]]; then
154 displaySeconds
="true";
155 witherPrecision
="true";
157 displaySeconds
="false";
159 if $witherPrecision && [[ $precision -gt 0 ]]; then
160 displaySeconds
="true";
162 if $witherPrecision; then ((precision--
)); fi;
164 ## Determine whether or not the "T" separator is needed to separate date and time elements
165 if ( $displayHours ||
$displayMinutes ||
$displaySeconds); then
166 displayDateTime
="true"; else displayDateTime
="false"; fi
168 ## Construct duration output string
170 if $displayYears; then
171 output
=$output$fullYears"Y"; fi
172 if $displayMonths; then
173 output
=$output$fullMonths"M"; fi
174 if $displayDays; then
175 output
=$output$fullDays"D"; fi
176 if $displayDateTime; then
177 output
=$output"T"; fi
178 if $displayHours; then
179 output
=$output$fullHours"H"; fi
180 if $displayMinutes; then
181 output
=$output$fullMinutes"M"; fi
182 if $displaySeconds; then
183 output
=$output$fullSeconds"S"; fi
185 ## Output duration string to stdout
186 echo "$output" && returnState
="true";
188 #===Determine function return code===
189 if [ "$returnState" = "true" ]; then
191 elif [ "$returnState" = "error_input" ]; then
195 yell
"ERROR:Unknown";
199 } # Get duration (ex: PT10M4S )
201 #==BEGIN sample code==
202 echo "Precision 6 duration:$(timeDuration "$
(date +%s
)" 6)"
203 echo "Precision 5 duration:$(timeDuration "$
(date +%s
)" 5)"
204 echo "Precision 4 duration:$(timeDuration "$
(date +%s
)" 4)"
205 echo "Precision 3 duration:$(timeDuration "$
(date +%s
)" 3)"
206 echo "Precision 2 duration:$(timeDuration "$
(date +%s
)" 2)"
207 echo "Precision 1 duration:$(timeDuration "$
(date +%s
)" 1)"
208 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)"
209 echo "Parsing argument provided to script:\"$*\""; timeDuration
"$@"
212 # Author: Steven Baltakatei Sandoval