File Coverage

blib/lib/DateTime/Event/Jewish/Sunrise.pm
Criterion Covered Total %
statement 30 122 24.5
branch 0 14 0.0
condition 0 12 0.0
subroutine 10 21 47.6
pod 11 11 100.0
total 51 180 28.3


line stmt bran cond sub pod time code
1             package DateTime::Event::Jewish::Sunrise;
2 1     1   2631 use strict;
  1         2  
  1         30  
3 1     1   4 use warnings;
  1         1  
  1         21  
4 1     1   4 use base qw(Exporter);
  1         1  
  1         79  
5 1     1   4 use vars qw(@EXPORT_OK);
  1         2  
  1         59  
6 1     1   880 use DateTime;
  1         116732  
  1         36  
7 1     1   8 use DateTime::Duration;
  1         1  
  1         16  
8 1     1   1470 use Math::Trig;
  1         20811  
  1         154  
9 1     1   484 use DateTime::Event::Jewish::Declination qw(declination %Declination);
  1         1  
  1         139  
10 1     1   2060 use DateTime::Event::Jewish::ZoneLocation;
  1         3  
  1         62  
11 1     1   339 use DateTime::Event::Jewish::Eqt qw(eqt );
  1         2  
  1         995  
12             our $VERSION = '0.01';
13              
14             our @months=("Jan", "Feb", "Mar", "Apr", "May", "Jun",
15             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
16              
17             @EXPORT_OK = qw(@months);
18              
19             =head1 NAME
20              
21             DateTime::Event::Jewish::Sunrise - Calculate halachically
22             interesting times
23              
24             =head1 SYNOPSIS
25              
26             use DateTime::Event::Jewish::Sunrise ;
27              
28             my $jerusalem = DateTime::Event::Jewish::Sunrise->new([31,34,57], [35,13,0]), 'Asia/Jerusalem';
29             my $date = DateTime->new(year=>2010, month=>3, day=>30);
30              
31             my $shkia = $jerusalem->shkia($date);
32             my $shabbat = $jerusalem->kabbalatShabbat($date);
33             my $netz = $jerusalem->netzHachama($date);
34             my $night = $jerusalem->motzeiShabbat($date);
35              
36             =head1 DESCRIPTION
37              
38             This module assumes that the earth is a smooth sphere. No
39             allowance is made for atmospheric refraction or diffraction of
40             light passing close to the earth's surface. To allow for
41             refraction one uses a depression of 0.833 degrees (50' arc). As,
42             by default, we use 1 degree of depression this is more than adequate.
43              
44             The methods that return times actually return a DateTime
45             object in the correct timezone as specified in the constructor.
46             If an evaluation fails, e.g. no sunrise inside the Arctic cirle,
47             undef is returned.
48              
49             All times are corrected for the equation of time: the variation
50             between sundial time and clock time.
51              
52             If you call this module for a high latitude in the height of
53             summer or the depth of winter it will return nonsense. Ask a silly
54             question and you will get a very silly answer.
55              
56             Calculations are done using the spherical cosine rule.
57              
58             =head3 new($latitude, $longitude, $timeZone)
59              
60             Constructs a new object.
61              
62             =over
63              
64             =item $latitude
65              
66             An arrayref of three numbers representing degrees, minutes and
67             seconds of latitude. North latitudes are positive, south
68             latitudes are negative.
69              
70             =item $Longitude
71              
72             An arrayref of three numbers representing degrees, minutes and
73             seconds of longitude. East longitudes are positive, west
74             longitudes are negative.
75              
76             =item $timeZone
77              
78             A time-zone name as stored in the time-zone database, e.g.
79             "Europe/London".
80              
81             =back
82              
83             =cut
84              
85             sub new {
86 0     0 1   my $class = shift;
87 0           my ($lat, $long, $zone) = @_;
88 0           my $self = {lat=>$lat, long=>$long, zone=>$zone};
89 0           bless $self, $class;
90             }
91              
92              
93             =head3 halachicHalfDay($date, [$extra])
94              
95             Calculates the length of the halachic half day in minutes. This
96             is half the time between sunrise and sunset.
97              
98             If you call this method for a high latitude in the height of
99             summer or the depth of winter it will return 0. Ask a silly
100             question and you will get a very silly answer.
101              
102             =over
103              
104              
105             =item $date
106              
107             A DateTime object, either a Gregorian or a Hebrew date.
108             Only the day and month are relevant.
109              
110             =item $extra
111              
112             The number of degrees below the tangent plane that the sun
113             should be at halachic sunrise/set. Default 1.
114              
115             =item returns
116              
117             The length of the half-day in minutes. Returns 0 if there is no
118             sensible answer, e.g. inside the Arctic circle in summer.
119              
120             =back
121              
122             =cut
123              
124             sub halachicHalfDay {
125 0     0 1   my ($self, $hdate) = @_;
126 0           shift;shift;
  0            
127             # Ensure that we have a Gregorian date
128 0           my $date = DateTime->from_object(object=>$hdate);
129 0   0       my $extra= shift || 1;
130 0           my $delta = cos(deg2rad(90.0+$extra));
131             # Convert the location to radians.
132 0           my $phi = recalculate_coordinate($self->{lat},"rad");
133              
134 0           my ($day, $month) = ($date->day, $date->month);
135              
136 0           my $decl = declination($date); # Radians
137              
138             #Protect against stupidity - works for both summer and winter.
139 0 0         return 0 if abs($phi)+ abs($decl) +abs(deg2rad($extra)) > pi/2.0;
140              
141             # Find the longitude difference between the base point and the sun
142 0           my $dlong = acos(($delta - sin($phi)*sin($decl))/(cos($phi)*cos($decl)));
143             # Each degree of longitude represents 4 minutes of time
144 0           my $minutes_offset= rad2deg($dlong)*4.0;
145 0           return $minutes_offset;
146             }
147              
148             =head3 localnoon($date)
149              
150             Returns a DateTime object of the local time of local noon.
151              
152             =over
153              
154             =item $date
155              
156             A DateTime object, either a Gregorian or a Hebrew date.
157             Only the day and month are relevant.
158              
159             =back
160              
161             =cut
162              
163             sub localnoon {
164 0     0 1   my $self = shift;
165 0           my $hdate = shift;
166 0           my $date = DateTime->from_object(object=>$hdate);
167             # Extract the longitude so that we can do the correct shift
168 0           my $longref = $self->{long};
169 0           my $long = $longref->[0] + $longref->[1]/60.0 + $longref->[2]/3600.0;
170 0           $date->set_time_zone("UTC");
171 0           $date->set_hour(12);
172 0           $date->set_minute(0);
173 0           $date->set_second(0);
174 0           my $res = $date - DateTime::Duration->new(minutes=>eqt($date)+4*$long);
175             # We now have the correct time, but expressed in UTC
176             # So change the time-zone to what the user expects.
177 0           $res->set_time_zone($self->{zone});
178 0           return $res;
179             }
180              
181             =head3 halfday($date, [$as])
182              
183             Calculates the offset in minutes from local noon of sunrise/sunset at
184             the given location on the specified day of the year.
185             i.e. the time when the sun is 90 degrees from the zenith.
186              
187             =over
188              
189             =item $date
190              
191             A DateTime object. Only the day and month are relevant.
192             N.B. This is a Gregorian date.
193              
194             =item Returns
195              
196             The length of the ahlf day in minutes. Returns 0 if there is no
197             sensible answer, e.g. inside the ARctic circle in summer.
198              
199             =back
200              
201             =cut
202              
203             sub halfday {
204 0     0 1   my ($self, $date) = @_;
205 0           shift;shift;
  0            
206 0           my $_as= shift;
207             # Convert the location to radians.
208 0           my $phi = recalculate_coordinate($self->{lat},"rad");
209              
210 0           my ($day, $month) = ($date->day, $date->month);
211              
212 0           my $decl = declination($date);
213             #Protect against stupidity
214 0 0         return 0 if abs($phi)+ abs($decl) > pi/2.0;
215              
216             # Find the longitude difference between the base point and the sun
217 0           my $dlong = rad2deg(acos(-tan($phi)*tan($decl)));
218             # Each degree of longitude represents 4 minutes of time
219 0           my $minutes_offset= $dlong*4.0;
220 0           return $minutes_offset;
221             }
222              
223              
224             =head3 sunset($date)
225              
226             Calculates the time of sunset, i.e. when the mid-line of the
227             sun is 90degs from the zenith. The result returned has to be
228             corrected for your distance from the standard meridian.
229              
230             =over
231              
232             =item $date
233              
234             A DateTime object. Only the day and month are relevant.
235              
236             =item Returns
237              
238             A DateTime object.
239              
240             =back
241              
242             =cut
243              
244             sub sunset {
245 0     0 1   my ($self, $date) = @_;
246 0           my $res = $self->localnoon($date) +
247             DateTime::Duration->new(minutes=>$self->halfday($date));
248 0           $res->set_time_zone($self->{zone});
249             }
250              
251             =head3 sunrise($date)
252              
253             Calculates the time of sunrise, i.e. when the mid-line of the
254             sun is 90degs from the zenith. The result returned has to be
255             corrected for your distance from the standard meridian.
256              
257             =over
258              
259             =item $date
260              
261             A DateTime object. Only the day and month are relevant.
262              
263             =back
264              
265              
266             =cut
267              
268             sub sunrise {
269 0     0 1   my ($self, $date) = @_;
270 0           my $res = $self->localnoon($date) -
271             DateTime::Duration->new(minutes=>$self->halfday($date));
272 0           $res->set_time_zone($self->{zone});
273             }
274              
275             =head3 shkia($date, [$extra])
276              
277             Kabbalat Shabbat is 15 minutes before shkia, though some people
278             use 18 minutes;
279             Motzei Shabbat is 72 minutes after shkia (R Tam)
280              
281             Other values for 'extra' give the times of Motzei Shabbat.
282             Pre 1977 7.5 degs
283             Post 1977 8 degs or 8.5
284             Fast end 6 degs
285              
286             =over
287              
288             =item $date
289              
290             A DateTime object specifying the Gregorian date that we are interested in.
291              
292             =item $extra
293              
294             The number of degrees below the tangent plane that the sun
295             should be at halachic sunrise/set. Default 1.
296              
297             =back
298              
299             =cut
300              
301             sub shkia {
302 0     0 1   my($self, $date) = @_;
303 0           shift;shift;
  0            
304 0   0       my $extra=shift ||1;
305              
306             # $offset is a number of minutes
307 0           my $res = $self->localnoon($date)
308             +DateTime::Duration->new(minutes=>$self->halachicHalfDay( $date, $extra));
309 0           $res->set_time_zone($self->{zone});
310             }
311              
312             =head3 netzHachama($date, [$extra])
313              
314             Calculates the time of halachic sunrise. This is usually when the sun
315             is 1 degree below the tangent plane, though some people use other values.
316              
317             =over
318              
319             =item $date
320              
321             A DateTime object specifying the Gregorian date that we are interested in.
322              
323             =item $extra
324              
325             The number of degrees below the tangent plane that the sun
326             should be at halachic sunrise/set. Default 1.
327              
328             =back
329              
330             =cut
331              
332             sub netzHachama {
333 0     0 1   my ($self, $date) = @_;
334 0           my $res = $self->localnoon($date)
335             - DateTime::Duration->new(minutes=>$self->halachicHalfDay( $date));
336 0           $res->set_time_zone($self->{zone});
337             }
338              
339              
340             =head3 kabbalatShabbat($date, [$extra])
341              
342             Calculates the time of kabbalat Shabbat. This is usually 15 minutes
343             before shkia, though some people use 18 minutes.
344              
345             =over
346              
347             =item $date
348              
349             A DateTime object specifying the Gregorian date that we are interested in.
350              
351             =item $extra
352              
353             The number of minutes before shkia that one should use for kabbalat
354             Shabbat. Default 15.
355              
356             =back
357              
358             =cut
359              
360             sub kabbalatShabbat {
361 0     0 1   my ($self, $date) = @_;
362 0           shift; shift;
  0            
363 0   0       my $extra = shift|| 15;
364 0           my $res = $self->shkia($date)
365             - DateTime::Duration->new(minutes=>$extra);
366 0           return $res;
367             }
368              
369             =head3 motzeiShabbat($date, [$extra])
370              
371             Motzei Shabbat according to R Tam is 72 mins after shkia.
372             More conventionally we use the time when the sun is
373             either 7degs or 8 degs or 8.5 degs below the horizon.
374             Some people even use 11 or 14 degrees, but this quickly becomes
375             ridiculous in even moderate latitudes.
376              
377             For fast ends we use 6 degs (R Schneur Zalman).
378              
379             =over
380              
381             =item $date
382              
383             A DateTime object. Only the day and month are relevant.
384              
385             =item $extra
386              
387             The number of degrees below the tangent plane that the sun
388             should be at motzei Shabbat. Default 8.
389              
390             =back
391              
392             =cut
393              
394             sub motzeiShabbat {
395 0     0 1   my ($self, $date) = @_;
396 0           shift; shift;
  0            
397 0   0       my $extra= shift||8.0;
398            
399 0           return $self->shkia($date, $extra) ;
400             }
401              
402             =head3 recalculate_coordinate($location, $as)
403              
404             Convert a tupe of (degrees, minutes, seconds) to a normal form.
405              
406             =over
407              
408             =item $location
409              
410             An arrayref of three components: degrees, minutes, seconds.
411              
412             =item $as
413              
414             Return value type. Can be specified as 'deg', 'min', 'sec' or 'rad'; default
415             return value is a proper coordinate tuple.
416              
417             =back
418              
419             =cut
420              
421             sub recalculate_coordinate {
422 0     0 1   my $val = shift;
423 0           my $_as = shift;
424            
425             # Accepts a coordinate as a tuple (degree, minutes, seconds)
426             # You can give only one of them (e.g. only minutes as a floating point number) and it will be duly
427             # recalculated into degrees, minutes and seconds.
428              
429              
430 0           my ($deg, $min, $sec) = @$val;
431             # pass outstanding values from right to left
432 0   0       $min = ($min or 0) + int($sec) / 60;
433 0           $sec = $sec % 60;
434 0   0       $deg = ($deg or 0) + int($min) / 60;
435 0           $min = $min % 60;
436             # pass decimal part from left to right
437 0           my $dint = int($deg);
438 0           my $dfrac = $deg - $dint;
439 0           $min = $min + $dfrac * 60;
440 0           $deg = $dint;
441 0           my $mint = int($min);
442 0           my $mfrac = $min - $mint;
443 0           $sec = $sec + $mfrac * 60;
444 0           $min = $mint;
445 0 0         if ($_as) {
446 0           $sec = $sec + $min * 60 + $deg * 3600;
447 0 0         if ($_as eq 'sec') { return $sec;}
  0            
448 0 0         if ($_as eq 'min') { return $sec / 60;}
  0            
449 0 0         if ($_as eq 'deg') { return $sec / 3600;}
  0            
450 0 0         if ($_as eq 'rad') { return deg2rad($sec / 3600);}
  0            
451             }
452 0           return [$deg, $min, $sec];
453             }
454              
455             =head1 AUTHOR
456              
457             Raphael Mankin, C<< >>
458              
459             =head1 BUGS
460              
461             Please report any bugs or feature requests to C, or through
462             the web interface at L. I will be notified, and then you'll
463             automatically be notified of progress on your bug as I make changes.
464              
465              
466              
467              
468             =head1 SUPPORT
469              
470             You can find documentation for this module with the perldoc command.
471              
472             perldoc DateTime::Event::Jewish
473              
474              
475             You can also look for information at:
476              
477             =over 4
478              
479             =item * RT: CPAN's request tracker
480              
481             L
482              
483             =item * AnnoCPAN: Annotated CPAN documentation
484              
485             L
486              
487             =item * CPAN Ratings
488              
489             L
490              
491             =item * Search CPAN
492              
493             L
494              
495             =back
496              
497              
498             =head1 ACKNOWLEDGEMENTS
499              
500              
501             =head1 LICENSE AND COPYRIGHT
502              
503             Copyright 2010 Raphael Mankin.
504              
505             This program is free software; you can redistribute it and/or modify it
506             under the terms of either: the GNU General Public License as published
507             by the Free Software Foundation; or the Artistic License.
508              
509             See http://dev.perl.org/licenses/ for more information.
510              
511              
512             =cut
513              
514             1; # End of DateTime::Event::Sunrise