File Coverage

blib/lib/Date/Julian/Simple.pm
Criterion Covered Total %
statement 102 112 91.0
branch 22 42 52.3
condition 4 17 23.5
subroutine 20 20 100.0
pod 8 12 66.6
total 156 203 76.8


line stmt bran cond sub pod time code
1             package Date::Julian::Simple;
2              
3             $Date::Julian::Simple::VERSION = '0.17';
4             $Date::Julian::Simple::AUTHORITY = 'cpan:MANWAR';
5              
6             =head1 NAME
7              
8             Date::Julian::Simple - Represents Julian date.
9              
10             =head1 VERSION
11              
12             Version 0.17
13              
14             =cut
15              
16 2     2   161397 use 5.006;
  2         16  
17 2     2   1283 use Data::Dumper;
  2         15633  
  2         131  
18 2     2   915 use Time::localtime;
  2         11839  
  2         124  
19 2     2   1062 use POSIX qw/ceil floor/;
  2         13357  
  2         11  
20 2     2   3983 use Date::Exception::InvalidDay;
  2         159525  
  2         76  
21              
22 2     2   17 use Moo;
  2         4  
  2         10  
23 2     2   657 use namespace::autoclean;
  2         6  
  2         13  
24              
25 2     2   172 use overload q{""} => 'as_string', fallback => 1;
  2         5  
  2         10  
26              
27             =head1 DESCRIPTION
28              
29             Represents the Julian date.
30              
31             =cut
32              
33             our $MJD_CONST = 2400000.5;
34              
35             our $JULIAN_MONTHS = [
36             undef,
37             qw(January February March April May June
38             July August September October November December)
39             ];
40              
41             our $JULIAN_DAYS = [
42             qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
43             ];
44              
45             our $JULIAN_MONTH_DAYS = [
46             qw(31 28 31 30 31 30
47             31 31 30 31 30 31)
48             ];
49              
50             has julian_epoch => (is => 'ro', default => sub { 1721423.5 });
51             has days => (is => 'ro', default => sub { $JULIAN_DAYS });
52             has months => (is => 'ro', default => sub { $JULIAN_MONTHS });
53              
54             has year => (is => 'rw', predicate => 1);
55             has month => (is => 'rw', predicate => 1);
56             has day => (is => 'rw', predicate => 1);
57              
58             with 'Date::Utils';
59              
60             sub BUILD {
61 5     5 0 26 my ($self) = @_;
62              
63 5 50 33     42 if ($self->has_year && $self->has_month && $self->has_day) {
      33        
64 5         20 $self->validate_year($self->year);
65 5         104 $self->validate_month($self->month);
66 5 100       99 if ($self->is_leap_year($self->year)) {
67 2         6 my $day = $self->day;
68 2         14 my @caller = caller(0);
69 2 50       6 @caller = caller(2) if $caller[3] eq '(eval)';
70              
71 2 50       17 return if ($self->month != 2);
72              
73 0 0 0     0 Date::Exception::InvalidDay->throw({
    0 0        
      0        
74             method => __PACKAGE__."::validate_day",
75             message => sprintf("ERROR: Invalid day [%s].", defined($day)?($day):('')),
76             filename => $caller[1],
77             line_number => $caller[2] })
78             unless (defined($day) && ($day =~ /^\d+$/) && ($day >= 1) && ($day <= 29));
79             }
80             else {
81 3         11 $self->validate_day($self->day);
82             }
83             }
84             else {
85 0         0 my $today = localtime;
86 0         0 my $year = $today->year + 1900;
87 0         0 my $month = $today->mon + 1;
88 0         0 my $day = $today->mday;
89 0         0 $self->year($year);
90 0         0 $self->month($month);
91 0         0 $self->day($day);
92             }
93             }
94              
95             =head1 SYNOPSIS
96              
97             use strict; use warnings;
98             use Date::Julian::Simple;
99              
100             # prints today's Julian date.
101             print Date::Julian::Simple->new, "\n";
102              
103             =head1 METHODS
104              
105             =head2 to_julian()
106              
107             Returns julian day equivalent of the Julian date.
108              
109             =cut
110              
111             sub to_julian {
112 3     3 1 823 my ($self, $year, $month, $day) = @_;
113              
114 3 50       14 $day = $self->day unless defined $day;
115 3 50       10 $month = $self->month unless defined $month;
116 3 50       10 $year = $self->year unless defined $year;
117              
118             # Adjust negative common era years to the zero-based notation we use.
119 3 50       10 if ($year < 1) {
120 0         0 $year++;
121             }
122              
123             # Algorithm as given in Meeus, Astronomical Algorithms, Chapter 7, page 61.
124 3 50       8 if ($month <= 2) {
125 3         5 $year--;
126 3         5 $month += 12;
127             }
128              
129 3         29 return ((floor((365.25 * ($year + 4716))) + floor((30.6001 * ($month + 1))) + $day) - 1524.5);
130             }
131              
132             =head2 to_modified_julian()
133              
134             Returns modified julian day equivalent of the Julian date.
135              
136             =cut
137              
138             sub to_modified_julian {
139 1     1 1 5 my ($self, $year, $month, $day) = @_;
140              
141 1 50       7 $day = $self->day unless defined $day;
142 1 50       5 $month = $self->month unless defined $month;
143 1 50       4 $year = $self->year unless defined $year;
144              
145 1         4 $year = floor($year);
146 1         4 $month = floor($month);
147 1         2 $day = floor($day);
148              
149 1         5 my $L = ceil(($month - 14) / 12);
150 1         6 my $p1 = $day - 32075 + floor(1461 * ($year + 4800 + $L) / 4);
151 1         4 my $p2 = floor (367 * ($month - 2 - $L * 12) / 12);
152 1         7 my $p3 = 3 * floor(floor(($year + 4900 + $L) / 100) / 4);
153              
154 1         6 return ($p1 + $p2 - $p3 - 0.5) - $MJD_CONST;
155             }
156              
157             =head2 from_julian($julian_day)
158              
159             Returns Julian date as an object of type L equivalent of the
160             C<$julian_day>.
161              
162             =cut
163              
164             sub from_julian {
165 2     2 1 1645 my ($self, $julian_day) = @_;
166              
167 2         5 $julian_day += 0.5;
168 2         6 my $a = floor($julian_day);
169 2         6 my $b = $a + 1524;
170 2         6 my $c = floor(($b - 122.1) / 365.25);
171 2         5 my $d = floor(365.25 * $c);
172 2         7 my $e = floor(($b - $d) / 30.6001);
173              
174 2 50       9 my $month = floor(($e < 14) ? ($e - 1) : ($e - 13));
175 2 50       8 my $year = floor(($month > 2) ? ($c - 4716) : ($c - 4715));
176 2         7 my $day = $b - $d - floor(30.6001 * $e);
177              
178             # If year is less than 1, subtract one to convert from
179             # a zero based date system to the common era system in
180             # which the year -1 (1 B.C.E) is followed by year 1 (1 C.E)
181 2 50       7 if ($year < 1) {
182 0         0 $year--;
183             }
184              
185 2         52 return Date::Julian::Simple->new({ year => $year, month => $month, day => $day });
186             }
187              
188             =head2 from_modified_julian($modified_julian_day)
189              
190             Returns Julian date as an object of type L equivalent of the
191             C<$modified_julian_day>.
192              
193             =cut
194              
195             sub from_modified_julian {
196 1     1 1 7 my ($self, $modified_julian_day) = @_;
197              
198 1         5 my $jd = floor($modified_julian_day) + $MJD_CONST;
199 1         4 my $jdi = floor($jd);
200 1         2 my $jdf = $jd - $jdi + 0.5;
201              
202 1 50       4 if ($jdf >= 1.0) {
203 1         3 $jdf = $jdf - 1.0;
204 1         2 $jdi = $jdi + 1;
205             }
206              
207 1         3 my $hour = $jdf * 24.0;
208 1         2 my $l = $jdi + 68569;
209 1         4 my $n = floor(4 * $l / 146097);
210              
211 1         6 $l = floor($l) - floor((146097 * $n + 3) / 4);
212 1         4 my $year = floor(4000 * ($l + 1) / 1461001);
213              
214 1         4 $l = $l - (floor(1461 * $year / 4)) + 31;
215 1         3 my $month = floor(80 * $l / 2447);
216              
217 1         5 my $day = $l - floor(2447 * $month / 80);
218              
219 1         2 $l = floor($month / 11);
220              
221 1         4 $month = floor($month + 2 - 12 * $l);
222 1         3 $year = floor(100 * ($n - 49) + $year + $l);
223              
224 1         23 return Date::Julian::Simple->new({ year => $year, month => $month, day => $day });
225             }
226              
227             =head2 to_gregorian()
228              
229             Returns Gregorian date as list (yyyy,mm,dd) equivalent of the Julian date.
230              
231             =cut
232              
233             sub to_gregorian {
234 1     1 1 560 my ($self) = @_;
235              
236 1         3 return $self->julian_to_gregorian($self->to_julian);
237             }
238              
239             =head2 from_gregorian($year, $month, $day)
240              
241             Returns Julian date as an object of type L equivalent of the
242             given Gregorian date C<$year>, C<$month> and C<$day>.
243              
244             =cut
245              
246             sub from_gregorian {
247 1     1 1 643 my ($self, $year, $month, $day) = @_;
248              
249 1         6 $self->validate_date($year, $month, $day);
250 1         60 return $self->from_julian($self->gregorian_to_julian($year, $month, $day));
251             }
252              
253             =head2 day_of_week()
254              
255             Returns day of the week, starting 0 for Sunday, 1 for Monday and so on.
256              
257             +-------+-------------------------------------------------------------------+
258             | Index | English Name |
259             +-------+-------------------------------------------------------------------+
260             | 0 | Sunday |
261             | 1 | Monday |
262             | 2 | Tuesday |
263             | 3 | Wednesday |
264             | 4 | Thursday |
265             | 5 | Friday |
266             | 6 | Saturday |
267             +-------+-------------------------------------------------------------------+
268              
269             =cut
270              
271             sub day_of_week {
272 1     1 1 3 my ($self) = @_;
273              
274 1         4 return $self->jwday($self->to_julian);
275             }
276              
277             =head2 is_leap_year($year)
278              
279             Returns 0 or 1 if the given Julian year C<$year> is a leap year or not.
280              
281             =cut
282              
283             sub is_leap_year {
284 15     15 1 33 my ($self, $year) = @_;
285              
286 15   100     93 return (($year % 4) == (($year > 0) ? 0 : 3)) || 0;
287             }
288              
289             sub days_in_year {
290 3     3 0 7 my ($self, $year) = @_;
291              
292 3 100       8 ($self->is_leap_year($year))
293             ?
294             (return 366)
295             :
296             (return 365);
297             }
298              
299             sub days_in_month_year {
300 4     4 0 11 my ($self, $month, $year) = @_;
301              
302 4 100       11 if ($self->is_leap_year($year)) {
303 2 50       11 return 29 if ($month == 2);
304             }
305              
306 2         12 return $JULIAN_MONTH_DAYS->[$month-1];
307             }
308              
309             sub as_string {
310 2     2 0 267 my ($self) = @_;
311              
312 2         10 return sprintf("%d, %s %d", $self->day, $self->get_month_name, $self->year);
313             }
314              
315             =head1 AUTHOR
316              
317             Mohammad S Anwar, C<< >>
318              
319             =head1 REPOSITORY
320              
321             L
322              
323             =head1 SEE ALSO
324              
325             =over 4
326              
327             =item L
328              
329             =item L
330              
331             =item L
332              
333             =item L
334              
335             =item L
336              
337             =item L
338              
339             =back
340              
341             =head1 BUGS
342              
343             Please report any bugs / feature requests to C,
344             or through the web interface at L.
345             I will be notified, and then you'll automatically be notified of progress on your
346             bug as I make changes.
347              
348             =head1 SUPPORT
349              
350             You can find documentation for this module with the perldoc command.
351              
352             perldoc Date::Julian::Simple
353              
354             You can also look for information at:
355              
356             =over 4
357              
358             =item * RT: CPAN's request tracker
359              
360             L
361              
362             =item * AnnoCPAN: Annotated CPAN documentation
363              
364             L
365              
366             =item * CPAN Ratings
367              
368             L
369              
370             =item * Search CPAN
371              
372             L
373              
374             =back
375              
376             =head1 LICENSE AND COPYRIGHT
377              
378             Copyright (C) 2017 Mohammad S Anwar.
379              
380             This program is free software; you can redistribute it and / or modify it under
381             the terms of the the Artistic License (2.0). You may obtain a copy of the full
382             license at:
383              
384             L
385              
386             Any use, modification, and distribution of the Standard or Modified Versions is
387             governed by this Artistic License.By using, modifying or distributing the Package,
388             you accept this license. Do not use, modify, or distribute the Package, if you do
389             not accept this license.
390              
391             If your Modified Version has been derived from a Modified Version made by someone
392             other than you,you are nevertheless required to ensure that your Modified Version
393             complies with the requirements of this license.
394              
395             This license does not grant you the right to use any trademark, service mark,
396             tradename, or logo of the Copyright Holder.
397              
398             This license includes the non-exclusive, worldwide, free-of-charge patent license
399             to make, have made, use, offer to sell, sell, import and otherwise transfer the
400             Package with respect to any patent claims licensable by the Copyright Holder that
401             are necessarily infringed by the Package. If you institute patent litigation
402             (including a cross-claim or counterclaim) against any party alleging that the
403             Package constitutes direct or contributory patent infringement,then this Artistic
404             License to you shall terminate on the date that such litigation is filed.
405              
406             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND
407             CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
408             WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
409             NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS
410             REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT,
411             INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE
412             OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
413              
414             =cut
415              
416             1; # End of Date::Julian::Simple