File Coverage

blib/lib/DateTime/Format/Natural/Calc.pm
Criterion Covered Total %
statement 205 207 99.0
branch 32 44 72.7
condition 10 19 52.6
subroutine 35 35 100.0
pod n/a
total 282 305 92.4


line stmt bran cond sub pod time code
1             package DateTime::Format::Natural::Calc;
2              
3 26     26   232 use strict;
  26         77  
  26         974  
4 26     26   130 use warnings;
  26         47  
  26         1460  
5 26         14010 use base qw(
6             DateTime::Format::Natural::Compat
7             DateTime::Format::Natural::Utils
8             DateTime::Format::Natural::Wrappers
9 26     26   133 );
  26         47  
10              
11 26     26   296 use constant MORNING => '08';
  26         64  
  26         2554  
12 26     26   191 use constant AFTERNOON => '14';
  26         55  
  26         1601  
13 26     26   155 use constant EVENING => '20';
  26         48  
  26         95805  
14              
15             our $VERSION = '1.47';
16              
17             my $multiply_by = sub
18             {
19             my ($value, $opts) = @_;
20             return $value * $opts->{multiply_by} if exists $opts->{multiply_by};
21             return $value;
22             };
23              
24             sub _no_op
25             {
26 92     92   227 my $self = shift;
27 92         421 $self->_register_trace;
28 92         330 my $opts = pop;
29             }
30              
31             sub _ago_variant
32             {
33 497     497   1390 my $self = shift;
34 497         2612 $self->_register_trace;
35 497         2598 my $opts = pop;
36 497         11927 $self->_subtract($opts->{unit} => $multiply_by->(shift, $opts));
37             }
38              
39             sub _now_variant
40             {
41 108     108   263 my $self = shift;
42 108         548 $self->_register_trace;
43 108         309 my $opts = pop;
44 108         390 my ($value, $when) = @_;
45             $self->_add_or_subtract({
46             when => $when,
47             unit => $opts->{unit},
48 108         560 value => $multiply_by->($value, $opts),
49             });
50             }
51              
52             sub _daytime_variant
53             {
54 409     409   959 my $self = shift;
55 409         1791 $self->_register_trace;
56 409         1249 my $opts = pop;
57 409         1160 my ($daytime) = @_;
58 409         2417 my %lookup = (
59             0 => 'morning',
60             1 => 'afternoon',
61             2 => 'evening',
62             );
63 409         1202 $daytime = $lookup{$daytime};
64 409         2322 my %daytimes = (
65             morning => MORNING,
66             afternoon => AFTERNOON,
67             evening => EVENING,
68             );
69             my $hour = exists $self->{Daytime}{$daytime}
70             ? $self->{Daytime}{$daytime}
71 409 100       1738 : $daytimes{$daytime};
72 409 50       2177 if ($self->_valid_time(hour => $hour)) {
73 409         7798 $self->_set(hour => $hour);
74             }
75             }
76              
77             sub _daytime
78             {
79 390     390   993 my $self = shift;
80 390         1895 $self->_register_trace;
81 390         1261 my $opts = pop;
82 390         1124 my ($hour) = @_;
83 390   100     2666 $hour += $opts->{hours} || 0;
84 390 50       2348 if ($self->_valid_time(hour => $hour)) {
85 390         7934 $self->_set(hour => $hour);
86             }
87             }
88              
89             sub _hourtime_variant
90             {
91 96     96   235 my $self = shift;
92 96         447 $self->_register_trace;
93 96         266 my $opts = pop;
94 96         283 my ($value, $when) = @_;
95 96   100     518 my $hours = $opts->{hours} || 0;
96 96 50       599 if ($self->_valid_time(hour => $hours)) {
97 96         1785 $self->_set(hour => $hours);
98 96         501 $self->{datetime}->set(minute => 0, second => 0, nanosecond => 0);
99             $self->_add_or_subtract({
100             when => $when,
101             unit => $opts->{unit},
102 96         50424 value => $multiply_by->($value, $opts),
103             });
104             }
105             }
106              
107             sub _month_day
108             {
109 2660     2660   7220 my $self = shift;
110 2660         12740 $self->_register_trace;
111 2660         8916 my $opts = pop;
112 2660         8663 my ($day, $month) = @_;
113 2660 50       15176 if ($self->_valid_date(month => $month, day => $day)) {
114 2660         53058 $self->_set(
115             month => $month,
116             day => $day,
117             );
118             }
119             }
120              
121             sub _unit_date
122             {
123 519     519   1366 my $self = shift;
124 519         2274 $self->_register_trace;
125 519         1799 my $opts = pop;
126 519         1734 my ($value) = @_;
127 519 100       3221 $self->{datetime}->set(day => 1) if $opts->{unit} eq 'month';
128 519 50       60326 if ($self->_valid_date($opts->{unit} => $value)) {
129 519         11202 $self->_set($opts->{unit} => $value);
130             }
131             }
132              
133             sub _weekday
134             {
135 2319     2319   5636 my $self = shift;
136 2319         9913 $self->_register_trace;
137 2319         6780 my $opts = pop;
138 2319         5942 my ($day) = @_;
139 2319 100       13811 if ($day > $self->{datetime}->wday) {
140 723         7255 $self->_add(day => ($day - $self->{datetime}->wday));
141             }
142             else {
143 1596         12886 $self->_subtract(day => ($self->{datetime}->wday - $day));
144             }
145             }
146              
147             sub _count_day_variant_week
148             {
149 1116     1116   3725 my $self = shift;
150 1116         5770 $self->_register_trace;
151 1116         3856 my $opts = pop;
152 1116         3799 my ($when, $day) = @_;
153             my %days = (
154             -1 => ($self->{datetime}->wday + (7 - $day)),
155             0 => ($day - $self->{datetime}->wday),
156 1116         8250 1 => (7 - $self->{datetime}->wday + $day),
157             );
158             $self->_add_or_subtract({
159             when => ($when == 0) ? 1 : $when,
160             unit => 'day',
161 1116 100       31202 value => $days{$when},
162             });
163             }
164              
165             sub _count_day_variant_month
166             {
167 54     54   152 my $self = shift;
168 54         266 $self->_register_trace;
169 54         171 my $opts = pop;
170 54         182 my ($when, $day) = @_;
171 54 50       419 if ($self->_valid_date(day => $day)) {
172 54         1206 $self->_add(month => $when);
173 54         280 $self->_set(day => $day);
174             }
175             }
176              
177             sub _unit_variant
178             {
179 2254     2254   5349 my $self = shift;
180 2254         9562 $self->_register_trace;
181 2254         7517 my $opts = pop;
182 2254         6158 my ($when) = @_;
183             $self->_add_or_subtract({
184             when => $when,
185             unit => $opts->{unit},
186 2254         12143 value => $multiply_by->(1, $opts),
187             });
188             }
189              
190             sub _count_month_variant_year
191             {
192 54     54   189 my $self = shift;
193 54         299 $self->_register_trace;
194 54         190 my $opts = pop;
195 54         241 my ($when, $month) = @_;
196 54 50       426 if ($self->_valid_date(month => $month)) {
197 54         1266 $self->_add(year => $when);
198 54         306 $self->_set(month => $month);
199             }
200             }
201              
202             sub _in_count_variant
203             {
204 136     136   372 my $self = shift;
205 136         647 $self->_register_trace;
206 136         406 my $opts = pop;
207 136         897 $self->_add_or_subtract($opts->{unit} => $multiply_by->(shift, $opts));
208             }
209              
210             sub _month_variant
211             {
212 108     108   281 my $self = shift;
213 108         549 $self->_register_trace;
214 108         342 my $opts = pop;
215 108         361 my ($when, $month) = @_;
216 108 50       611 if ($self->_valid_date(month => $month)) {
217 108         2163 $self->_add(year => $when);
218 108         539 $self->_set(month => $month);
219             }
220             }
221              
222             sub _count_weekday_variant_month
223             {
224 168     168   417 my $self = shift;
225 168         815 $self->_register_trace;
226 168         522 my $opts = pop;
227 168         633 my ($when, $count, $day, $month) = @_;
228 168         338 my $year;
229 168         387 local $@;
230 168         456 eval {
231             ($year, $month, $day) =
232             $self->_Nth_Weekday_of_Month_Year(
233             $self->{datetime}->year + $when,
234             defined $month
235             ? $month
236             : $self->{datetime}->month,
237 168 100       1213 $day,
238             $count,
239             );
240             };
241 168 50 33     4438 if (!$@
      33        
      33        
      33        
242             and defined $year && defined $month && defined $day
243             and $self->_check_date($year, $month, $day)
244             ) {
245 168         1213 $self->_set(
246             year => $year,
247             month => $month,
248             day => $day,
249             );
250             }
251             else {
252 0         0 $self->_set_failure;
253 0         0 $self->_set_error("(date is not valid)");
254             }
255             }
256              
257             sub _daytime_unit_variant
258             {
259 144     144   336 my $self = shift;
260 144         688 $self->_register_trace;
261 144         415 my $opts = pop;
262 144         537 my ($value, $when, $days) = @_;
263 144         893 $self->_add(day => $days);
264 144         1119 $self->{datetime}->set(hour => 0, minute => 0, second => 0, nanosecond => 0);
265             $self->_add_or_subtract({
266             when => $when,
267             unit => $opts->{unit},
268 144         99740 value => $multiply_by->($value, $opts),
269             });
270             }
271              
272             # wrapper for <time> AM/PM
273             sub _at
274             {
275 6487     6487   17627 my $self = shift;
276 6487         29963 $self->_register_trace;
277 6487         33488 $self->_at_time(@_);
278             }
279              
280             # wrapper for <time>
281             sub _time
282             {
283 1835     1835   4546 my $self = shift;
284 1835         9312 $self->_register_trace;
285 1835         10029 $self->_at_time(@_);
286             }
287              
288             sub _at_time
289             {
290 8322     8322   19403 my $self = shift;
291 8322         20481 my $opts = pop;
292 8322         24349 my ($time) = @_;
293 8322         33289 my @units = qw(hour minute second nanosecond);
294 8322         49650 my %values = map { shift @units => $_ } split /[:\.]/, $time;
  18033         73762  
295 8322 100       44316 $values{nanosecond} *= 1_000_000 if exists $values{nanosecond}; # milli to nano
296 8322 50       50403 if ($self->_valid_time(%values)) {
297 8322         174506 $self->_set(%values);
298             }
299             }
300              
301             sub _count_yearday_variant_year
302             {
303 132     132   366 my $self = shift;
304 132         1017 $self->_register_trace;
305 132         463 my $opts = pop;
306 132         532 my ($day, $when) = @_;
307 132         428 my ($year, $month);
308 132         1125 ($year, $month, $day) = $self->_Add_Delta_Days($self->{datetime}->year, $day);
309 132         2299 $self->_set(
310             year => $year + $when,
311             month => $month,
312             day => $day,
313             );
314             }
315              
316             sub _count_weekday
317             {
318 78     78   181 my $self = shift;
319 78         498 $self->_count_weekday_variant_month(0, @_[0,1], undef, $_[-1]);
320             }
321              
322             sub _day_month_year
323             {
324 27     27   89 my $self = shift;
325 27         167 $self->_register_trace;
326 27         92 my $opts = pop;
327 27         126 my ($day, $month, $year) = @_;
328 27 50       252 if ($self->_valid_date(year => $year, month => $month, day => $day)) {
329 27         660 $self->_set(
330             year => $year,
331             month => $month,
332             day => $day,
333             );
334             }
335             }
336              
337             sub _count_weekday_from_now
338             {
339 42     42   122 my $self = shift;
340 42         273 $self->_register_trace;
341 42         163 my $opts = pop;
342 42         167 my ($count, $day) = @_;
343 42         398 my $wday = $self->{datetime}->wday;
344 42 50       713 $self->_add(day => ($count - 1) * 7 +
345             (($wday < $day)
346             ? $day - $wday
347             : (7 - $wday) + $day)
348             );
349             }
350              
351             sub _final_weekday_in_month
352             {
353 60     60   178 my $self = shift;
354 60         302 $self->_register_trace;
355 60         217 my $opts = pop;
356 60         280 my ($wday, $month) = @_;
357 60         1281 my $days = $self->_Days_in_Month($self->{datetime}->year, $month);
358 60         1264 my ($year, $day);
359             ($year, $month, $day) = $self->_Nth_Weekday_of_Month_Year(
360             $self->{datetime}->year,
361 60         280 $month,
362             $wday,
363             1,
364             );
365 60         920 while ($day <= $days - 7) {
366 186         451 $day += 7;
367             }
368             $self->_set(
369 60         413 year => $year,
370             month => $month,
371             day => $day,
372             );
373             }
374              
375             sub _first_last_day_unit
376             {
377 78     78   187 my $self = shift;
378 78         321 $self->_register_trace;
379 78         222 my $opts = pop;
380 78         163 my ($year, $month, $day) = do {
381 78 100       389 @_ >= 3 ? @_ : (undef, @_);
382             };
383 78   66     531 $year ||= $self->{datetime}->year;
384 78 100       609 unless (defined $day) {
385 39         254 $day = $self->_Days_in_Month($year, $month);
386             }
387             $self->_set(
388 78         586 year => $year,
389             month => $month,
390             day => $day,
391             );
392             }
393              
394             sub _variant_last_month
395             {
396 12     12   46 my $self = shift;
397 12         64 $self->_register_trace;
398 12         34 my $opts = pop;
399 12         34 my ($day) = @_;
400 12         74 $self->_subtract(month => 1);
401 12 100       75 unless (defined $day) {
402 6         41 $day = $self->_Days_in_Month($self->{datetime}->year, $self->{datetime}->month);
403             }
404 12         109 $self->_set(day => $day);
405             }
406              
407             sub _variant_quarter
408             {
409 18     18   42 my $self = shift;
410 18         72 $self->_register_trace;
411 18         53 my $opts = pop;
412 18         71 my ($when) = @_;
413 18         169 $self->_subtract(day => $self->{datetime}->day_of_quarter - 1);
414             $self->_add_or_subtract({
415             when => $when,
416             unit => $opts->{unit},
417 18         155 value => 3,
418             });
419             }
420              
421             sub _begin_end_month
422             {
423 36     36   115 my $self = shift;
424 36         176 $self->_register_trace;
425 36         99 my $opts = pop;
426 36         97 my ($day) = @_;
427 36 100       182 unless (defined $day) {
428 18         106 $day = $self->_Days_in_Month($self->{datetime}->year, $self->{datetime}->month);
429             }
430 36         311 $self->_set(day => $day);
431             }
432              
433             1;
434             __END__
435              
436             =head1 NAME
437              
438             DateTime::Format::Natural::Calc - Basic calculations
439              
440             =head1 SYNOPSIS
441              
442             Please see the DateTime::Format::Natural documentation.
443              
444             =head1 DESCRIPTION
445              
446             The C<DateTime::Format::Natural::Calc> class defines the worker methods.
447              
448             =head1 SEE ALSO
449              
450             L<DateTime::Format::Natural>
451              
452             =head1 AUTHOR
453              
454             Steven Schubiger <schubiger@cpan.org>
455              
456             =head1 LICENSE
457              
458             This program is free software; you may redistribute it and/or
459             modify it under the same terms as Perl itself.
460              
461             See L<http://dev.perl.org/licenses/>
462              
463             =cut