File Coverage

blib/lib/DateTime/Schedule.pm
Criterion Covered Total %
statement 43 43 100.0
branch 4 4 100.0
condition n/a
subroutine 8 8 100.0
pod 4 5 80.0
total 59 60 98.3


line stmt bran cond sub pod time code
1             package DateTime::Schedule 0.03;
2 5     5   685820 use v5.26;
  5         23  
3              
4             # ABSTRACT: Determine scheduled days in range based on inclusions/exclusions
5              
6 5     5   2171 use Object::Pad;
  5         41734  
  5         52  
7              
8             class DateTime::Schedule {
9 5     5   5450 use DateTime::Set;
  5         4384841  
  5         9602  
10              
11 37     37 1 1353 field $portion : param : reader = 1;
12              
13              
14 37     824 1 207 field $exclude : param : reader = [];
  824         3594  
15              
16 824         3813 ADJUST {
17             $portion = 0 if ($portion < 0);
18             $portion = 1 if ($portion > 1);
19              
20             $exclude = {map {$_->strftime('%F') => 1} ($exclude // [])->@*};
21             }
22              
23 34     34 0 104 method day_frac($datetime) {
  34         123  
  34         74  
  34         55  
24 34         109 my $this_day = $datetime->clone->truncate(to => 'day');
25 34         12793 my $next_day = $this_day->clone->add(days => 1);
26 34         47071 my $total = $next_day->epoch - $this_day->epoch; #total number of seconds in "this" day
27 34         642 my $diff = $datetime->epoch - $this_day->epoch; #number of seconds elapsed since beginning of day
28 34         827 return $diff / $total;
29             }
30              
31 22     22 1 45 method is_day_scheduled($d) {
  22         81  
  22         34  
  22         30  
32 22         84 my $str = $d->strftime('%F');
33 22         1731 return !$self->exclude->{$str};
34             }
35              
36 34     34 1 79932 method days_in_range($start, $end) {
  34         163  
  34         69  
  34         70  
  34         89  
37 34         141 $start = $start->clone;
38 34         515 $end = $end->clone;
39 34 100       505 $end = $end->add(days => 1) if ($self->day_frac($end) > $self->portion);
40 34         2296 $start->truncate(to => 'day');
41 34         12029 $end->truncate(to => 'day');
42              
43 34         11146 my $dates = [];
44 34         150 my $d = $start->clone;
45 34         560 while ($d < $end) {
46 823 100       1179837 push($dates->@*, $d->clone) if ($self->is_day_scheduled($d));
47 823         10850 $d->add(days => 1);
48             }
49 34         46339 DateTime::Set->from_datetimes(dates => $dates);
50             }
51              
52             }
53              
54             =head1 NAME
55              
56             DateTime::Schedule - Determine scheduled days in range based on exclusions
57              
58             =head1 SYNOPSIS
59              
60             use DateTime::Schedule;
61              
62             my $dts = DateTime::Schedule->new(exclude => [
63             DateTime->new(year => 2024, month => 01, day => 01),
64             DateTime->new(year => 2024, month => 07, day => 04),
65             DateTime->new(year => 2024, month => 12, day => 25)
66             ]);
67              
68             my $start = DateTime->new(year => 2024, month => 1, day => 1);
69             my $end = DateTime->new(year => 2024, month => 12, day => 31);
70             print $dts->days_in_range($start, $end)->count; # 363
71              
72             =head1 DESCRIPTION
73              
74             This is a simple class that allows you to find out which days are "scheduled"
75             between a start date and an end date. For instance, given the start date of a
76             school year, and the current date, and with all school holidays entered as
77             L</exclude>d, this can tell you how many school days have elapsed in the year.
78              
79             =head1 CONSTRUCTORS
80              
81             =head2 new
82              
83             Default constructor. Returns a new L<DateTime::Schedule> instance.
84              
85             Parameters:
86              
87             =head4 portion
88              
89             I<Optional>. Default C<1>.
90              
91             A number between 0 and 1 indicating how much of a day must elapse to be
92             included/excluded at the boundaries of the range.
93              
94             =head4 exclude
95              
96             I<Optional>. Default C<[]>
97              
98             An arrayref of L<DateTime>s. These days are exclusions to the normal schedule
99             (e.g., holidays). Any time-portion of the DateTimes is ignored.
100              
101             =head1 METHODS
102              
103             =head2 is_day_scheduled($datetime)
104              
105             Given a L<DateTime>, returns true/false to indicate whether that day is
106             scheduled or not.
107              
108             Should be overridden by subclasses.
109              
110             =head2 days_in_range($start, $end)
111              
112             Given start/end L<DateTime>s, returns a L<DateTime::Set> of all the days which
113             are scheduled (i.e., not excluded)
114              
115             =head1 AUTHOR
116              
117             Mark Tyrrell C<< <mark@tyrrminal.dev> >>
118              
119             =head1 LICENSE
120              
121             Copyright (c) 2024 Mark Tyrrell
122              
123             Permission is hereby granted, free of charge, to any person obtaining a copy
124             of this software and associated documentation files (the "Software"), to deal
125             in the Software without restriction, including without limitation the rights
126             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
127             copies of the Software, and to permit persons to whom the Software is
128             furnished to do so, subject to the following conditions:
129              
130             The above copyright notice and this permission notice shall be included in all
131             copies or substantial portions of the Software.
132              
133             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
134             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
135             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
136             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
137             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
138             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
139             SOFTWARE.
140              
141             =cut
142              
143             1;
144              
145             __END__