File Coverage

blib/lib/Date/Bahai/Simple.pm
Criterion Covered Total %
statement 109 109 100.0
branch 27 36 75.0
condition 17 33 51.5
subroutine 24 24 100.0
pod 5 13 38.4
total 182 215 84.6


line stmt bran cond sub pod time code
1             package Date::Bahai::Simple;
2              
3             $Date::Bahai::Simple::VERSION = '0.28';
4             $Date::Bahai::Simple::AUTHORITY = 'cpan:MANWAR';
5              
6             =head1 NAME
7              
8             Date::Bahai::Simple - Represents Bahai date.
9              
10             =head1 VERSION
11              
12             Version 0.28
13              
14             =cut
15              
16 2     2   246399 use 5.006;
  2         15  
17 2     2   1096 use Data::Dumper;
  2         12173  
  2         101  
18 2     2   944 use Time::localtime;
  2         7991  
  2         93  
19 2     2   11 use POSIX qw/floor/;
  2         4  
  2         14  
20 2     2   3624 use Astro::Utils;
  2         859040  
  2         143  
21 2     2   1002 use Date::Exception::InvalidDay;
  2         69077  
  2         115  
22 2     2   855 use Date::Exception::InvalidMonth;
  2         4581  
  2         53  
23              
24 2     2   12 use Moo;
  2         11  
  2         7  
25 2     2   588 use namespace::autoclean;
  2         5  
  2         9  
26              
27 2     2   160 use overload q{""} => 'as_string', fallback => 1;
  2         4  
  2         12  
28              
29             =head1 DESCRIPTION
30              
31             Represents the Bahai date.
32              
33             =cut
34              
35             our $BAHAI_MONTHS = [
36             '',
37             'Baha', 'Jalal', 'Jamal', 'Azamat', 'Nur', 'Rahmat',
38             'Kalimat', 'Kamal', 'Asma', 'Izzat', 'Mashiyyat', 'Ilm',
39             'Qudrat', 'Qawl', 'Masail', 'Sharaf', 'Sultan', 'Mulk',
40             'Ala'
41             ];
42              
43             our $BAHAI_CYCLES = [
44             '',
45             'Alif', 'Ba', 'Ab', 'Dal', 'Bab', 'Vav',
46             'Abad', 'Jad', 'Baha', 'Hubb', 'Bahhaj', 'Javab',
47             'Ahad', 'Vahhab', 'Vidad', 'Badi', 'Bahi', 'Abha',
48             'Vahid'
49             ];
50              
51             our $BAHAI_DAYS = [
52             'Jamal', 'Kamal', 'Fidal', 'Idal', 'Istijlal', 'Istiqlal', 'Jalal'
53             ];
54              
55             has bahai_epoch => (is => 'ro', default => sub { 2394646.5 });
56             has days => (is => 'ro', default => sub { $BAHAI_DAYS });
57             has months => (is => 'ro', default => sub { $BAHAI_MONTHS });
58             has bahai_cycles => (is => 'ro', default => sub { $BAHAI_CYCLES });
59              
60             has major => (is => 'rw');
61             has cycle => (is => 'rw');
62             has year => (is => 'rw', predicate => 1);
63             has month => (is => 'rw', predicate => 1);
64             has day => (is => 'rw', predicate => 1);
65              
66             with 'Date::Utils';
67              
68             sub BUILD {
69 22     22 0 88 my ($self) = @_;
70              
71 22 100       88 $self->validate_day($self->day) if $self->has_day;
72 22 100       71 $self->validate_month($self->month) if $self->has_month;
73 22 100       85 $self->validate_year($self->year) if $self->has_year;
74              
75 22 100 66     334 unless ($self->has_year && $self->has_month && $self->has_day) {
      66        
76 2         8 my $today = localtime;
77 2         317 my $year = $today->year + 1900;
78 2         35 my $month = $today->mon + 1;
79 2         31 my $day = $today->mday;
80 2         15 my $date = $self->from_gregorian($year, $month, $day);
81 2         9 $self->major($date->major);
82 2         7 $self->cycle($date->cycle);
83 2         6 $self->year($date->year);
84 2         19 $self->month($date->month);
85 2         28 $self->day($date->day);
86             }
87             }
88              
89             =head1 SYNOPSIS
90              
91             use strict; use warnings;
92             use Date::Bahai::Simple;
93              
94             # prints today's Bahai date.
95             print Date::Bahai::Simple->new->as_string, "\n";
96              
97             my $date = Date::Bahai::Simple->new({ major => 1, cycle => 10, year => 1, month => 1, day => 1 });
98              
99             # print given Bahai date.
100             print $date->as_string, "\n";
101              
102             # prints equivalent Julian date.
103             print $date->to_julian, "\n";
104              
105             # prints equivalent Gregorian date.
106             print sprintf("%04d-%02d-%02d", $date->to_gregorian), "\n";
107              
108             # prints day of the week index (0 for Jamal, 1 for Kamal and so on).
109             print $date->day_of_week, "\n";
110              
111             # prints equivalent Bahai date of the given Gregorian date.
112             print $date->from_gregorian(2015, 4, 16), "\n";
113              
114             # prints equivalent Bahai date of the given Julian date.
115             print $date->from_julian(2457102.5), "\n";
116              
117             =head1 METHODS
118              
119             =head2 to_julian()
120              
121             Returns julian date equivalent of the Bahai date.
122              
123             =cut
124              
125             sub to_julian {
126 17     17 1 2422 my ($self) = @_;
127              
128 17         46 my ($g_year) = $self->julian_to_gregorian($self->bahai_epoch);
129 17         846 my ($gm, $gd) = _vernal_equinox_month_day($g_year);
130 17         43 my $gy = (361 * ($self->major - 1)) +
131             (19 * ($self->cycle - 1)) +
132             ($self->year - 1) + $g_year;
133              
134 17 50       38 return $self->gregorian_to_julian($gy, $gm, $gd)
    100          
135             +
136             (19 * ($self->month - 1))
137             +
138             (($self->month != 20) ? 0 : ($self->is_gregorian_leap_year($gy + 1) ? -14 : -15))
139             +
140             $self->day;
141             }
142              
143             =head2 from_julian($julian_date)
144              
145             Returns Bahai date as an object of type L<Date::Bahai::Simple> equivalent of the
146             given Julian date C<$julian_date>.
147              
148             =cut
149              
150             sub from_julian {
151 4     4 1 45 my ($self, $julian_date) = @_;
152              
153 4         10 $julian_date = floor($julian_date) + 0.5;
154 4         11 my $gregorian_year = ($self->julian_to_gregorian($julian_date))[0];
155 4         174 my $start_year = ($self->julian_to_gregorian($self->bahai_epoch))[0];
156              
157 4         159 my $j1 = $self->gregorian_to_julian($gregorian_year, 1, 1);
158 4         24 my ($gm, $gd) = _vernal_equinox_month_day($gregorian_year);
159 4         28 my $j2 = $self->gregorian_to_julian($gregorian_year, $gm, $gd);
160              
161 4 50 33     74 my $bahai_year = $gregorian_year - ($start_year + ((($j1 <= $julian_date) && ($julian_date <= $j2)) ? 1 : 0));
162 4         9 my ($major, $cycle, $year) = $self->get_major_cycle_year($bahai_year);
163              
164 4         88 my $b_date1 = Date::Bahai::Simple->new({
165             major => $major, cycle => $cycle, year => $year, month => 1, day => 1 });
166 4         12 my $days = $julian_date - $b_date1->to_julian;
167              
168 4         116 my $b_date2 = Date::Bahai::Simple->new({
169             major => $major, cycle => $cycle, year => $year, month => 20, day => 1 });
170 4         12 my $bld = $b_date2->to_julian;
171 4 50       67 my $month = ($julian_date >= $bld) ? 20 : (floor($days / 19) + 1);
172              
173 4         73 my $b_date3 = Date::Bahai::Simple->new({
174             major => $major, cycle => $cycle, year => $year, month => $month, day => 1 });
175 4         11 my $day = ($julian_date + 1) - $b_date3->to_julian;
176              
177 4         107 return Date::Bahai::Simple->new({
178             major => $major, cycle => $cycle, year => $year, month => $month, day => $day });
179             }
180              
181             =head2 to_gregorian()
182              
183             Returns gregorian date (yyyy,mm,dd) equivalent of the Bahai date.
184              
185             =cut
186              
187             sub to_gregorian {
188 2     2 1 453 my ($self) = @_;
189              
190 2         6 return $self->julian_to_gregorian($self->to_julian);
191             }
192              
193             =head2 from_gregorian($year, $month, $day)
194              
195             Returns Bahai date as an object of type L<Date::Bahai::Simple> equivalent of the
196             given gregorian C<$year>, C<$month> and C<$day>.
197              
198             =cut
199              
200             sub from_gregorian {
201 3     3 1 9 my ($self, $year, $month, $day) = @_;
202              
203 3         10 return $self->from_julian($self->gregorian_to_julian($year, $month, $day));
204             }
205              
206             =head2 day_of_week()
207              
208             Returns day of the week, starting 0 for Jamal, 1 for Kamal and so on.
209              
210             +-------------+--------------+----------------------------------------------+
211             | Arabic Name | English Name | Day of the Week |
212             +-------------+--------------+----------------------------------------------+
213             | Jamal | Beauty | Sunday |
214             | Kamal | Perfection | Monday |
215             | Fidal | Grace | Tuesday |
216             | Idal | Justice | Wednesday |
217             | Istijlal | Majesty | Thursday |
218             | Istiqlal | Independence | Friday |
219             | Jalal | Glory | Saturday |
220             +-------------+--------------+----------------------------------------------+
221              
222             =cut
223              
224             sub day_of_week {
225 1     1 1 2 my ($self) = @_;
226              
227 1         4 return $self->jwday($self->to_julian);
228             }
229              
230             sub get_year {
231 2     2 0 13 my ($self) = @_;
232              
233 2         28 return ($self->major * (19 * ($self->cycle - 1))) + $self->year;
234             }
235              
236             sub get_date {
237 2     2 0 5 my ($self, $day, $month, $year) = @_;
238              
239 2         6 $self->validate_day($day);
240 2         5 $self->validate_month($month);
241 2         6 $self->validate_year($year);
242              
243 2         25 my ($major, $cycle, $bahai_year) = $self->get_major_cycle_year($year - 1);
244              
245 2         45 return Date::Bahai::Simple->new({
246             major => $major,
247             cycle => $cycle,
248             year => $bahai_year,
249             month => $month,
250             day => $day });
251             }
252              
253             sub is_same {
254 2     2 0 4 my ($self, $other) = @_;
255              
256 2 50       6 return 0 unless (ref($other) eq 'Date::Bahai::Simple');
257              
258 2   66     22 return (($self->major == $other->major)
259             &&
260             ($self->cycle == $other->cycle)
261             &&
262             ($self->year == $other->year)
263             &&
264             ($self->get_month_name eq $other->get_month_name)
265             &&
266             ($self->day == $other->day))+0;
267             }
268              
269             sub get_major_cycle_year {
270 7     7 0 522 my ($self, $bahai_year) = @_;
271              
272 7         24 my $major = floor($bahai_year / 361) + 1;
273 7         19 my $cycle = floor(($bahai_year % 361) / 19) + 1;
274 7         11 my $year = ($bahai_year % 19) + 1;
275              
276 7         18 return ($major, $cycle, $year);
277             }
278              
279             sub validate_month {
280 24     24 0 4262 my ($self, $month) = @_;
281              
282 24 100 66     83 if (defined $month && ($month =~ /[A-Z]/i)) {
283 1         6 return $self->validate_month_name($month);
284             }
285              
286 23         95 my @caller = caller(0);
287 23 50       50 @caller = caller(2) if ($caller[3] eq '(eval)');
288              
289 23 50 33     159 Date::Exception::InvalidMonth->throw({
    100 33        
      66        
290             method => __PACKAGE__."::validate_month",
291             message => sprintf("ERROR: Invalid month [%s].", defined($month)?($month):('')),
292             filename => $caller[1],
293             line_number => $caller[2] })
294             unless (defined($month) && ($month =~ /^\d{1,2}$/) && ($month >= 1) && ($month <= 20));
295             }
296              
297             sub validate_day {
298 23     23 0 1136 my ($self, $day) = @_;
299              
300 23         111 my @caller = caller(0);
301 23 50       56 @caller = caller(2) if $caller[3] eq '(eval)';
302              
303 23 50 33     217 Date::Exception::InvalidDay->throw({
    100 33        
      66        
304             method => __PACKAGE__."::validate_day",
305             message => sprintf("ERROR: Invalid day [%s].", defined($day)?($day):('')),
306             filename => $caller[1],
307             line_number => $caller[2] })
308             unless (defined($day) && ($day =~ /^\d{1,2}$/) && ($day >= 1) && ($day <= 19));
309             }
310              
311             sub as_string {
312 1     1 0 5 my ($self) = @_;
313              
314 1         5 return sprintf("%d, %s %d BE",
315             $self->day, $self->get_month_name, $self->get_year);
316             }
317              
318             #
319             #
320             # PRIVATE METHODS
321              
322             sub _vernal_equinox_month_day {
323 21     21   32 my ($year) = @_;
324              
325             # Source: Wikipedia
326             # In 2014, the Universal House of Justice selected Tehran, the birthplace of
327             # Baha'u'lláh, as the location to which the date of the vernal equinox is to
328             # be fixed, thereby "unlocking" the Badi calendar from the Gregorian calendar.
329             # For determining the dates, astronomical tables from reliable sources are
330             # used.
331             # In the same message the Universal House of Justice decided that the
332             # birthdays of the Bab and Baha'u'lláh will be celebrated on "the first and
333             # the second day following the occurrence of the eighth new moon after
334             # Naw-Ruz" (also with the use of astronomical tables) and fixed the dates of
335             # the Bahaí Holy Days in the Baha'í calendar, standardizing dates for Baha'ís
336             # worldwide. These changes came into effect as of sunset on 20 March 2015.The
337             # changes take effect from the next Bahai New Year, from sunset on March 20,
338             # 2015.
339              
340 21         27 my $month = 3;
341 21         23 my $day = 20;
342              
343 21 100       44 if ($year >= 2015) {
344 4         13 my $equinox_date = calculate_equinox('mar', 'utc', $year);
345 4 50       6447 if ($equinox_date =~ /\d{4}\-(\d{2})\-(\d{2})\s/) {
346 4         10 $month = $1;
347 4         8 $day = $2;
348             }
349             }
350              
351 21         46 return ($month, $day);
352             }
353              
354             =head1 AUTHOR
355              
356             Mohammad S Anwar, C<< <mohammad.anwar at yahoo.com> >>
357              
358             =head1 REPOSITORY
359              
360             L<https://github.com/manwar/Date-Bahai-Simple>
361              
362             =head1 SEE ALSO
363              
364             =over 4
365              
366             =item L<Date::Gregorian::Simple>
367              
368             =item L<Date::Hebrew::Simple>
369              
370             =item L<Date::Hijri::Simple>
371              
372             =item L<Date::Julian::Simple>
373              
374             =item L<Date::Persian::Simple>
375              
376             =item L<Date::Saka::Simple>
377              
378             =back
379              
380             =head1 BUGS
381              
382             Please report any bugs / feature requests to C<bug-date-bahai-simple at rt.cpan.org>,
383             or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Date-Bahai-Simple>.
384             I will be notified, and then you'll automatically be notified of progress on your
385             bug as I make changes.
386              
387             =head1 SUPPORT
388              
389             You can find documentation for this module with the perldoc command.
390              
391             perldoc Date::Bahai::Simple
392              
393             You can also look for information at:
394              
395             =over 4
396              
397             =item * RT: CPAN's request tracker
398              
399             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Date-Bahai-Simple>
400              
401             =item * AnnoCPAN: Annotated CPAN documentation
402              
403             L<http://annocpan.org/dist/Date-Bahai-Simple>
404              
405             =item * CPAN Ratings
406              
407             L<http://cpanratings.perl.org/d/Date-Bahai-Simple>
408              
409             =item * Search CPAN
410              
411             L<http://search.cpan.org/dist/Date-Bahai-Simple/>
412              
413             =back
414              
415             =head1 LICENSE AND COPYRIGHT
416              
417             Copyright (C) 2015 - 2017 Mohammad S Anwar.
418              
419             This program is free software; you can redistribute it and / or modify it under
420             the terms of the the Artistic License (2.0). You may obtain a copy of the full
421             license at:
422              
423             L<http://www.perlfoundation.org/artistic_license_2_0>
424              
425             Any use, modification, and distribution of the Standard or Modified Versions is
426             governed by this Artistic License.By using, modifying or distributing the Package,
427             you accept this license. Do not use, modify, or distribute the Package, if you do
428             not accept this license.
429              
430             If your Modified Version has been derived from a Modified Version made by someone
431             other than you,you are nevertheless required to ensure that your Modified Version
432             complies with the requirements of this license.
433              
434             This license does not grant you the right to use any trademark, service mark,
435             tradename, or logo of the Copyright Holder.
436              
437             This license includes the non-exclusive, worldwide, free-of-charge patent license
438             to make, have made, use, offer to sell, sell, import and otherwise transfer the
439             Package with respect to any patent claims licensable by the Copyright Holder that
440             are necessarily infringed by the Package. If you institute patent litigation
441             (including a cross-claim or counterclaim) against any party alleging that the
442             Package constitutes direct or contributory patent infringement,then this Artistic
443             License to you shall terminate on the date that such litigation is filed.
444              
445             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND
446             CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
447             WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
448             NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS
449             REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT,
450             INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE
451             OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
452              
453             =cut
454              
455             1; # End of Date::Bahai::Simple