File Coverage

blib/lib/Date/Utils.pm
Criterion Covered Total %
statement 80 88 90.9
branch 29 46 63.0
condition 24 42 57.1
subroutine 19 20 95.0
pod 11 11 100.0
total 163 207 78.7


line stmt bran cond sub pod time code
1             package Date::Utils;
2              
3             $Date::Utils::VERSION = '0.27';
4             $Date::Utils::AUTHORITY = 'cpan:MANWAR';
5              
6             =head1 NAME
7              
8             Date::Utils - Common date functions as Moo Role.
9              
10             =head1 VERSION
11              
12             Version 0.27
13              
14             =cut
15              
16 2     2   82198 use 5.006;
  2         6  
17 2     2   983 use Data::Dumper;
  2         10791  
  2         99  
18 2     2   789 use POSIX qw/floor/;
  2         10245  
  2         8  
19 2     2   3048 use Term::ANSIColor::Markup;
  2         23186  
  2         9  
20              
21 2     2   455 use Moo::Role;
  2         14012  
  2         9  
22 2     2   1345 use namespace::autoclean;
  2         12047  
  2         6  
23              
24 2     2   927 use Date::Exception::InvalidDay;
  2         37068  
  2         56  
25 2     2   796 use Date::Exception::InvalidMonth;
  2         4325  
  2         50  
26 2     2   762 use Date::Exception::InvalidYear;
  2         4332  
  2         2485  
27              
28             requires qw(months days);
29              
30             has gregorian_epoch => (is => 'ro', default => sub { 1721425.5 });
31              
32             =head1 DESCRIPTION
33              
34             Common date functions as Moo Role. It is being used by the following distributions:
35              
36             =over 4
37              
38             =item * L
39              
40             =item * L
41              
42             =item * L
43              
44             =item * L
45              
46             =item * L
47              
48             =item * L
49              
50             =item * L
51              
52             =back
53              
54             =head1 METHODS
55              
56             =head2 jwday($julian_date)
57              
58             Returns day of week for the given Julian date C<$julian_date>, with 0 for Sunday.
59              
60             =cut
61              
62             sub jwday {
63 1     1 1 2 my ($self, $julian_date) = @_;
64              
65 1         6 return floor($julian_date + 1.5) % 7;
66             }
67              
68             =head2 gregorian_to_julian($year, $month, $day)
69              
70             Returns Julian date equivalent of the given Gregorian date.
71              
72             =cut
73              
74             sub gregorian_to_julian {
75 4     4 1 251 my ($self, $year, $month, $day) = @_;
76              
77 4 50       27 return ($self->gregorian_epoch - 1) +
    100          
78             (365 * ($year - 1)) +
79             floor(($year - 1) / 4) +
80             (-floor(($year - 1) / 100)) +
81             floor(($year - 1) / 400) +
82             floor((((367 * $month) - 362) / 12) +
83             (($month <= 2) ? 0 : ($self->is_gregorian_leap_year($year) ? -1 : -2)) +
84             $day);
85             }
86              
87             =head2 julian_to_gregorian($julian_date)
88              
89             Returns Gregorian date as list (year, month, day) equivalent of the given Julian
90             date C<$julian_date>.
91              
92             =cut
93              
94             sub julian_to_gregorian {
95 1     1 1 3 my ($self, $julian) = @_;
96              
97 1         3 my $wjd = floor($julian - 0.5) + 0.5;
98 1         3 my $depoch = $wjd - $self->gregorian_epoch;
99 1         4 my $quadricent = floor($depoch / 146097);
100 1         1 my $dqc = $depoch % 146097;
101 1         3 my $cent = floor($dqc / 36524);
102 1         1 my $dcent = $dqc % 36524;
103 1         2 my $quad = floor($dcent / 1461);
104 1         2 my $dquad = $dcent % 1461;
105 1         2 my $yindex = floor($dquad / 365);
106 1         2 my $year = ($quadricent * 400) + ($cent * 100) + ($quad * 4) + $yindex;
107              
108 1 50 33     6 $year++ unless (($cent == 4) || ($yindex == 4));
109              
110 1         3 my $yearday = $wjd - $self->gregorian_to_julian($year, 1, 1);
111 1 50       10 my $leapadj = (($wjd < $self->gregorian_to_julian($year, 3, 1)) ? 0 : (($self->is_gregorian_leap_year($year) ? 1 : 2)));
    50          
112 1         3 my $month = floor(((($yearday + $leapadj) * 12) + 373) / 367);
113 1         2 my $day = ($wjd - $self->gregorian_to_julian($year, $month, 1)) + 1;
114              
115 1         4 return ($year, $month, $day);
116             }
117              
118             =head2 is_gregorian_leap_year($year)
119              
120             Returns 0 or 1 if the given Gregorian year C<$year> is a leap year or not.
121              
122             =cut
123              
124             sub is_gregorian_leap_year {
125 6     6 1 486 my ($self, $year) = @_;
126              
127 6   66     44 return (($year % 4) == 0) && (!((($year % 100) == 0) && (($year % 400) != 0)));
128             }
129              
130             =head2 get_month_number($month_name)
131              
132             Returns the month number starting with 1 for the given C<$month_name>.
133              
134             =cut
135              
136             sub get_month_number {
137 5     5 1 3942 my ($self, $month_name) = @_;
138              
139 5 50 33     27 if (defined $month_name && ($month_name !~ /^\d+$/)) {
140 5         10 $self->validate_month_name($month_name);
141             }
142             else {
143 0         0 $month_name = $self->get_month_name;
144             }
145              
146 4         9 my $months = $self->months;
147 4         10 foreach my $index (1..$#$months) {
148 26 100       57 return $index if (uc($months->[$index]) eq uc($month_name));
149             }
150              
151 0         0 my @caller = caller(0);
152 0 0       0 @caller = caller(2) if $caller[3] eq '(eval)';
153              
154 0 0       0 Date::Exception::InvalidMonth->throw({
155             method => __PACKAGE__."::get_month_number",
156             message => sprintf("ERROR: Invalid month name [%s].", defined($month_name)?($month_name):('')),
157             filename => $caller[1],
158             line_number => $caller[2] });
159             }
160              
161             =head2 get_month_name($month)
162              
163             Returns the month name for the given C<$month> number (1,2,3 etc).
164              
165             =cut
166              
167             sub get_month_name {
168 3     3 1 14 my ($self, $month) = @_;
169              
170 3 100       7 if (defined $month) {
171 2         5 $self->validate_month($month);
172             }
173             else {
174 1         3 $month = $self->month;
175             }
176              
177 2         8 return $self->months->[$month];
178             }
179              
180             =head2 validate_year($year)
181              
182             Validates the given C<$year>. It has to be > 0 and numbers only.
183              
184             =cut
185              
186             sub validate_year {
187 3     3 1 536 my ($self, $year) = @_;
188              
189 3         17 my @caller = caller(0);
190 3 50       8 @caller = caller(2) if $caller[3] eq '(eval)';
191              
192 3 50 66     36 Date::Exception::InvalidYear->throw({
    100 66        
193             method => __PACKAGE__."::validate_year",
194             message => sprintf("ERROR: Invalid year [%s].", defined($year)?($year):('')),
195             filename => $caller[1],
196             line_number => $caller[2] })
197             unless (defined($year) && ($year =~ /^\d+$/) && ($year > 0));
198             }
199              
200             =head2 validate_month($month)
201              
202             Validates the given C<$month>. It has to be between 1 and 12 or month name.
203              
204             =cut
205              
206             sub validate_month {
207 8     8 1 3382 my ($self, $month) = @_;
208              
209 8 100 66     51 if (defined $month && ($month !~ /^[-+]?\d+$/)) {
210 1         3 return $self->validate_month_name($month);
211             }
212              
213 7         40 my @caller = caller(0);
214 7 50       15 @caller = caller(2) if $caller[3] eq '(eval)';
215              
216 7 50 66     70 Date::Exception::InvalidMonth->throw({
    100 66        
      100        
217             method => __PACKAGE__."::validate_month",
218             message => sprintf("ERROR: Invalid month [%s].", defined($month)?($month):('')),
219             filename => $caller[1],
220             line_number => $caller[2] })
221             unless (defined($month) && ($month =~ /^\+?\d+$/) && ($month >= 1) && ($month <= 12));
222             }
223              
224             =head2 validate_month_name($month_name)
225              
226             Validates the given C<$month_name>.
227              
228             =cut
229              
230             sub validate_month_name {
231 8     8 1 964 my ($self, $month_name) = @_;
232              
233 8         47 my @caller = caller(0);
234 8 50       21 @caller = caller(2) if $caller[3] eq '(eval)';
235              
236 8         15 my $months = $self->months;
237             Date::Exception::InvalidMonth->throw({
238             method => __PACKAGE__."::validate_month_name",
239             message => sprintf("ERROR: Invalid month name [%s].", defined($month_name)?($month_name):('')),
240             filename => $caller[1],
241             line_number => $caller[2] })
242 8 50 33     49 unless (defined($month_name) && ($month_name !~ /^[-+]?\d+$/) && (grep /$month_name/i, @{$months}[1..$#$months]));
  8 100 66     126  
243             }
244              
245             =head2 validate_day($day)
246              
247             Validates the given C<$day>. It has to be between 1 and 31.
248              
249             =cut
250              
251             sub validate_day {
252 2     2 1 443 my ($self, $day) = @_;
253              
254 2         11 my @caller = caller(0);
255 2 50       7 @caller = caller(2) if $caller[3] eq '(eval)';
256              
257 2 50 33     33 Date::Exception::InvalidDay->throw({
    100 33        
      66        
258             method => __PACKAGE__."::validate_day",
259             message => sprintf("ERROR: Invalid day [%s].", defined($day)?($day):('')),
260             filename => $caller[1],
261             line_number => $caller[2] })
262             unless (defined($day) && ($day =~ /^\d+$/) && ($day >= 1) && ($day <= 31));
263             }
264              
265             =head2 validate_date($year, $month, $day)
266              
267             Validates the given C<$year>, C<$month> and C<$day>.
268              
269             =cut
270              
271             sub validate_date {
272 0     0 1   my ($self, $year, $month, $day) = @_;
273              
274 0           $self->validate_year($year);
275 0           $self->validate_month($month);
276 0           $self->validate_day($day);
277             }
278              
279             =head1 AUTHOR
280              
281             Mohammad S Anwar, C<< >>
282              
283             =head1 REPOSITORY
284              
285             L
286              
287             =head1 ACKNOWLEDGEMENTS
288              
289             Entire logic is based on the L written by John Walker.
290              
291             =head1 SEE ALSO
292              
293             =over 4
294              
295             =item * L
296              
297             =item * L
298              
299             =item * L
300              
301             =item * L
302              
303             =item * L
304              
305             =item * L
306              
307             =item * L
308              
309             =back
310              
311             =head1 BUGS
312              
313             Please report any bugs / feature requests to C, or
314             through the web interface at L.
315             I will be notified, and then you'll automatically be notified of progress on your
316             bug as I make changes.
317              
318             =head1 SUPPORT
319              
320             You can find documentation for this module with the perldoc command.
321              
322             perldoc Date::Utils
323              
324             You can also look for information at:
325              
326             =over 4
327              
328             =item * RT: CPAN's request tracker
329              
330             L
331              
332             =item * AnnoCPAN: Annotated CPAN documentation
333              
334             L
335              
336             =item * CPAN Ratings
337              
338             L
339              
340             =item * Search CPAN
341              
342             L
343              
344             =back
345              
346             =head1 LICENSE AND COPYRIGHT
347              
348             Copyright (C) 2015 - 2017 Mohammad S Anwar.
349              
350             This program is free software; you can redistribute it and / or modify it under
351             the terms of the the Artistic License (2.0). You may obtain a copy of the full
352             license at:
353              
354             L
355              
356             Any use, modification, and distribution of the Standard or Modified Versions is
357             governed by this Artistic License.By using, modifying or distributing the Package,
358             you accept this license. Do not use, modify, or distribute the Package, if you do
359             not accept this license.
360              
361             If your Modified Version has been derived from a Modified Version made by someone
362             other than you,you are nevertheless required to ensure that your Modified Version
363             complies with the requirements of this license.
364              
365             This license does not grant you the right to use any trademark, service mark,
366             tradename, or logo of the Copyright Holder.
367              
368             This license includes the non-exclusive, worldwide, free-of-charge patent license
369             to make, have made, use, offer to sell, sell, import and otherwise transfer the
370             Package with respect to any patent claims licensable by the Copyright Holder that
371             are necessarily infringed by the Package. If you institute patent litigation
372             (including a cross-claim or counterclaim) against any party alleging that the
373             Package constitutes direct or contributory patent infringement,then this Artistic
374             License to you shall terminate on the date that such litigation is filed.
375              
376             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND
377             CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
378             WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
379             NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS
380             REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT,
381             INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE
382             OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
383              
384             =cut
385              
386             1; # End of Date::Utils