File Coverage

blib/lib/Date/Persian/Simple.pm
Criterion Covered Total %
statement 70 82 85.3
branch 12 22 54.5
condition 2 6 33.3
subroutine 17 17 100.0
pod 6 9 66.6
total 107 136 78.6


line stmt bran cond sub pod time code
1             package Date::Persian::Simple;
2              
3             $Date::Persian::Simple::VERSION = '0.23';
4             $Date::Persian::Simple::AUTHORITY = 'cpan:MANWAR';
5              
6             =head1 NAME
7              
8             Date::Persian::Simple - Represents Persian date.
9              
10             =head1 VERSION
11              
12             Version 0.23
13              
14             =cut
15              
16 2     2   129428 use 5.006;
  2         15  
17 2     2   1231 use Data::Dumper;
  2         12873  
  2         122  
18 2     2   831 use Time::localtime;
  2         9003  
  2         109  
19 2     2   957 use POSIX qw/floor ceil/;
  2         12309  
  2         11  
20 2     2   3631 use Date::Calc qw/Delta_Days/;
  2         14637  
  2         149  
21              
22 2     2   1068 use Moo;
  2         21288  
  2         8  
23 2     2   3678 use namespace::autoclean;
  2         24775  
  2         19  
24              
25 2     2   125 use overload q{""} => 'as_string', fallback => 1;
  2         4  
  2         14  
26              
27             =head1 DESCRIPTION
28              
29             Represents the Persian date.
30              
31             =cut
32              
33             our $PERSIAN_MONTHS = [
34             '',
35             'Farvardin', 'Ordibehesht', 'Khordad', 'Tir', 'Mordad', 'Shahrivar',
36             'Mehr' , 'Aban' , 'Azar' , 'Dey', 'Bahman', 'Esfand'
37             ];
38              
39             our $PERSIAN_DAYS = [
40             'Yekshanbeh', 'Doshanbeh', 'Seshhanbeh', 'Chaharshanbeh',
41             'Panjshanbeh', 'Jomeh', 'Shanbeh'
42             ];
43              
44             has persian_epoch => (is => 'ro', default => sub { 1948320.5 });
45             has days => (is => 'ro', default => sub { $PERSIAN_DAYS });
46             has months => (is => 'ro', default => sub { $PERSIAN_MONTHS });
47              
48             has year => (is => 'rw', predicate => 1);
49             has month => (is => 'rw', predicate => 1);
50             has day => (is => 'rw', predicate => 1);
51              
52             with 'Date::Utils';
53              
54             sub BUILD {
55 11     11 0 44 my ($self) = @_;
56              
57 11 50       62 $self->validate_year($self->year) if $self->has_year;
58 11 50       179 $self->validate_month($self->month) if $self->has_month;
59 11 50       179 $self->validate_day($self->day) if $self->has_day;
60              
61 11 50 33     193 unless ($self->has_year && $self->has_month && $self->has_day) {
      33        
62 0         0 my $today = localtime;
63 0         0 my $year = $today->year + 1900;
64 0         0 my $month = $today->mon + 1;
65 0         0 my $day = $today->mday;
66 0         0 my $date = $self->from_gregorian($year, $month, $day);
67 0         0 $self->year($date->year);
68 0         0 $self->month($date->month);
69 0         0 $self->day($date->day);
70             }
71             }
72              
73             =head1 SYNOPSIS
74              
75             use strict; use warnings;
76             use Date::Persian::Simple;
77              
78             # prints today's Persian date.
79             print Date::Persian::Simple->new, "\n";
80              
81             # prints the given Persian date.
82             print Date::Persian::Simple->new({ year => 1394, month => 1, day => 1 })->as_string;
83              
84             # prints the equivalent Julian date.
85             print $date->to_julian, "\n";
86              
87             # prints the equivalent Gregorian date.
88             print sprintf("%04d-%02d-%02d", $date->to_gregorian), "\n";
89              
90             # prints the equivalent Persian date of the given Julian date
91             print $date->from_julian(2455538.5), "\n";
92              
93             # prints the equivalent Persian date of the Gregorian date.
94             print $date->from_gregorian(2015, 6, 25), "\n";
95              
96             # prints day of the week index (0 for Yekshanbeh, 1 for Doshanbehl and so on.
97             print $date->day_of_week, "\n";
98              
99             =head1 METHODS
100              
101             =head2 to_julian()
102              
103             Returns julian date equivalent of the Bahai date.
104              
105             =cut
106              
107             sub to_julian {
108 11     11 1 640 my ($self) = @_;
109              
110 11 50       29 my $epbase = $self->year - (($self->year >= 0) ? 474 : 473);
111 11         22 my $epyear = 474 + ($epbase % 2820);
112              
113 11 100       98 return $self->day + (($self->month <= 7)?(($self->month - 1) * 31):((($self->month - 1) * 30) + 6)) +
114             floor((($epyear * 682) - 110) / 2816) +
115             ($epyear - 1) * 365 +
116             floor($epbase / 2820) * 1029983 +
117             ($self->persian_epoch - 1);
118             }
119              
120             =head2 from_julian($julian_date)
121              
122             Returns Persian date as an object of type L equivalent of
123             the Julian date C<$julian_date>.
124              
125             =cut
126              
127             sub from_julian {
128 2     2 1 7188 my ($self, $julian_date) = @_;
129              
130 2         5 $julian_date = floor($julian_date) + 0.5;
131 2         58 my $depoch = $julian_date - Date::Persian::Simple->new({ year => 475, month => 1, day => 1 })->to_julian;
132 2         13 my $cycle = floor($depoch / 1029983);
133 2         5 my $cyear = $depoch % 1029983;
134              
135 2         2 my $ycycle;
136 2 50       6 if ($cyear == 1029982) {
137 0         0 $ycycle = 2820;
138             }
139             else {
140 2         4 my $aux1 = floor($cyear / 366);
141 2         3 my $aux2 = $cyear % 366;
142 2         6 $ycycle = floor(((2134 * $aux1) + (2816 * $aux2) + 2815) / 1028522) + $aux1 + 1;
143             }
144              
145 2         18 my $year = $ycycle + (2820 * $cycle) + 474;
146 2 50       6 if ($year <= 0) {
147 0         0 $year--;
148             }
149              
150 2         36 my $a_persian = Date::Persian::Simple->new({ year => $year, month => 1, day => 1 });
151 2         6 my $yday = ($julian_date - $a_persian->to_julian) + 1;
152 2 50       9 my $month = ($yday <= 186) ? ceil($yday / 31) : ceil(($yday - 6) / 30);
153 2         33 my $b_persian = Date::Persian::Simple->new({ year => $year, month => $month, day => 1 });
154 2         6 my $day = ($julian_date - $b_persian->to_julian) + 1;
155              
156 2         44 return Date::Persian::Simple->new({ year => $year, month => $month, day => $day });
157             }
158              
159             =head2 to_gregorian()
160              
161             Returns gregorian date as list (yyyy,mm,dd) equivalent of the Persian date.
162              
163             =cut
164              
165             sub to_gregorian {
166 3     3 1 466 my ($self) = @_;
167              
168 3         7 return $self->julian_to_gregorian($self->to_julian);
169             }
170              
171             =head2 from_gregorian($year, $month, $day)
172              
173             Returns Persian date as an object of type L equivalent of
174             the given Gregorian date C<$year>, C<$month> and C<$day>.
175              
176             =cut
177              
178             sub from_gregorian {
179 1     1 1 1351 my ($self, $year, $month, $day) = @_;
180              
181 1         5 $self->validate_date($year, $month, $day);
182 1         43 my $julian = $self->gregorian_to_julian($year, $month, $day) +
183             (floor(0 + 60 * (0 + 60 * 0) + 0.5) / 86400.0);
184 1         47 return $self->from_julian($julian);
185             }
186              
187             =head2 day_of_week()
188              
189             Returns day of the week, starting 0 for Yekshanbeh, 1 for Doshanbehl and so on.
190              
191             +-------+---------------+---------------------------------------------------+
192             | Index | Persian Name | English Name |
193             +-------+---------------+---------------------------------------------------+
194             | 0 | Yekshanbeh | Sunday |
195             | 1 | Doshanbeh | Monday |
196             | 2 | Seshhanbeh | Tuesday |
197             | 3 | Chaharshanbeh | Wednesday |
198             | 4 | Panjshanbeh | Thursday |
199             | 5 | Jomeh | Friday |
200             | 6 | Shanbeh | Saturday |
201             +-------+---------------+---------------------------------------------------+
202              
203             =cut
204              
205             sub day_of_week {
206 1     1 1 3 my ($self) = @_;
207              
208 1         2 return $self->jwday($self->to_julian);
209             }
210              
211             =head2 is_leap_year($year)
212              
213             Returns 0 or 1 if the given Persian year C<$year> is a leap year or not.
214              
215             =cut
216              
217             sub is_leap_year {
218 1     1 1 4 my ($self, $year) = @_;
219              
220 1 50       9 return (((((($year - (($year > 0) ? 474 : 473)) % 2820) + 474) + 38) * 682) % 2816) < 682;
221             }
222              
223             sub days_in_month_year {
224 1     1 0 1361 my ($self, $month, $year) = @_;
225              
226 1         5 $self->validate_year($year);
227 1         16 $self->validate_month($month);
228              
229 1         32 my @start = Date::Persian::Simple->new({
230             year => $year,
231             month => $month,
232             day => 1 })->to_gregorian;
233              
234 1 50       51 if ($month == 12) {
235 0         0 $year += 1;
236 0         0 $month = 1;
237             }
238             else {
239 1         3 $month += 1;
240             }
241              
242 1         19 my @end = Date::Persian::Simple->new({
243             year => $year,
244             month => $month,
245             day => 1 })->to_gregorian;
246              
247 1         67 return Delta_Days(@start, @end);
248             }
249              
250             sub as_string {
251 1     1 0 6 my ($self) = @_;
252              
253 1         4 return sprintf("%d, %s %d", $self->day, $self->get_month_name, $self->year);
254             }
255              
256             =head1 AUTHOR
257              
258             Mohammad S Anwar, C<< >>
259              
260             =head1 REPOSITORY
261              
262             L
263              
264             =head1 SEE ALSO
265              
266             =over 4
267              
268             =item L
269              
270             =item L
271              
272             =item L
273              
274             =item L
275              
276             =item L
277              
278             =item L
279              
280             =back
281              
282             =head1 BUGS
283              
284             Please report any bugs / feature requests to C,
285             or through the web interface at L.
286             I will be notified, and then you'll automatically be notified of progress on your
287             bug as I make changes.
288              
289             =head1 SUPPORT
290              
291             You can find documentation for this module with the perldoc command.
292              
293             perldoc Date::Persian::Simple
294              
295             You can also look for information at:
296              
297             =over 4
298              
299             =item * RT: CPAN's request tracker
300              
301             L
302              
303             =item * AnnoCPAN: Annotated CPAN documentation
304              
305             L
306              
307             =item * CPAN Ratings
308              
309             L
310              
311             =item * Search CPAN
312              
313             L
314              
315             =back
316              
317             =head1 LICENSE AND COPYRIGHT
318              
319             Copyright (C) 2015 - 2017 Mohammad S Anwar.
320              
321             This program is free software; you can redistribute it and / or modify it under
322             the terms of the the Artistic License (2.0). You may obtain a copy of the full
323             license at:
324              
325             L
326              
327             Any use, modification, and distribution of the Standard or Modified Versions is
328             governed by this Artistic License.By using, modifying or distributing the Package,
329             you accept this license. Do not use, modify, or distribute the Package, if you do
330             not accept this license.
331              
332             If your Modified Version has been derived from a Modified Version made by someone
333             other than you,you are nevertheless required to ensure that your Modified Version
334             complies with the requirements of this license.
335              
336             This license does not grant you the right to use any trademark, service mark,
337             tradename, or logo of the Copyright Holder.
338              
339             This license includes the non-exclusive, worldwide, free-of-charge patent license
340             to make, have made, use, offer to sell, sell, import and otherwise transfer the
341             Package with respect to any patent claims licensable by the Copyright Holder that
342             are necessarily infringed by the Package. If you institute patent litigation
343             (including a cross-claim or counterclaim) against any party alleging that the
344             Package constitutes direct or contributory patent infringement,then this Artistic
345             License to you shall terminate on the date that such litigation is filed.
346              
347             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND
348             CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
349             WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
350             NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS
351             REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT,
352             INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE
353             OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
354              
355             =cut
356              
357             1; # End of Date::Persian::Simple