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