File Coverage

blib/lib/DateTime/Event/Jewish/Parshah.pm
Criterion Covered Total %
statement 103 106 97.1
branch 49 56 87.5
condition 18 20 90.0
subroutine 12 12 100.0
pod 2 2 100.0
total 184 196 93.8


line stmt bran cond sub pod time code
1             package DateTime::Event::Jewish::Parshah;
2              
3             =head1 NAME
4              
5             DateTime::Event::Jewish::Parshah - Calculate leyning for next
6             shabbat
7              
8             =head1 SYNOPSIS
9              
10             use DateTime::Event::Jewish::Parshah qw(parshah);
11              
12             my $parshah = parshah(DateTime->today, $israel);
13              
14             =head1 DESCRIPTION
15              
16             Returns either a parshah name or a yom tov name for the Shabbat
17             after the date supplied. The optional I flag specifies
18             whether to calculate the leyning for Israel or for the diaspora.
19              
20              
21             =cut
22              
23 6     6   1080780 use strict;
  6         17  
  6         231  
24 6     6   30 use warnings;
  6         12  
  6         180  
25 6     6   29 use DateTime;
  6         14  
  6         138  
26 6     6   31 use DateTime::Duration;
  6         11  
  6         146  
27 6     6   84256 use DateTime::Calendar::Hebrew;
  6         37472  
  6         255  
28 6     6   4159 use DateTime::Event::Jewish::Yomtov qw(@festival);
  6         13  
  6         830  
29 6     6   3040 use DateTime::Event::Jewish::Sedrah qw(@sedrah);
  6         14  
  6         589  
30 6     6   33 use base qw(Exporter);
  6         12  
  6         369  
31 6     6   29 use vars qw(@EXPORT_OK);
  6         10  
  6         6377  
32             @EXPORT_OK = qw(nextShabbat parshah);
33             our $VERSION = '0.01';
34              
35             our %YomTovMap;
36             our %IsraelYomTovMap;
37             our %YomTovYear;
38              
39             =head3 _initYomTov($year)
40              
41             Internal function that initialises the yom tov table for the
42             year in question.
43              
44             =cut
45              
46             sub _initYomTov {
47 366     366   544 my $year = shift;
48 366 100       1229 return if $YomTovYear{$year};
49 131         629 my $f = DateTime::Calendar::Hebrew->today;
50 131         378359 foreach my $yt (@festival)
51             {
52 2882         5716 my ($name, $day, $month, $diaspora) = @$yt;
53 2882         9391 $f->set(year=>$year, month=>$month, day=>$day);
54 2882         487455 my $fDays = $f->{rd_days};
55 2882         8415 $YomTovMap{$fDays} = $name;
56 2882 100       10208 $IsraelYomTovMap{$fDays} = $name unless $diaspora;
57             }
58 131         836 $YomTovYear{$year} = 1;
59             }
60              
61              
62              
63             # (C) Raphael Mankin 2009
64              
65             # The algorithm for calculating the weekly parshah is taken
66             # from http://individual.utoronto.ca/kalendis/hebrew/parshah.htm
67              
68             =head3 nextShabbat($date)
69              
70             Returns the next Shabbat which is strictly after $date. The
71             returned object is a Hewbrew date.
72              
73             $date is some sort of DateTime object; it does not matter which.
74              
75             =cut
76              
77             sub nextShabbat {
78 244     244 1 1675 my $date = DateTime->from_object(object=>shift)->add(days=>1);
79 244         305447 while ($date->day_of_week != 6) {
80 723         294062 $date->add(days=>1);
81             }
82 244         117436 return DateTime::Calendar::Hebrew->from_object(object=>$date);
83             }
84              
85             =head3 parshah($date ,[$israel])
86              
87             Returns the parshah name or a yomtov name for the Shabbat
88             strictly after $date.
89              
90             $date is some sort of DateTime object; it does not matter which.
91              
92             $israel is an optional flag to indicate that we should use the
93             logic for Israel rather than the Diaspora.
94              
95             See http://individual.utoronto.ca/kalendis/hebrew/parshah.htm for
96             the logic of this code.
97              
98             =cut
99              
100             sub parshah {
101 122     122 1 154477 my $today = shift;
102 122   100     743 my $israel = shift || 0;
103              
104 122         435 my $targetShabbat = nextShabbat($today);
105 122         257456 my $thisYear = $targetShabbat->year();
106 122         977 _initYomTov($thisYear-1);
107 122         302 _initYomTov($thisYear);
108 122         365 _initYomTov($thisYear+1);
109 122 100       449 if ($israel) {
110 61 50       329 if (exists $IsraelYomTovMap{$targetShabbat->{rd_days}}) {
111 0         0 return $IsraelYomTovMap{$targetShabbat->{rd_days}};
112             }
113             } else {
114 61 50       302 if (exists $YomTovMap{$targetShabbat->{rd_days}}) {
115 0         0 return $YomTovMap{$targetShabbat->{rd_days}};
116             }
117             }
118 122         579 my $thisMonth = $targetShabbat->month();
119 122         955 my $thisDay = $targetShabbat->day();
120             # Get the date of last Simchat Torah, bearing in mind that
121             # the year begins in Nissan.
122 122 50 33     1327 my $simchatTorah =
123             DateTime::Calendar::Hebrew->new(month=>7,
124             day=>23-$israel,
125             year=>($thisMonth==7 && $thisDay<23-$israel )? $thisYear-1: $thisYear);
126              
127             # The next few dates only matter if we are in the right part of the year.
128             # Otherwise, the dates are not used and it is of no
129             # consequence which year we calculate. The calculation is
130             # relative to 'workingShabbat', not relative to
131             # 'targetShabbat'.
132             #
133             # The date of Pesach
134 122 50       14298 my $pesach = DateTime::Calendar::Hebrew->new(month=>1, day=>15,
135             year=>($thisMonth==7 ? $thisYear-1: $thisYear));
136             # The date of 9 Av
137 122 50       33348 my $tishaBAv = DateTime::Calendar::Hebrew->new(month=>5, day=>9,
138             year=>($thisMonth==7 ? $thisYear-1: $thisYear) );
139             # The date of RoshHashanah
140 122 50       32231 my $RoshHashanah= DateTime::Calendar::Hebrew->new(month=>7, day=>1,
141             year=>($thisMonth==7 ? $thisYear : $thisYear+1));
142 122         17503 my $workingShabbat =
143             nextShabbat($simchatTorah)->subtract_duration(DateTime::Duration->new(days=>7));
144 122         165942 my $startDay = $workingShabbat->{rd_days};
145 122         315 my $endDay = $targetShabbat->{rd_days};
146 122         281 my $parshahNumber = int(($endDay-$startDay)/7);
147              
148              
149             #print "Next Shabbat: " , $targetShabbat->ymd, "\n";
150              
151 122 50       429 if ($parshahNumber < 22) { # No special Shabbattot
152 0         0 return $sedrah[$parshahNumber];
153             }
154              
155             # From week 22 onwards there are special cases
156 122         237 $parshahNumber = 21;
157 122         522 $workingShabbat->add_duration(DateTime::Duration->new(days=>21*7));
158 122         134410 my $leapYear = DateTime::Calendar::Hebrew::_leap_year($simchatTorah->year);
159 122         1461 my $wDayPesach = $pesach->day_of_week;
160 122         931 my $combined = 0;
161            
162             #print "leap year: $leapYear\tPesach $wDayPesach\n";
163            
164              
165             LOOP:
166 122         656 while ($workingShabbat < $targetShabbat) {
167 2938         93618 $workingShabbat->add_duration(DateTime::Duration->new(days=> 7));
168             # If the Shabbat in question is yom tov or chol hamoed
169             # it does not count towards the parshah count.
170 2938 100 100     3554601 if ($israel && exists $IsraelYomTovMap{$workingShabbat->{rd_days}}) {next;}
  60         237  
171 2878 100 100     12666 if (!$israel && exists $YomTovMap{$workingShabbat->{rd_days}}) {next;}
  92         367  
172 2786         4779 my $workingDays = $workingShabbat->{rd_days};
173 2786 100       5139 $parshahNumber++ if ($combined);
174 2786         2957 $combined = 0;
175 2786         2832 $parshahNumber++;
176 2786 100       5117 if($parshahNumber==22) { #Vayakhel
177             # Combine Vayakhel/Pekudei if there are fewer than 4
178             # Shabbatot *before* the first day of Pesach
179 122         242 my $pesachDays = $pesach->{rd_days};
180 122 100       335 $combined =1 if ($pesachDays - $workingDays < 22);
181 122         618 next LOOP;
182             }
183 2664 100 100     10956 if($parshahNumber ==27 || # Tazria
184             $parshahNumber ==29) { # Acharei Mot
185 240 100       565 $combined = 1 if (!$leapYear);
186 240         989 next LOOP;
187             }
188 2424 100       4270 if($parshahNumber == 32) { # Behar
189 120 100       413 if ($israel) {
190             # In Israel we need to change the condition to
191             # not a leap year and Pesach not on Shabbat.
192             # Ths can only happen in a 354 day year that
193             # started on a thursday.
194 60 100 100     288 $combined = 1 if (!$leapYear && $wDayPesach != 7);
195             } else {
196             # if Pesach falls on Shabbat then Pesach8 is
197             # also on Shabbat. This is not relevant in
198             # Israel.
199 60 100       201 $combined = 1 if (!$leapYear);
200             }
201 120         537 next LOOP;
202             }
203 2304 100       4027 if($parshahNumber == 39) { # Chukat
204             # If Pesach falls on Thursday then Shavuot2 is Shabbat
205             # In Israel never combine because Shavuot can never be Shabbat
206 120 100 100     569 $combined = 1 if( $wDayPesach == 5 && !$israel);
207 120         612 next LOOP;
208             }
209 2184 100       4151 if($parshahNumber == 42) { # Mattot
210             # Devraim is always read on the Shabbat before
211             # Tisha B'Av. If 9 Av falls on Shabbat, then
212             # we read Devarim actually on 9 Av.
213 120         264 my $avDays = $tishaBAv->{rd_days};
214 120 100       368 $combined = 1 if (($avDays - $workingDays) < 14);
215 120         596 next LOOP;
216             }
217 2064 100       9536 if(51 == $parshahNumber ) { # Nitzavim
218             # Is there a Shabbat between YC and Succot?
219 60         151 my $roshDays = $RoshHashanah->{rd_days};
220 60 100       191 $combined = 1
221             if (($roshDays - $workingDays) > 3);
222 60         334 next LOOP;
223             }
224             }
225              
226 122         4675 my $parshah = $sedrah[$parshahNumber];
227 122 100       486 $parshah .= "/" . $sedrah[$parshahNumber+1] if ($combined);
228              
229 122         2136 return $parshah;
230              
231             }
232              
233             =head1 BUGS
234              
235             DateTime::Calendar::Hebrew is not a sub-class of DateTime. It
236             does not implement the all functionality of DateTime, and where it
237             does implement it, it uses different names and interfaces.
238             In particular, none of the arithmetic works.
239              
240             =cut
241              
242             1;
243              
244             =head1 AUTHOR
245              
246             Raphael Mankin, C<< >>
247              
248             =head1 BUGS
249              
250             Please report any bugs or feature requests to C, or through
251             the web interface at L. I will be notified, and then you'll
252             automatically be notified of progress on your bug as I make changes.
253              
254              
255              
256              
257             =head1 SUPPORT
258              
259             You can find documentation for this module with the perldoc command.
260              
261             perldoc DateTime::Event::Jewish
262              
263              
264             You can also look for information at:
265              
266             =over 4
267              
268             =item * RT: CPAN's request tracker
269              
270             L
271              
272             =item * AnnoCPAN: Annotated CPAN documentation
273              
274             L
275              
276             =item * CPAN Ratings
277              
278             L
279              
280             =item * Search CPAN
281              
282             L
283              
284             =back
285              
286              
287             =head1 ACKNOWLEDGEMENTS
288              
289              
290             =head1 LICENSE AND COPYRIGHT
291              
292             Copyright 2010 Raphael Mankin.
293              
294             This program is free software; you can redistribute it and/or modify it
295             under the terms of either: the GNU General Public License as published
296             by the Free Software Foundation; or the Artistic License.
297              
298             See http://dev.perl.org/licenses/ for more information.
299              
300              
301             =cut
302              
303             1; # End of DateTime::Event::Parshah