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.003;
2             # ABSTRACT: Convert seconds to JSCalendar durations and back
3              
4 1     1   69302 use strict;
  1         9  
  1         30  
5 1     1   5 use warnings;
  1         2  
  1         26  
6              
7 1     1   5 use Carp qw(croak);
  1         2  
  1         54  
8 1     1   6 use Exporter qw(import);
  1         2  
  1         173  
9              
10             our @EXPORT = qw(seconds_to_duration duration_to_seconds);
11              
12             sub duration_to_seconds {
13 27     27 1 20270 my $input = shift;
14              
15 27 50       68 croak("Usage: duration_to_seconds(\$duration). (Extra args provided: @_)")
16             if @_;
17              
18 27 50       59 croak('Usage: duration_to_seconds($duration)')
19             unless defined $input;
20              
21             # Let's get that out of the way
22 27 100       73 return '0' if $input eq 'P0D';
23              
24 25         50 my $toparse = $input;
25              
26 25         37 my $seconds = 0;
27              
28 1 100   1   7 if ($toparse =~ /\P{ASCII}/) {
  1         1  
  1         14  
  25         98  
29 1         257 croak("Invalid duration '$input', must be entirely ASCII");
30             }
31              
32 24 50       116 unless ($toparse =~ s/^P//) {
33 0         0 croak("Invalid duration '$input', must start with 'P'");
34             }
35              
36 24 100       70 if ($toparse =~ s/^(\d+)W\z//) {
37             # Weeks must appear on their own, no day or time component.
38 1         3 $seconds += (86400 * 7 * $1);
39 1         9 return $seconds;
40             }
41              
42 23 100       81 if ($toparse =~ s/^(\d+)D//) {
43 8         36 $seconds += (86400 * $1);
44             }
45              
46 23 100       61 return $seconds unless $toparse;
47              
48 20 50       62 unless ($toparse =~ s/^T//) {
49 0         0 croak("Invalid duration '$input', expected T here: '$toparse'");
50             }
51              
52 20 100       61 if ($toparse =~ s/^(\d+)H//) {
53 4         13 $seconds += (3600 * $1);
54             }
55              
56 20 100       60 if ($toparse =~ s/^(\d+)M//) {
57 7         24 $seconds += (60 * $1);
58             }
59              
60 20 100       83 if ($toparse =~ s/^(\d+(?:\.\d+)?)S//) {
61 18         55 $seconds += $1;
62             }
63              
64 20 50       41 if ($toparse) {
65 0         0 croak("Invalid duration '$input': confused by '$toparse'");
66             }
67              
68 20         130 return $seconds;
69             }
70              
71             sub seconds_to_duration {
72 26     26 1 17023 my $input = shift;
73              
74 26 50       76 croak("Usage: seconds_to_duration(\$seconds). (Extra args provided: @_)")
75             if @_;
76              
77 26 50       60 croak('Usage: seconds_to_duration($seconds)')
78             unless defined $input;
79              
80 26         37 my $toparse = $input;
81              
82 26         39 my $dec;
83              
84 26 100       112 $dec = $1 if $toparse =~ s/\.(\d+)$//;
85              
86             # .1 becomes "", we want 0 after
87 26   100     79 $toparse ||= 0;
88              
89 26 50 66     153 if ($toparse && $toparse !~ /^\d+$/) {
90 0         0 croak("Usage: seconds_to_duration(\$seconds). (Non-number value provided: '$input'");
91             }
92              
93 26         53 my ($durday, $durtime) = ("", "");
94              
95 26         37 my $days = 0;
96              
97 26         76 while ($toparse >= 86400) {
98 31         41 $days++;
99 31         53 $toparse -= 86400;
100             }
101              
102 26 100 100     94 if ($days && $days % 7 == 0 && $toparse == 0) {
      100        
103 1         23 return 'P' . ($days/7) . "W";
104             }
105              
106 25 100       50 $durday = "${days}D" if $days;
107              
108 25         39 my $hours = 0;
109              
110 25         51 while ($toparse >= 3600) {
111 48         60 $hours++;
112 48         78 $toparse -= 3600;
113             }
114              
115 25 100       46 $durtime = "${hours}H" if $hours;
116              
117 25         36 my $minutes = 0;
118              
119 25         47 while ($toparse >= 60) {
120 239         285 $minutes++;
121 239         372 $toparse -= 60;
122             }
123              
124 25 100       54 $durtime .= "${minutes}M" if $minutes;
125              
126 25         48 my $seconds = 0;
127              
128 25         50 while ($toparse >= 1) {
129 364         498 $seconds++;
130 364         675 $toparse -= 1;
131             }
132              
133 25 100       50 $durtime .= "${seconds}" if $seconds;
134              
135 25 100       61 if ($dec) {
    100          
136 7 100       19 $durtime .= $durtime ? ".${dec}S" : "0.${dec}S";
137             } elsif ($seconds) {
138 11         18 $durtime .= "S";
139             }
140              
141             # PD
142 25 100 100     83 return "P0D" unless $durday || $durtime;
143              
144 23 100       56 $durtime = "T$durtime" if $durtime;
145              
146 23         175 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.003
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 EXPORTS
182              
183             =head2 seconds_to_duration
184              
185             my $duration = seconds_to_duration("86401.2");
186              
187             Converts seconds to a JSCalendar duration representation.
188              
189             =head2 duration_to_seconds
190              
191             my $seconds = duration_to_seconds("P1DT4H");
192              
193             Converts a JSCalendar duration to seconds.
194              
195             =head1 SEE ALSO
196              
197             =over 4
198              
199             =item L
200              
201             The JSCalendar duration spec.
202              
203             =back
204              
205             =head1 AUTHOR
206              
207             Matthew Horsfall
208              
209             =head1 CONTRIBUTORS
210              
211             =for stopwords Mohammad S Anwar Ricardo Signes
212              
213             =over 4
214              
215             =item *
216              
217             Mohammad S Anwar
218              
219             =item *
220              
221             Ricardo Signes
222              
223             =back
224              
225             =head1 COPYRIGHT AND LICENSE
226              
227             This software is copyright (c) 2018 by Matthew Horsfall.
228              
229             This is free software; you can redistribute it and/or modify it under
230             the same terms as the Perl 5 programming language system itself.
231              
232             =cut
233              
234             __END__