File Coverage

blib/lib/JSCalendar/Duration.pm
Criterion Covered Total %
statement 76 80 95.0
branch 46 54 85.1
condition 13 14 92.8
subroutine 7 7 100.0
pod 2 2 100.0
total 144 157 91.7


line stmt bran cond sub pod time code
1             package JSCalendar::Duration 0.004;
2             # ABSTRACT: Convert seconds to JSCalendar durations and back
3              
4 1     1   66682 use strict;
  1         11  
  1         30  
5 1     1   5 use warnings;
  1         2  
  1         27  
6              
7 1     1   5 use Carp qw(croak);
  1         2  
  1         56  
8 1     1   7 use Exporter qw(import);
  1         1  
  1         171  
9              
10             our @EXPORT = qw(seconds_to_duration duration_to_seconds);
11              
12             sub duration_to_seconds {
13 27     27 1 19837 my $input = shift;
14              
15 27 50       77 croak("Usage: duration_to_seconds(\$duration). (Extra args provided: @_)")
16             if @_;
17              
18 27 50       66 croak('Usage: duration_to_seconds($duration)')
19             unless defined $input;
20              
21             # Let's get that out of the way
22 27 100       78 return '0' if $input eq 'P0D';
23              
24 25         41 my $toparse = $input;
25              
26 25         32 my $seconds = 0;
27              
28 1 100   1   6 if ($toparse =~ /\P{ASCII}/) {
  1         2  
  1         13  
  25         91  
29 1         202 croak("Invalid duration '$input', must be entirely ASCII");
30             }
31              
32 24 50       145 unless ($toparse =~ s/^P//) {
33 0         0 croak("Invalid duration '$input', must start with 'P'");
34             }
35              
36 24 100       71 if ($toparse =~ s/^(\d+)W\z//) {
37             # Weeks must appear on their own, no day or time component.
38 1         4 $seconds += (86400 * 7 * $1);
39 1         8 return $seconds;
40             }
41              
42 23 100       69 if ($toparse =~ s/^(\d+)D//) {
43 8         25 $seconds += (86400 * $1);
44             }
45              
46 23 100       69 return $seconds unless $toparse;
47              
48 20 50       63 unless ($toparse =~ s/^T//) {
49 0         0 croak("Invalid duration '$input', expected T here: '$toparse'");
50             }
51              
52 20 100       55 if ($toparse =~ s/^(\d+)H//) {
53 4         13 $seconds += (3600 * $1);
54             }
55              
56 20 100       55 if ($toparse =~ s/^(\d+)M//) {
57 7         24 $seconds += (60 * $1);
58             }
59              
60 20 100       84 if ($toparse =~ s/^(\d+(?:\.\d+)?)S//) {
61 18         55 $seconds += $1;
62             }
63              
64 20 50       39 if ($toparse) {
65 0         0 croak("Invalid duration '$input': confused by '$toparse'");
66             }
67              
68 20         131 return $seconds;
69             }
70              
71             sub seconds_to_duration {
72 26     26 1 15880 my $input = shift;
73              
74 26 50       75 croak("Usage: seconds_to_duration(\$seconds). (Extra args provided: @_)")
75             if @_;
76              
77 26 50       57 croak('Usage: seconds_to_duration($seconds)')
78             unless defined $input;
79              
80 26         34 my $toparse = $input;
81              
82 26         39 my $dec;
83              
84 26 100       118 $dec = $1 if $toparse =~ s/\.(\d+)$//;
85              
86             # .1 becomes "", we want 0 after
87 26   100     67 $toparse ||= 0;
88              
89 26 50 66     151 if ($toparse && $toparse !~ /^\d+$/) {
90 0         0 croak("Usage: seconds_to_duration(\$seconds). (Non-number value provided: '$input'");
91             }
92              
93 26         57 my ($durday, $durtime) = ("", "");
94              
95 26         38 my $days = 0;
96              
97 26         71 while ($toparse >= 86400) {
98 31         38 $days++;
99 31         56 $toparse -= 86400;
100             }
101              
102 26 100 100     77 if ($days && $days % 7 == 0 && $toparse == 0) {
      100        
103 1         14 return 'P' . ($days/7) . "W";
104             }
105              
106 25 100       46 $durday = "${days}D" if $days;
107              
108 25         36 my $hours = 0;
109              
110 25         67 while ($toparse >= 3600) {
111 48         61 $hours++;
112 48         76 $toparse -= 3600;
113             }
114              
115 25 100       47 $durtime = "${hours}H" if $hours;
116              
117 25         36 my $minutes = 0;
118              
119 25         45 while ($toparse >= 60) {
120 239         291 $minutes++;
121 239         369 $toparse -= 60;
122             }
123              
124 25 100       51 $durtime .= "${minutes}M" if $minutes;
125              
126 25         41 my $seconds = 0;
127              
128 25         52 while ($toparse >= 1) {
129 364         435 $seconds++;
130 364         585 $toparse -= 1;
131             }
132              
133 25 100       50 $durtime .= "${seconds}" if $seconds;
134              
135 25 100       59 if ($dec) {
    100          
136 7 100       22 $durtime .= $durtime ? ".${dec}S" : "0.${dec}S";
137             } elsif ($seconds) {
138 11         15 $durtime .= "S";
139             }
140              
141             # PD
142 25 100 100     85 return "P0D" unless $durday || $durtime;
143              
144 23 100       54 $durtime = "T$durtime" if $durtime;
145              
146 23         146 return "P" . $durday . $durtime;
147             }
148              
149             1;
150              
151             =pod
152              
153             =encoding UTF-8
154              
155             =head1 NAME
156              
157             JSCalendar::Duration - Convert seconds to JSCalendar durations and back
158              
159             =head1 VERSION
160              
161             version 0.004
162              
163             =head1 SYNOPSIS
164              
165             use JSCalendar::Duration qw(
166             seconds_to_duration
167             duration_to_seconds
168             );
169              
170             # 104403.1
171             my $seconds = duration_to_seconds("P1DT5H3.1S");
172              
173             # P1D
174             my $duration = seconds_to_duration('86400');
175              
176             =head1 DESCRIPTION
177              
178             This module converts between a duration of time as specified by seconds and
179             a JSCalendar duration (L).
180              
181             =head1 PERL VERSION
182              
183             This module should work on any version of perl still receiving updates from
184             the Perl 5 Porters. This means it should work on any version of perl released
185             in the last two to three years. (That is, if the most recently released
186             version is v5.40, then this module should work on both v5.40 and v5.38.)
187              
188             Although it may work on older versions of perl, no guarantee is made that the
189             minimum required version will not be increased. The version may be increased
190             for any reason, and there is no promise that patches will be accepted to lower
191             the minimum required perl.
192              
193             =head1 EXPORTS
194              
195             =head2 seconds_to_duration
196              
197             my $duration = seconds_to_duration("86401.2");
198              
199             Converts seconds to a JSCalendar duration representation.
200              
201             =head2 duration_to_seconds
202              
203             my $seconds = duration_to_seconds("P1DT4H");
204              
205             Converts a JSCalendar duration to seconds.
206              
207             =head1 SEE ALSO
208              
209             =over 4
210              
211             =item L
212              
213             The JSCalendar duration spec.
214              
215             =back
216              
217             =head1 AUTHOR
218              
219             Matthew Horsfall
220              
221             =head1 CONTRIBUTORS
222              
223             =for stopwords Mohammad S Anwar Ricardo Signes
224              
225             =over 4
226              
227             =item *
228              
229             Mohammad S Anwar
230              
231             =item *
232              
233             Ricardo Signes
234              
235             =back
236              
237             =head1 COPYRIGHT AND LICENSE
238              
239             This software is copyright (c) 2018 by Matthew Horsfall.
240              
241             This is free software; you can redistribute it and/or modify it under
242             the same terms as the Perl 5 programming language system itself.
243              
244             =cut
245              
246             __END__