File Coverage

blib/lib/Religion/Islam/PrayerTimes.pm
Criterion Covered Total %
statement 21 716 2.9
branch 0 202 0.0
condition 0 66 0.0
subroutine 7 53 13.2
pod 0 46 0.0
total 28 1083 2.5


line stmt bran cond sub pod time code
1             #=Copyright Infomation
2             #==========================================================
3             #Module Name : Religion::Islam::PrayerTimes
4             #Program Author : Ahmed Amin Elsheshtawy
5             #Home Page : http://www.islamware.com
6             #Contact Email : support@islamware.com
7             #Copyrights © 2006 IslamWare. All rights reserved.
8             #==========================================================
9             # Original c++ source code version Copyright by Fayez Alhargan, 2001
10             # This is a module that computes prayer times and sunrise.
11             #==========================================================
12             package Religion::Islam::PrayerTimes;
13              
14 1     1   45443 use Carp;
  1         2  
  1         168  
15 1     1   7 use strict;
  1         2  
  1         35  
16 1     1   5 use warnings;
  1         71  
  1         30  
17 1     1   5 use Exporter;
  1         2  
  1         103  
18              
19             our @ISA = qw(Exporter);
20             our @EXPORT = qw();
21             our $VERSION = '1.02';
22              
23 1     1   1473 use Math::Complex;
  1         23312  
  1         266  
24 1     1   10391 use POSIX;
  1         9333  
  1         7  
25              
26             use constant{
27 1         7 pi => 4 * atan2(1, 1), # 3.1415926535897932;
28             DToR => (pi / 180),
29             RToH => (12 / pi),
30             EarthRadius => 6378.14
31 1     1   3501 };
  1         3  
32              
33             my $HStartYear =1420;
34             my $HEndYear = 1450;
35              
36             our @MonthMap = qw(19410
37             19396 19337 19093 13613 13741 15210 18132 19913 19858 19110
38             18774 12974 13677 13162 15189 19114 14669 13469 14685 12986
39             13749 17834 15701 19098 14638 12910 13661 15066 18132 18085
40             );
41             our @gmonth = qw(31 31 28 31 30 31 30 31 31 30 31 30 31 31); #makes it circular m[0]=m[12] & m[13]=m[1]
42             our @smonth = qw(31 30 30 30 30 30 29 31 31 31 31 31 31 30); #makes it circular m[0]=m[12] & m[13]=m[1]
43              
44             our %TimeZoneUS = (
45             AL => -6,
46             AK => -9,
47             AZ => -7,
48             AR => -6,
49             CA => -8,
50             CO => -7,
51             CT => -5,
52             DE => -5,
53             DC => -5,
54             FL => -5,
55             GA => -5,
56             GU => 10,
57             HI => -10,
58             ID => -7,
59             IL => -6,
60             IN => -6,
61             IA => -6,
62             KS => -6,
63             KY => -5,
64             LA => -6,
65             ME => -5,
66             MH => 12,
67             MD => -5,
68             MA => -5,
69             MI => -5,
70             MN => -6,
71             MS => -6,
72             MO => -6,
73             MT => -7,
74             'NE' => -6,
75             NV => -8,
76             NH => -5,
77             NJ => -5,
78             NM => -7,
79             NY => -5,
80             NC => -5,
81             ND => -6,
82             MP => 10,
83             OH => -5,
84             OK => -6,
85             OR => -8,
86             PA => -5,
87             PR => -4,
88             RI => -5,
89             SC => -5,
90             SD => -6,
91             TN => -5,
92             TX => -6,
93             UT => -7,
94             VT => -5,
95             VI => -4,
96             VA => -5,
97             WA => -5,
98             WV => -5,
99             WI => -6,
100             WY => -7,
101             );
102              
103             our %TimeZone = (
104             'AA'=> -4,
105             'AC'=>-4,
106             'AE'=> 4,
107             'AF'=>4.5,
108             'AG'=>1,
109             'AJ'=>4,
110             'AL'=>1,
111             'AM'=>4,
112             'AN'=>1,
113             'AO'=>1,
114             'AR'=>-3,
115              
116             #'AS'=>'Australia',
117              
118             #'AT'=>'Ashmore and Cartier Islands',
119             'AU'=>1,
120             'AV'=>-4,
121             'AX'=>-11,
122             'BA'=>3,
123             'BB'=>-4,
124             'BC'=>2,
125             'BD'=>-4,
126             'BE'=>1,
127             'BF'=>-5,
128             'BG'=>6,
129             'BH'=>-6,
130             'BK'=>1,
131             'BL'=>-4,
132             #'BM'=>'Burma',
133             'BN'=>1,
134             'BO'=>2,
135             'BP'=>11,
136             'BR'=>'Brazil',
137             #'BS'=>'Bassas da India',
138             'BT'=>6,
139             'BU'=>2,
140             #'BV'=>'Bouvet Island',
141             'BX'=>8,
142             'BY'=>2,
143             'CA'=>'Canada',
144             'CB'=>7,
145             'CD'=>1,
146             'CE'=>5.5,
147             'CF'=>1,
148             'CG'=>1,
149             'CH'=>8,
150             'CI'=>-4,
151             'CJ'=>-5,
152             'CK'=>6.5,
153             'CM'=>1,
154             'CN'=>3,
155             'CO'=>-5,
156             #'CR'=>'Coral Sea Islands',
157             'CS'=>-6,
158             'CT'=>1,
159             'CU'=>-5,
160             'CV'=>-1,
161             #'CW'=>'Avarua',
162             'CY'=>2,
163             'DA'=>1,
164             'DJ'=>3,
165             'DO'=>-4,
166             'DR'=>-4,
167             'EC'=>-5,
168             'EG'=>2,
169             'EI'=>0,
170             'EK'=>1,
171             'EN'=>2,
172             'ER'=>3,
173             'ES'=>-6,
174             'ET'=>3,
175             #'EU'=>'Europa Island',
176             'EZ'=>1,
177             'FG'=>3,
178             'FI'=>2,
179             'FJ'=>12,
180             'FK'=>-4,
181             #'FM'=>'Palikir',
182             'FO'=>0,
183             #'FP'=>'Clipperton Island',
184             'FR'=>1,
185             #'FS'=>'French Southern and Antarctic Lands',
186             'GA'=>0,
187             'GB'=>1,
188             'GG'=>4,
189             'GH'=>0,
190             'GI'=>1,
191             'GJ'=>-4,
192             'GK'=>0,
193             'GL'=>-3,
194             'GM'=>1,
195             #'GO'=>'Glorioso Islands',
196             'GP'=>-4,
197             'GR'=>2,
198             'GT'=>-6,
199             'GU'=>1,
200             'GV'=>0,
201             'GY'=>-4,
202             'GZ'=>2,
203             'HA'=>-5,
204             'HK'=>8,
205             #'HM'=>'Heard Island and McDonald Islands',
206             'HO'=>-6,
207             'HR'=>1,
208             'HU'=>1,
209             'IC'=>0,
210             #Indonesia Centeral
211             'ID'=>8,
212             #Indonesia East
213             'ID1'=>9,
214             #Indonesia West
215             'ID2'=>7,
216             #'IM'=>'Isle of Man',
217             'IN'=>5.5,
218             #'IO'=>'British Indian Ocean Territory',
219             #'IP'=>'Clipperton Island',
220             'IR'=>3.5,
221             'IS'=>2,
222             'IT'=>1,
223             'IV'=>0,
224             'IZ'=>3,
225             'JA'=>9,
226             'JE'=>0,
227             'JM'=>-5,
228             'JN'=>1,
229             'JO'=>2,
230             #'JU'=>'Juan de Nova Island',
231             'KE'=>3,
232             'KG'=>5,
233             'KN'=>9,
234             'KR'=>12,
235             'KS'=>9,
236             'KT'=>-10,
237             'KU'=>3,
238             'KZ'=>6,
239             'LA'=>7,
240             'LE'=>2,
241             'LG'=>2,
242             'LH'=>2,
243             'LI'=>0,
244             'LO'=>1,
245             'LS'=>1,
246             'LT'=>2,
247             'LU'=>1,
248             'LY'=>2,
249             'MA'=>3,
250             'MB'=>-4,
251             #'MC'=>'Macau',
252             'MD'=>2,
253             'MF'=>3,
254             'MG'=>8,
255             'MH'=>-4,
256             'MI'=>2,
257             'MK'=>1,
258             'ML'=>0,
259             'MN'=>1,
260             'MO'=>0,
261             'MP'=>4,
262             'MR'=>0,
263             'MT'=>1,
264             'MU'=>4,
265             'MV'=>5,
266             'MX'=>'Mexico',
267             'MY'=>8,
268             'MZ'=>2,
269             'NC'=>11,
270             #'NE'=>'Alofi',
271             'NF'=>11.5,
272             'NG'=>1,
273             'NH'=>11,
274             'NI'=>1,
275             'NL'=>1,
276             #'NM'=>'No Man\'s Land',
277             'NO'=>1,
278             'NP'=>7.75,
279             'NR'=>12,
280             'NS'=>-3,
281             'NT'=>-4,
282             'NU'=>-6,
283             'NZ'=>12,
284             'PA'=>-4,
285             #'PC'=>'Adamstown',
286             'PE'=>-5,
287             'PK'=>5,
288             'PL'=>1,
289             'PM'=>-5,
290             'PO'=>0,
291             'PP'=>10,
292             'PR'=>-4,
293             'PU'=>0,
294             'QA'=>3,
295             'RE'=>4,
296             #'RM'=>'Majuro',
297             'RO'=>2,
298             'RP'=>8,
299              
300             #'RS'=>'Russia',
301             'RS1'=>2,
302             'RS2'=>4,
303             'RS3'=>4,
304             'RS4'=>5,
305             'RS5'=>6,
306             'RS6'=>7,
307             'RS7'=>8,
308             'RS8'=>9,
309             'RS9'=>10,
310             'RS10'=>11,
311             'RS11'=>12,
312              
313             'RW'=>2,
314             'SA'=>3,
315             'SB'=>-3,
316             'SC'=>-4,
317             'SE'=>4,
318             'SF'=>2,
319             'SG'=>0,
320             #'SH'=>'Jamestown',
321             'SI'=>1,
322             'SL'=>0,
323             'SM'=>1,
324             'SN'=>8,
325             'SO'=>3,
326             'SP'=>1,
327             'ST'=>-4,
328             'SU'=>2,
329             #'SV'=>'Svalbard',
330             'SW'=>1,
331             #'SX'=>'South Georgia and the South Sandwich Islands',
332             'SY'=>2,
333             'SZ'=>1,
334             'TD'=>-4,
335             #'TE'=>'Tromelin Island',
336             'TH'=>7,
337             'TI'=>6,
338             'TK'=>-5,
339             #'TL'=>'Tokelau',
340             'TN'=>13,
341             'TO'=>0,
342             'TP'=>0,
343             'TS'=>1,
344             #'TT'=>'East Timor',
345             'TU'=>2,
346             'TV'=>12,
347             'TW'=>8,
348             'TX'=>5,
349             'TZ'=>3,
350             'UG'=>3,
351             'UK'=>0,
352             'UP'=>2,
353             #'US'=>'United States',
354             'UV'=>0,
355             'UY'=>-3,
356             'UZ'=>5,
357             'VC'=>-4,
358             'VE'=>-4,
359             'VI'=>-4,
360             'VM'=>7,
361             'VT'=>1,
362             'WA'=>1,
363             'WE'=>2,
364             'WF'=>12,
365             'WI'=>0,
366             'WS'=>-11,
367             'WZ'=>2,
368             'YI'=>1,
369             'YM'=>3,
370             'ZA'=>2,
371             'ZI'=>2
372             );
373              
374             #==========================================================
375             #==========================================================
376             sub new {
377 0     0 0   my ($class, %args) = @_;
378            
379 0           my $self = bless {}, $class;
380            
381             # Defaults
382 0           $self->{JuristicMethod} = 1; # Standard
383 0           $self->CalculationMethod(3); # Egyptian General Authority of Survey
384 0           $self->{SafetyTime} = 0.016388; # 59 seconds, safety time
385 0           $self->{ReferenceAngle} = 45;
386 0           $self->{EidPrayerTime} = 4.2; # Eid Prayer Time 4.2
387 0           $self->{DaylightSaving} = 0;
388              
389             # Cairo as the default location
390 0           $self->{Latitude} = 30.050;
391 0           $self->{Longitude} = 31.250;
392 0           $self->{Altitude} = 22;
393 0           $self->{TimeZone} = 2;
394            
395 0           $self->{TimeMode} = 0; # 12 or 24 hour time format
396              
397 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
398 0           $mon++;
399 0           $year += 1900;
400              
401 0           return $self;
402             }
403             #==========================================================
404             # Safety time in hours should be 0.016383h. 59seconds, safety time
405             sub SafetyTime {
406 0     0 0   my ($self) = shift;
407 0 0         $self->{SafetyTime} = shift if @_;
408 0           return $self->{SafetyTime};
409             }
410             #==========================================================
411             #Juristic Methods:
412             # 1)-Standard (Imams Shafii, Hanbali, and Maliki),
413             # 2)-Hanafi
414             #Aser = 1 or 2
415             #The Henfy (asr=2), Shafi (asr=1, Omalqrah asr=1)
416             sub JuristicMethod {
417 0     0 0   my ($self, $method) = @_;
418            
419 0 0 0       if (!defined $method || int($method) < 1 || int($method) > 2) {
      0        
420 0           return $self->{JuristicMethod};
421             }
422 0           $self->{JuristicMethod} = $method;
423 0           return $self->{JuristicMethod};
424             }
425             #==========================================================
426             # Calculation Method
427             #1: Umm Al-Qura Committee
428             #2: Muslim World League
429             #3: Egyptian General Authority of Survey
430             #4: University Of Islamic Sciences, Karachi
431             #5: ISNA, Islamic Society of North America
432             sub CalculationMethod {
433 0     0 0   my ($self, $method) = @_;
434            
435 0 0 0       if (!defined $method || int($method) < 1 || int($method) > 5) {
      0        
436 0           return $self->{CalculationMethod};
437             }
438              
439 0           $self->{CalculationMethod} = $method;
440              
441 0 0         if ($method == 1) {# Umm Al-Qura Committee
    0          
    0          
    0          
    0          
442 0           $self->{FajerAngle} = 19;
443 0           $self->{IshaAngle} = 0;
444 0           $self->{IshaFixedTime} = 1.5;
445             }
446             elsif ($method == 2) {# Muslim World League
447 0           $self->{FajerAngle} = 18;
448 0           $self->{IshaAngle} = 17;
449 0           $self->{IshaFixedTime} = 1.5;
450             }
451             elsif ($method == 3) {# Egyptian General Authority of Survey
452 0           $self->{FajerAngle} = 19.5;
453 0           $self->{IshaAngle} = 17.5;
454 0           $self->{IshaFixedTime} = 1.5;
455             }
456             elsif ($method == 4) { # University Of Islamic Sciences, Karachi
457 0           $self->{FajerAngle} = 18;
458 0           $self->{IshaAngle} = 18;
459 0           $self->{IshaFixedTime} = 1.5;
460             }
461             elsif ($method == 5) { # ISNA, Islamic Society of North America
462 0           $self->{FajerAngle} = 15;
463 0           $self->{IshaAngle} = 15;
464 0           $self->{IshaFixedTime} = 1.5;
465             }
466            
467 0           return $self->{CalculationMethod};
468             }
469             #==========================================================
470             sub FajerAngle{
471 0     0 0   my ($self) = shift;
472 0 0         $self->{FajerAngle} = shift if @_;
473 0           return $self->{FajerAngle};
474             }
475             #==========================================================
476             sub IshaAngle{
477 0     0 0   my ($self) = shift;
478 0 0         $self->{IshaAngle} = shift if @_;
479 0           return $self->{IshaAngle};
480             }
481             #==========================================================
482             sub IshaFixedTime{
483 0     0 0   my ($self) = shift;
484 0 0         $self->{IshaFixedTime} = shift if @_;
485 0           return $self->{IshaFixedTime};
486             }
487             #==========================================================
488             #Reference Angle suggested by Rabita 45
489             #latude (radian) that should be used for places above -+65.5 should be 45deg as suggested by Rabita
490             sub ReferenceAngle{
491 0     0 0   my ($self) = shift;
492 0 0         $self->{ReferenceAngle} = shift if @_;
493 0           return $self->{ReferenceAngle};
494             }
495             #==========================================================
496             # Eid Prayer Time 4.2
497             sub EidPrayerTime{
498 0     0 0   my ($self) = shift;
499 0 0         $self->{EidPrayerTime} = shift if @_;
500 0           return $self->{EidPrayerTime};
501             }
502             #==========================================================
503             # Longitude in radians
504             sub Longitude{
505 0     0 0   my ($self) = shift;
506 0 0         $self->{Longitude} = shift if @_;
507 0           return $self->{Longitude};
508             }
509             #==========================================================
510             # Latitude in radians
511             sub Latitude {
512 0     0 0   my ($self) = shift;
513 0 0         $self->{Latitude} = shift if @_;
514 0           return $self->{Latitude};
515             }
516             #==========================================================
517             # HeightdifW : param[3]: The place western herizon height difference in meters
518             # HeightdifE : param[4]: The place eastern herizon height difference in meters
519             sub Altitude{
520 0     0 0   my ($self) = shift;
521 0 0         $self->{Altitude} = shift if @_;
522 0           return $self->{Altitude};
523             }
524             #==========================================================
525             # Time Zone difference from GMT
526             sub TimeZone{
527 0     0 0   my ($self) = shift;
528 0 0         $self->{TimeZone} = shift if @_;
529 0           return $self->{TimeZone};
530             }
531             #==========================================================
532             # Q. What is daylight saving? Ans. Many countries try to adopt their work time by subtracting
533             # from their clocks one hour in the Fall and Winter seasons.
534             sub DaylightSaving {
535 0     0 0   my ($self) = shift;
536 0           my ($time);
537            
538 0 0         if (@_) {
539 0           $time = shift;
540 0 0         if ($time > 0) {
  0            
541 0           $self->{DaylightSaving} = 1;
542             }
543             else {$self->{DaylightSaving} = 0;}
544             }
545 0           return $self->{DaylightSaving};
546             }
547             #==========================================================
548             sub TimeMode {
549 0     0 0   my ($self, $mode) = @_;
550            
551 0 0         if (!defined $mode) {
552 0           return $self->{TimeMode};
553             }
554 0 0         $mode = $mode ? 1: 0;
555 0           $self->{TimeMode} = $mode;
556 0           return $self->{TimeMode};
557             }
558             #==========================================================
559             sub FormatTime{
560 0     0 0   my ($self, $time) = @_;
561 0           my ($hour, $min, $sec, $am);
562              
563 0           $hour = int ($time);
564 0           $min = int (60.0*($time- $hour));
565 0 0         if ($min == 60) {$hour++; $min = 0;}
  0            
  0            
566 0 0         if ($min < 0) {$min = -$min;}
  0            
567              
568 0           $min = sprintf("%02d", $min);
569 0           $hour = sprintf("%02d", $hour);
570            
571 0 0         if (!$self->{TimeMode}) { # 12 hour mode
572 0 0         $am = ($hour > 12)? 'pm': 'am';
573 0 0         if ($hour >12) {$hour -= 12;}
  0            
574             }
575             else {
576 0           $am = "";
577             }
578              
579 0           return ($hour, $min, $am);
580             }
581             #==========================================================
582             sub GregorianMonthLength {
583 0     0 0   my ($self, $month, $year) = @_;
584            
585             # Compute the last date of the month for the Gregorian calendar.
586 0 0         if ($month == 2)
587             {
588 0 0 0       return 29 if ($year % 4 == 0 && $year % 100 != 0) || ($year % 400 == 0);
      0        
589             }
590 0           return (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$month - 1];
591             }
592             #==========================================================
593             sub IslamicLeapYear {
594 0     0 0   my ($self, $year) = @_;
595             # True if year is an Islamic leap year
596 0 0         return ((((11 * $year) + 14) % 30) < 11) ? 1 : 0;
597             }
598             #==========================================================
599             sub IslamicMonthLength {
600 0     0 0   my ($self, $month, $year) = @_;
601             # Last day in month during year on the Islamic calendar.
602 0 0 0       return ($month % 2 == 1) || ($month == 12 && IslamicLeapYear($year)) ? 30 : 29;
603             }
604             #==========================================================
605             #Set the prayer location to calculate for.
606             sub PrayerLocation {
607 0     0 0   my ($self, %args) = @_;
608            
609 0           $self->{Latitude} = $args{Latitude}; # Latitude
610 0           $self->{Longitude} = $args{Longitude}; # Longitude
611 0           $self->{Altitude} = $args{Altitude}; #HeightdifW : param[3]: The place western herizon height difference in meters
612 0           $self->{TimeZone} = $args{TimeZone}; # Time Zone difference from GMT
613 0           my $key; my $value;
  0            
614             #while (($key, $value)=each(%args)) {print ("PrayerLocation: $key = $value\n");}
615             }
616             #==========================================================
617             #=PrayerTimes
618             # For international prayer times see Islamic Fiqah Council of the Muslim
619             # World League: Saturday 12 Rajeb 1406H, concerning prayer times and fasting
620             # times for countries of high latitudes.
621             # This program is based on the above.
622             #
623             # Arguments:
624             # yg, mg, dg : Date in Greg
625             # param[0] : Safety time in hours should be 0.016383h
626             # longtud,latud : param[1],[2] : The place longtude and latitude in radians
627             # HeightdifW : param[3]: The place western herizon height difference in meters
628             # HeightdifE : param[4]: The place eastern herizon height difference in meters
629             # Zonh :param[5]: The place zone time dif. from GMT West neg and East pos in decimal hours
630             # fjrangl : param[6]: The angle (radian) used to compute Fajer prayer time (OmAlqrah -19 deg.)
631             # ashangl : param[7]: The angle (radian) used to compute Isha prayer time
632             # ashangl=0 then use (OmAlqrah: ash=SunSet+1.5h)
633             # asr : param[8]: The Henfy (asr=2) Shafi (asr=1, Omalqrah asr=1)
634             # param[9] : latude (radian) that should be used for places above -+65.5 should be 45deg as suggested by Rabita
635             # param[10] : The Isha fixed time from Sunset
636             #
637             # Output:
638             # lst[]...lst[n],
639             # 1: Fajer
640             # 2: Sunrise
641             # 3: Zohar
642             # 4: Aser
643             # 5: Magreb
644             # 6: Isha
645             # 7: Fajer using exact Rabita method for places >48
646             # 8: Ash using exact Rabita method for places >48
647             # 9: Eid Prayer Time
648             # for places above 48 lst[1] and lst[6] use a modified version of
649             # Rabita method that tries to eliminate the discontinuity
650             # all in 24 decimal hours
651             #
652             #returns flag :0 if there are problems, flag:1 no problems
653             #=cut
654              
655             # Compute prayer times and sunrise
656             sub PrayerTimes {
657 0     0 0   my ($self, $yg, $mg, $dg) = @_;
658 0           my (@lst, @param, %result);
659            
660             #print "PrayerTimes for : $yg, $mg, $dg \n\n";
661 0           $param[0] = $self->{SafetyTime}; # 59seconds, safety time
662 0           $param[1] = $self->{Longitude}*DToR; # Longitude in radians
663 0           $param[2] = $self->{Latitude}*DToR; # Latitude in radians
664 0           $param[3] = $self->{Altitude}; #HeightdifW : param[3]: The place western herizon height difference in meters
665 0           $param[4] = $self->{Altitude}; #HeightdifE : param[4]: The place eastern herizon height difference in meters
666 0           $param[5] = $self->{TimeZone}; # Time Zone difference from GMT
667 0           $param[6] = $self->{FajerAngle}*DToR; # Fajer Angle =19
668 0           $param[7] = $self->{IshaAngle}*DToR; # Isha Angle if set to zero, then $param[10] must be 1.5
669 0           $param[8] = $self->{JuristicMethod}; #Aser=1, 2, Juristic Methods: Standard and Hanafi
670 0           $param[9] = $self->{ReferenceAngle}*DToR; # Reference Angle suggested by Rabita 45
671 0           $param[10] = $self->{IshaFixedTime}; # Isha fixed time from sunset =1.5, if $param[7] > 0 then this is discarded
672 0           $param[11] = $self->{EidPrayerTime}*DToR; # Eid Prayer Time 4.2
673             # $param[0]= 0.016388; # /* 59seconds, safety time */
674             # $param[1]=$longtud*DToR; #/* Longitude in radians */
675             # $param[2]=$latud*DToR;
676             # $param[3]=22.0;
677             # $param[4]=22.0;
678             # $param[5]=$Zonh; # /* Time Zone difference from GMT S.A. 2*/
679             # $param[6]=19.5*DToR; #/* Fajer Angle =19 */
680             # $param[7]=17.5*DToR; #/* Isha Angle if setto zero, then $param[10] must be 1.5*/
681             # $param[8]=1; #/* Aser=1,2 OmAlrqah Aser=1*/
682             # $param[9]=45*DToR; #/* Reference Angle suggested by Rabita 45*/
683             # $param[10]=1.5; #/* Isha fixed time from sunset =1.5, if $param[7] >0 then this is discarded*/
684             # $param[11]=4.2*DToR; #/* Eid Prayer Time 4.2 */
685              
686             # for my $xx(0..$#param) {
687             # print "Param: $xx = ". $param[$xx] . "\n";
688             # }
689 0           my $flag=1;
690 0           my $flagrs;
691 0           my $problm=0;
692              
693 0           my ($RA, $Decl);
694 0           my ($Rise, $Transit, $Setting);
695 0           my ($SINd, $COSd);
696 0           my ($act, $H, $angl, $K, $cH);
697 0           my ($X, $MaxLat);
698 0           my ($H0, $Night, $IshRt, $FajrRt);
699 0           my $HightCorWest=0;
700 0           my $HightCorEast=0;
701 0           my ($IshFix, $FajrFix);
702 0           my $JD;
703              
704             #Main Local variables:
705             #RA= Sun's right ascension
706             #Decl= Sun's declination
707             #H= Hour Angle for the Sun
708             #K= Noon time
709             #angl= The Sun altitude for the required time
710             #flagrs: sunrise sunset flags
711             # 0:no problem
712             # 16: Sun always above horizon (at the ploes for some days in the year)
713             # 32: Sun always below horizon
714              
715             # Compute the Sun various Parameters
716              
717 0           ($JD, $Rise, $Transit, $Setting, $RA, $Decl, $flagrs) =
718             $self->SunParamr($yg, $mg, $dg, -$param[1], $param[2], -$param[5]);
719              
720             # Compute General Values
721 0           $SINd=sin($Decl)*sin($param[2]);
722 0           $COSd=cos($Decl)*cos($param[2]);
723              
724             # Noon
725 0           $K=$Transit;
726              
727             # Compute the height correction
728 0           $HightCorWest=0; $HightCorEast=0;
  0            
729 0 0 0       if ($flagrs==0 && fabs($param[2])<0.79 && ($param[4]!=0 || $param[3]!=0))
      0        
      0        
730             { # height correction not used for problematic places above 45deg
731 0           $H0=$H=0;
732 0           $angl=-0.83333*DToR; # standard value angl=50min=0.8333deg for sunset and sunrise
733 0           $cH=(sin($angl)-$SINd)/($COSd);
734 0           $H0=acos($cH);
735              
736 0           $X= EarthRadius*1000.0; # meters
737 0           $angl = -0.83333*DToR+(0.5*pi - asin($X/($X+$param[3])));
738 0           $cH=(sin($angl)-$SINd)/($COSd);
739 0           $HightCorWest=acos($cH);
740 0           $HightCorWest=($H0-$HightCorWest)*(RToH);
741              
742 0           $angl=-0.83333*DToR+(0.5*pi-asin($X/($X+$param[4])));
743 0           $cH=(sin($angl)-$SINd)/($COSd);
744              
745 0           $HightCorEast=acos($cH);
746 0           $HightCorEast=($H0-$HightCorEast)*(RToH);
747             }
748              
749             # Modify Sunrise,Sunset and Transit for problematic places
750 0 0 0       if (!($flagrs==0 && fabs($Setting-$Rise)>1 && fabs($Setting-$Rise)<23))
      0        
751             { # There are problems in computing sun(rise,set)
752             # This is because of places above -+65.5 at some days of the year
753             #Note param[9] should be 45deg as suggested by Rabita
754 0           $problm=1;
755 0 0         if ($param[2]<0) {$MaxLat= -fabs($param[9]);} else {$MaxLat= fabs($param[9]);}
  0            
  0            
756             #Recompute the Sun various Parameters using the reference param[9]
757              
758             #($JD, $Rise, $Transit, $Setting, $RA, $Decl, $RiseSetFlags)
759 0           my ($JD, $Rise, $Transit, $Setting, $RA, $Decl, $flagrs) =
760             $self->SunParamr($yg, $mg, $dg, -$param[1], $MaxLat, -$param[5]);
761 0           $K = $Transit; # exact noon time
762              
763             #ReCompute General Values for the new reference param[9]
764 0           $SINd=sin($Decl)*sin($MaxLat);
765 0           $COSd=cos($Decl)*cos($MaxLat);
766             }
767             #-------------------------------------------------------------
768 0 0         if($K<0) {$K=$K+24;}
  0            
769             #print "[$Rise - $HightCorEast, K=$K] \n";
770 0           $lst[2]=$Rise-$HightCorEast; # Sunrise - Height correction
771 0           $lst[3]= $K+ $param[0]; # Zohar time+extra time to make sure that the sun has moved from zaowal
772 0           $lst[5]= $Setting+$HightCorWest+ $param[0]; #Magrib= SunSet + Height correction + Safety Time
773             #-------------------------------------------------------------
774             # Asr time: Henfy param[8]=2, Shafi param[8]=1, OmAlqrah asr=1
775 0 0         if($problm){# For places above 65deg
776 0           $act=$param[8]+tan(fabs($Decl-$MaxLat));
777             }
778             else {#no problem
779 0           $act=$param[8]+tan(fabs($Decl-$param[2])); # In the standard equations abs() is not used, but it is required for -ve latitude
780             }
781              
782 0           $angl=atan(1.0/$act);
783 0           $cH=(sin($angl)-$SINd)/($COSd);
784 0 0         if(fabs($cH)>1.0)
785             {
786 0           $H=3.5;
787 0           $flag=0; #problem in compuing Asr
788             }
789             else
790             {
791 0           $H=acos($cH);
792 0           $H=$H*RToH;
793             }
794              
795 0           $lst[4]=$K+$H+$param[0]; # Asr Time
796             #-------------------------------------------------------------
797             #Fajr Time
798 0           $angl= -$param[6]; # The value -19deg is used by OmAlqrah for Fajr, but it is not correct, Astronomical twilight and Rabita use -18deg
799 0           $cH=(sin($angl)-$SINd)/($COSd);
800 0 0         if (fabs($param[2])<0.83776){# If latitude<48deg
801             # no problem
802 0           $H=acos($cH);
803 0           $H=$H*RToH; #convert radians to hours
804 0           $lst[1]=$K-($H+$HightCorEast)+$param[0]; #Fajr time
805 0           $lst[7]=$lst[1];
806             }
807             else
808             { # Get fixed ratio, data depends on latitutde sign
809 0 0         if($param[2]<0){
810 0           my ($IshFix, $FajrFix) = $self->GetRatior($yg, 12, 21, @param);
811             }
812             else{
813 0           my ($IshFix, $FajrFix) = $self->GetRatior($yg, 6, 21, @param);
814             }
815              
816 0 0         if (fabs($cH)>(0.45+1.3369*$param[6]))# A linear equation I have interoduced
817             { # The problem occurs for places above -+48 in the summer
818 0           $Night = 24-($Setting-$Rise);# Night Length
819 0           $lst[1]=$Rise-$Night*$FajrFix; #According to the general ratio rule
820             }
821             else
822             { # no problem
823 0           $H=acos($cH);
824 0           $H=$H*RToH; #convert radians to hours
825 0           $lst[1]=$K-($H+$HightCorEast)+$param[0]; # Fajr time
826             }
827              
828 0           $lst[7]=$lst[1];
829 0 0         if (fabs($cH)>1)
830             { # The problem occurs for places above -+48 in the summer
831 0           my ($IshRt, $FajrRt) = $self->GetRatior($yg, $mg, $dg, @param);
832 0           $Night=24-($Setting-$Rise); #Night Length
833 0           $lst[7]= $Rise-$Night*$FajrRt; # Accoording to Rabita Method
834             }
835             else
836             { # no problem
837 0           $H = acos($cH);
838 0           $H= $H*RToH; #convert radians to hours
839 0           $lst[7] = $K- ($H+$HightCorEast)+$param[0]; #Fajr time
840             }
841             }
842             #-------------------------------------------------------------
843             # Isha prayer time
844 0 0         if($param[7]!=0) # if Ish angle not equal zero
845             {
846 0           $angl= -$param[7];
847 0           $cH=(sin($angl)-$SINd)/($COSd);
848 0 0         if (fabs($param[2])<0.83776) # If latitude<48deg
849             { #no problem
850 0           $H=acos($cH);
851 0           $H=$H*RToH; #convert radians to hours
852 0           $lst[6]=$K+($H+$HightCorWest+$param[0]); #Isha time, instead of Sunset+1.5h
853 0           $lst[8]=$lst[6];
854             }
855             else
856             {
857 0 0         if (fabs($cH)>(0.45+1.3369*$param[6])) # A linear equation I have interoduced
858             { #The problem occurs for places above -+48 in the summer
859 0           $Night=24-($Setting-$Rise); # Night Length
860 0           $lst[6]=$Setting+$Night*$IshFix; # Accoording to Rabita Method
861             }
862             else
863             { #no problem
864 0           $H=acos($cH);
865 0           $H=$H*RToH; #convert radians to hours
866 0           $lst[6]=$K+($H+$HightCorWest+$param[0]); # Isha time, instead of Sunset+1.5h
867             }
868              
869 0 0         if (fabs($cH)>1.0)
870             { #The problem occurs for places above -+48 in the summer
871 0           my ($IshRt, $FajrRt) = $self->GetRatior($yg, $mg, $dg, @param);
872              
873 0           $Night=24-($Setting-$Rise); #Night Length
874 0           $lst[8]=$Setting+$Night*$IshRt; #According to the general ratio rule
875             }
876             else
877             {
878 0           $H=acos($cH);
879 0           $H=$H*RToH; #convert radians to hours
880 0           $lst[8]=$K+($H+$HightCorWest+$param[0]); # Isha time, instead of Sunset+1.5h
881             }
882              
883             }
884             }
885             else
886             {
887 0           $lst[6]=$lst[5]+$param[10]; #Isha time OmAlqrah standard Sunset+fixed time (1.5h or 2h in Romadan)
888 0           $lst[8]=$lst[6];
889             }
890             # -------------------------------------------------------------
891             # Eid prayer time
892 0           $angl=$param[11]; # Eid Prayer time Angle is 4.2
893 0           $cH=(sin($angl)-$SINd)/($COSd);
894 0 0 0       if ((fabs($param[2])<1.134 || $flagrs==0) && fabs($cH)<=1.0)# If latitude<65deg
      0        
895             {# no problem
896 0           $H=acos($cH);
897 0           $H=$H*RToH; #convert radians to hours
898 0           $lst[9]=$K-($H+$HightCorEast)+$param[0];# Eid time
899             }
900             else
901             {
902 0           $lst[9]=$lst[2]+0.25; #If no Sunrise add 15 minutes
903             }
904             #---------------------------------------------
905             # return the result in a hash
906 0           undef %result;
907 0           $result{Flag} = $flag; # flag =0: means problem in compuing Asr
908            
909 0           $result{Fajr} = $lst[1] + $self->{DaylightSaving};
910 0           $result{Sunrise} = $lst[2] + $self->{DaylightSaving};
911 0           $result{Zohar} = $lst[3] + $self->{DaylightSaving};
912 0           $result{Aser} = $lst[4] + $self->{DaylightSaving};
913 0           $result{Maghrib} = $lst[5] + $self->{DaylightSaving};
914 0           $result{Isha} = $lst[6] + $self->{DaylightSaving};
915 0           $result{FajirRabita} = $lst[7] + $self->{DaylightSaving}; #Fajer using exact Rabita method for places >48
916 0           $result{IshaRabita} = $lst[8] + $self->{DaylightSaving}; #Ash using exact Rabita method for places >48
917 0           $result{Eid} = $lst[9] + $self->{DaylightSaving}; #Eid Prayer Time
918             #for places above 48 lst[1] and lst[6] use a modified version of
919             #Rabita method that tries to eliminate the discontinuity
920             #all in 24 decimal hours
921 0           return %result;
922             }
923             #==========================================================
924             sub atanxy{
925 0     0 0   my ($self, $x, $y) = @_;
926 0           my $argm;
927 0 0         if ($x==0) {$argm=0.5*pi;} else {$argm=atan($y/$x);}
  0            
  0            
928 0 0 0       if ($x>0 && $y<0) {$argm=2.0*pi+$argm;}
  0            
929 0 0         if ($x<0) {$argm=pi+$argm;}
  0            
930 0           return $argm;
931             }
932             #==========================================================
933             #==========================================================
934             # EclipToEquator(tht , 0, *RA,*Decl);
935             sub EclipToEquator{
936 0     0 0   my ($self, $lmdr, $betar) = @_;
937 0           my ($alph, $dltr);
938             # Convert Ecliptic to Equatorial Coordinate
939             # p.40 No.27, Peter Duffett-Smith book
940             # input: lmdr,betar in radians
941             # output: alph,dltr in radians
942 0           my $eps = 23.441884; # (in degrees) this changes with time
943 0           my ($sdlt, $epsr);
944 0           my ($x, $y, $alpr);
945 0           my $rad = 0.017453292; # =pi/180.0
946              
947 0           $epsr = $eps * $rad; # convert to radians
948 0           $sdlt = sin($betar)*cos($epsr)+cos($betar)*sin($epsr)*sin($lmdr);
949 0           $dltr = asin($sdlt);
950 0           $y = sin($lmdr)*cos($epsr)-tan($betar)*sin($epsr);
951 0           $x = cos($lmdr);
952 0           $alph = $self->atanxy($x, $y);
953 0           return ($alph, $dltr);
954             }
955             #==========================================================
956             sub RoutinR2{
957 0     0 0   my ($self, $M, $e) = @_;
958             # Routine R2:
959             # Calculate the value of E
960             # p.91, Peter Duffett-Smith book
961            
962 0           my $dt=1;
963 0           my ($dE, $Ec);
964 0           $Ec = $M;
965              
966 0           while (fabs($dt)>1e-9) {
967 0           $dt = $Ec - $e*sin($Ec)-$M;
968 0           $dE = $dt/(1-$e*cos($Ec));
969 0           $Ec = $Ec - $dE;
970             }
971 0           return $Ec;
972             }
973              
974             #==========================================================
975             # p.99 of the Peter Duffett-Smith book
976             sub SunParamr{
977 0     0 0   my ($self, $yg, $mg, $dg, $ObsLon, $ObsLat, $TimeZone)=@_;
978 0           my ( $Rise, $Transit, $Setting, $RA, $Decl, $RiseSetFlags);
979 0           my ($UT, $ET, $y, $L, $e, $M, $omg);
980 0           my ($eps, $T, $JD, $Ec);
981 0           my ($tnv, $v, $tht);
982 0           my ($K, $angl, $T1, $T2, $H, $cH);
983            
984 0           $RiseSetFlags = 0;
985              
986 0           $JD = $self->GCalendarToJD($yg, $mg, $dg);
987 0           $T = ($JD + $TimeZone/24.0 - 2451545.0) / 36525.0;
988              
989 0           $L = 279.6966778+36000.76892*$T + 0.0003025*$T*$T; # in degrees
990 0           while ($L > 360) {$L = $L-360;}
  0            
991 0           while ($L < 0) {$L = $L+360;}
  0            
992 0           $L = $L*pi/180.0; # radians
993              
994 0           $M = 358.47583+35999.04975*$T-0.00015*$T*$T-0.0000033*$T*$T*$T;
995 0           while ($M>360) {$M=$M-360;}
  0            
996 0           while ($M<0) {$M=$M+360;}
  0            
997 0           $M = $M*pi/180.0;
998              
999 0           $e=0.01675104-0.0000418*$T-0.000000126*$T*$T;
1000 0           $Ec=23.452294-0.0130125*$T-0.00000164*$T*$T+0.000000503*$T*$T*$T;
1001 0           $Ec=$Ec*pi/180.0;
1002              
1003 0           $y=tan(0.5*$Ec);
1004 0           $y=$y*$y;
1005 0           $ET=$y*sin(2*$L)-2*$e*sin($M)+4*$e*$y*sin($M)*cos(2*$L)-0.5*$y*$y*sin(4*$L)-5*0.25*$e*$e*sin(2*$M);
1006 0           $UT=$ET*180.0/(15.0*pi); # from radians to hours
1007              
1008 0           $Ec = $self->RoutinR2($M, $e);
1009 0           $tnv = sqrt((1+$e)/(1-$e))*tan(0.5*$Ec);
1010 0           $v = 2.0*atan($tnv);
1011 0           $tht = $L+$v-$M;
1012              
1013 0           ($RA, $Decl) = $self->EclipToEquator($tht,0);
1014              
1015 0           $K = 12-$UT-$TimeZone+$ObsLon*12.0/pi; # (Noon)
1016 0           $Transit = $K;
1017             # Sunrise and Sunset
1018              
1019 0           $angl = (-0.833333)*DToR; # Meeus p.98
1020 0           $T1=(sin($angl)-sin($Decl)*sin($ObsLat));
1021 0           $T2=(cos($Decl)*cos($ObsLat)); # p.38 Hour angle for the Sun
1022 0           $cH=$T1/$T2;
1023 0 0         if ($cH>1) {$RiseSetFlags = 16; $cH=1;} #At this day and place the sun does not rise or set
  0            
  0            
1024 0           $H = acos($cH);
1025 0           $H = $H*12.0/pi;
1026 0           $Rise = $K-$H; # Sunrise
1027 0           $Setting = $K+$H; # SunSet
1028              
1029 0           return ($JD, $Rise, $Transit, $Setting, $RA, $Decl, $RiseSetFlags);
1030             }
1031              
1032             #==========================================================
1033             # Function to obtain the ratio of the start time of Isha and Fajr at
1034             # a referenced latitude (45deg suggested by Rabita) to the night length
1035             # void GetRatior(int yg,int mg,int dg,double param[],double *IshRt,double *FajrRt)
1036             # ($IshFix, $FajrFix) = &GetRatior($yg, 12, 21, @param);
1037             sub GetRatior{
1038 0     0 0   my ($self, $yg, $mg, $dg, @param)=@_;
1039 0           my ($IshRt, $FajrRt);
1040              
1041 0           my $flagrs;
1042 0           my ($RA, $Decl);
1043 0           my ($Rise,$Transit, $Setting);
1044 0           my ($SINd, $COSd);
1045 0           my ($H, $angl, $cH);
1046 0           my ($MaxLat);
1047 0           my ($FjrRf, $IshRf);
1048 0           my ($Night);
1049              
1050 0 0         if ($param[2]<0) {$MaxLat= -fabs($param[9]);} else {$MaxLat= fabs($param[9]);}
  0            
  0            
1051              
1052 0           ($Rise, $Transit, $Setting, $RA, $Decl, $flagrs) =
1053             $self->SunParamr($yg, $mg, $dg, -$param[1], $MaxLat, -$param[5]);
1054              
1055              
1056 0           $SINd=sin($Decl)*sin($MaxLat);
1057 0           $COSd=cos($Decl)*cos($MaxLat);
1058 0           $Night=24-($Setting-$Rise); #Night Length
1059             #Fajr
1060 0           $angl= -$param[6];
1061 0           $cH=(sin($angl)-$SINd)/($COSd);
1062 0           $H=acos($cH);
1063 0           $H=$H*RToH; # convert radians to hours
1064 0           $FjrRf=$Transit-$H-$param[0]; #Fajr time
1065             #Isha
1066 0 0         if ($param[7]!=0) #if Ish angle not equal zero
1067             {
1068 0           $angl= -$param[7];
1069 0           $cH=(sin($angl)-$SINd)/($COSd);
1070 0           $H=acos($cH);
1071 0           $H=$H*RToH; #convert radians to hours
1072 0           $IshRf=$Transit+$H+$param[0];# Isha time, instead of Sunset+1.5h
1073             }
1074             else
1075             {
1076 0           $IshRf=$Setting+$param[10]; #Isha time OmAlqrah standard Sunset+1.5h
1077             }
1078 0           $IshRt= ($IshRf-$Setting)/$Night; #Isha time ratio
1079 0           $FajrRt=($Rise-$FjrRf)/$Night; #Fajr time ratio
1080 0           return ($IshRt, $FajrRt);
1081             }
1082             #==========================================================
1083             =BH2GA
1084             Name: BH2GA
1085             Type: Procedure
1086             Purpose: Finds Gdate(year,month,day) for Hdate(year,month,day=1)
1087             Arguments:
1088             Input: Hijrah date: year:yh, month:mh
1089             Output: Gregorian date: year:yg, month:mg, day:dg , day of week:dayweek
1090             and returns flag found:1 not found:0
1091             =cut
1092             # ($yg1, $mg1, $dg1, $dw2) = &BH2GA($yh2,$mh2);
1093             sub BH2GA{
1094 0     0 0   my ($self, $yh, $mh) = @_;
1095 0           my ($yg, $mg, $dg, $dayweek);
1096              
1097 0           my ($flag, $Dy, $m, $b);
1098 0           my ($JD);
1099 0           my ($GJD);
1100             #Make sure that the date is within the range of the tables
1101 0 0         if ($mh<1) {$mh=12;}
  0            
1102 0 0         if ($mh>12) {$mh=1;}
  0            
1103 0 0         if ($yh<$HStartYear) {$yh=$HStartYear;}
  0            
1104 0 0         if ($yh>$HEndYear) {$yh=$HEndYear;}
  0            
1105              
1106 0           $GJD = $self->HCalendarToJDA($yh,$mh,1);
1107 0           (undef, $yg, $mg, $dg) = $self->JDToGCalendar($GJD);
1108 0           $JD=$GJD;
1109 0           $dayweek=($JD+1)%7;
1110 0           $flag=1; #date has been found
1111 0           return ($flag, $yg, $mg, $dg, $dayweek);
1112             }
1113             #==========================================================
1114             =HCalendarToJDA
1115             Name: HCalendarToJDA
1116             Type: Function
1117             Purpose: convert Hdate(year,month,day) to Exact Julian Day
1118             Arguments:
1119             Input : Hijrah date: year:yh, month:mh, day:dh
1120             Output: The Exact Julian Day: JD
1121             =cut
1122             # $GJD= &HCalendarToJDA($yh,$mh,1);
1123             sub HCalendarToJDA{
1124 0     0 0   my ($self, $yh, $mh, $dh) = @_;
1125              
1126 0           my ($flag, $Dy, $m, $b);
1127 0           my ($JD);
1128 0           my ($GJD);
1129              
1130 0           $JD = int ($self->HCalendarToJD($yh,1,1));# estimate JD of the begining of the year
1131 0           $Dy = int ($MonthMap[$yh-$HStartYear]/4096); # Mask 1111000000000000
1132 0           $GJD=$JD-3+$Dy; #correct the JD value from stored tables
1133 0           $b = int ($MonthMap[$yh-$HStartYear]);
1134 0           $b=int ($b-$Dy*4096);
1135 0           for ($m=1; $m<$mh; $m++)
1136             {
1137 0           $flag = $b % 2; #Mask for the current month
1138 0 0         if ($flag) {$Dy=30;} else {$Dy=29;}
  0            
  0            
1139 0           $GJD=$GJD+$Dy; #Add the months lengths before mh
1140 0           $b=int (($b-$flag)/2);
1141             }
1142 0           $GJD=$GJD+$dh-1;
1143 0           return $GJD;
1144             }
1145             #==========================================================
1146             =HMonthLength
1147             Name: HMonthLength
1148             Type: Function
1149             Purpose: Obtains the month length
1150             Arguments:
1151             Input : Hijrah date: year:yh, month:mh
1152             Output: Month Length
1153             int HMonthLength(int yh,int mh)
1154             =cut
1155             sub HMonthLength{
1156 0     0 0   my ($self, $yh, $mh) = @_;
1157            
1158 0           my ($flag, $Dy, $N, $m, $b);
1159              
1160 0 0 0       if ($yh<$HStartYear || $yh>$HEndYear)
1161             {
1162 0           $flag=0;
1163 0           $Dy=0;
1164             }
1165             else
1166             {
1167 0           $Dy=int ($MonthMap[$yh-$HStartYear]/4096); # Mask 1111000000000000
1168 0           $b=int($MonthMap[$yh-$HStartYear]);
1169 0           $b=int($b-$Dy*4096);
1170 0           for($m=1;$m<=$mh;$m++)
1171             {
1172 0           $flag = $b % 2; #Mask for the current month
1173 0 0         if ($flag) {$Dy=30;} else {$Dy=29;}
  0            
  0            
1174 0           $b=int(($b-$flag)/2);
1175             }
1176             }
1177 0           return $Dy;
1178             }
1179             #==========================================================
1180             =DayInYear
1181             Name: DayInYear
1182             Type: Function
1183             Purpose: Obtains the day number in the yea
1184             Arguments:
1185             Input : Hijrah date: year:yh, month:mh day:dh
1186             Output: Day number in the Year
1187             int DayinYear(int yh,int mh,int dh)
1188             =cut
1189             sub DayinYear{
1190 0     0 0   my ($self, $yh, $mh, $dh) = @_;
1191            
1192 0           my ($flag, $Dy, $N, $m, $b, $DL);
1193              
1194 0 0 0       if ($yh<$HStartYear || $yh>$HEndYear)
1195             {
1196 0           $flag=0;
1197 0           $DL=0;
1198             }
1199             else
1200             {
1201 0           $Dy=int($MonthMap[$yh-$HStartYear]/4096); # Mask 1111000000000000
1202 0           $b=int($MonthMap[$yh-$HStartYear]);
1203 0           $b=int($b-$Dy*4096);
1204 0           $DL=0;
1205 0           for ($m=1; $m<=$mh; $m++)
1206             {
1207 0           $flag = $b % 2; #Mask for the current month
1208 0 0         if ($flag) {$Dy=30;} else {$Dy=29;}
  0            
  0            
1209 0           $b=int(($b-$flag)/2);
1210 0           $DL=int($DL+$Dy);
1211             }
1212 0           $DL=int($DL+$dh);
1213             }
1214 0           return $DL;
1215             }
1216             #==========================================================
1217             =HYearLength
1218             Name: HYearLength
1219             Type: Function
1220             Purpose: Obtains the year length
1221             Arguments:
1222             Input : Hijrah date: year:yh
1223             Output: Year Length
1224             int HYearLength(int yh)
1225             =cut
1226             sub HYearLength{
1227 0     0 0   my ($self, $yh) = @_;
1228            
1229 0           my ($flag, $Dy, $N, $m, $b, $YL);
1230              
1231 0 0 0       if ($yh<$HStartYear || $yh>$HEndYear)
1232             {
1233 0           $flag=0;
1234 0           $YL=0;
1235             }
1236             else
1237             {
1238 0           $Dy=int($MonthMap[$yh-$HStartYear]/4096); #Mask 1111000000000000
1239 0           $b=int($MonthMap[$yh-$HStartYear]);
1240 0           $b=int($b-$Dy*4096);
1241 0           $flag=$b % 2; #Mask for the current month
1242 0 0         if ($flag) {$YL=30;} else {$YL=29;}
  0            
  0            
1243 0           for ($m=2; $m<=12; $m++)
1244             {
1245 0           $flag = $b % 2; # Mask for the current month
1246 0 0         if ($flag) {$Dy=30;} else {$Dy=29;}
  0            
  0            
1247 0           $b=int(($b-$flag)/2);
1248 0           $YL=int($YL+$Dy);
1249             }
1250             }
1251 0           return $YL;
1252             }
1253              
1254             #==========================================================
1255             =G2HA
1256             Name: G2HA
1257             Type: Procedure
1258             Purpose: convert Gdate(year,month,day) to Hdate(year,month,day)
1259             Arguments:
1260             Input: Gregorian date: year:yg, month:mg, day:dg
1261             Output: Hijrah date: year:yh, month:mh, day:dh, day of week:dayweek
1262             and returns flag found:1 not found:0
1263             int G2HA(int yg,int mg, int dg,int *yh,int *mh,int *dh,int *dayweek)
1264             =cut
1265             sub G2HA{
1266 0     0 0   my ($self, $yg, $mg, $dg) = @_;
1267 0           my ($yh, $mh, $dh, $dayweek);
1268              
1269 0           my ($yh1, $mh1, $dh1);
1270 0           my ($yh2, $mh2, $dh2);
1271 0           my ($yg1, $mg1, $dg1);
1272 0           my ($yg2, $mg2, $dg2);
1273 0           my ($df, $dw2);
1274 0           my ($flag);
1275 0           my ($J);
1276 0           my ($GJD, $HJD);
1277              
1278              
1279 0           $GJD = $self->GCalendarToJD($yg, $mg, $dg+0.5); # find JD of Gdate
1280 0           ($yh1,$mh1, $dh1) = $self->JDToHCalendar($GJD); # estimate the Hdate that correspond to the Gdate
1281 0           $HJD = $self->HCalendarToJDA($yh1, $mh1, $dh1); #// get the exact Julian Day
1282 0           $df=int ($GJD-$HJD);
1283 0           $dh1=int($dh1+$df);
1284 0           while ($dh1>30)
1285             {
1286 0           $dh1=int($dh1-$self->HMonthLength($yh1, $mh1));
1287 0           $mh1++;
1288 0 0         if ($mh1>12) {$yh1++; $mh1=1;}
  0            
  0            
1289             }
1290 0 0         if ($dh1==30)
1291             {
1292 0           $mh2=int($mh1+1);
1293 0           $yh2=$yh1;
1294 0 0         if ($mh2>12) {$mh2=1;$yh2++;}
  0            
  0            
1295 0           ($yg1, $mg1, $dg1, $dw2) = $self->BH2GA(int($yh2), int($mh2));
1296 0           $yg1=int($yg1); $mg1= int($mg1); $dg1 = int($dg1); $dw2 = int($dw2);
  0            
  0            
  0            
1297 0 0         if ($dg==$dg1) {$yh1=$yh2;$mh1=$mh2;$dh1=1;} # Make sure that the month is 30days if not make adjustment
  0            
  0            
  0            
1298             }
1299            
1300 0           $J= int ($self->GCalendarToJD($yg,$mg,$dg)+2);
1301 0           $dayweek= $J % 7;
1302             #print "there $dayweek= $J % 7\n";
1303 0           $yh=$yh1;
1304 0           $mh=$mh1;
1305 0           $dh=$dh1;
1306 0           return ($flag, $yh, $mh, $dh, $dayweek);
1307             }
1308             #==========================================================
1309             =H2GA
1310             Name: H2GA
1311             Type: Procedure
1312             Purpose: convert Hdate(year,month,day) to Gdate(year,month,day)
1313             Arguments:
1314             Input/Ouput: Hijrah date: year:yh, month:mh, day:dh
1315             Output: Gregorian date: year:yg, month:mg, day:dg , day of week:dayweek
1316             and returns flag found:1 not found:0
1317             Note: The function will correct Hdate if day=30 and the month is 29 only
1318             int H2GA(int *yh,int *mh,int *dh, int *yg,int *mg, int *dg,int *dayweek)
1319             =cut
1320             sub H2GA{
1321 0     0 0   my ($self, $yh, $mh, $dh, $yg, $mg, $dg, $dayweek) = @_;
1322              
1323 0           my ($found,$yh1,$mh1,$yg1,$mg1,$dg1,$dw1);
1324              
1325             #make sure values are within the allowed values
1326 0 0         if ($dh>30) {$dh=1;$mh++;}
  0            
  0            
1327 0 0         if ($dh<1) {$dh=1; $mh--;}
  0            
  0            
1328 0 0         if ($mh>12) {$mh=1; $yh++;}
  0            
  0            
1329 0 0         if ($mh<1) {$mh=12;$yh--;}
  0            
  0            
1330              
1331             #find the date of the begining of the month
1332 0           ($found, $yg, $mg, $dg, $dayweek) = $self->BH2GA($yh, $mh);
1333 0           $dg=$dg+$dh-1;
1334            
1335 0           ($yg, $mg, $dg) = $self->GDateAjust($yg, $mg, $dg); # Make sure that dates are within the correct values
1336 0           $dayweek=$dayweek+$dh-1;
1337 0           $dayweek=$dayweek % 7;
1338              
1339             #find the date of the begining of the next month
1340 0 0         if ($dh==30)
1341             {
1342 0           $mh1=$mh+1;
1343 0           $yh1=$yh;
1344 0 0         if ($mh1>12) {$mh1=$mh1-12;$yh1++;}
  0            
  0            
1345 0           ($found, $yg1, $mg1, $dg1, $dw1) = $self->BH2GA($yh1, $mh1);
1346 0 0         if ($dg==$dg1) {$yh=$yh1;$mh=$mh1;$dh=1;} # Make sure that the month is 30days if not make adjustment
  0            
  0            
  0            
1347             }
1348              
1349 0           return ($found, $yg, $mg, $dg, $dayweek);
1350             }
1351             #==========================================================
1352             =JDToGCalendar
1353             Name: JDToGCalendar
1354             Type: Procedure
1355             Purpose: convert Julian Day to Gdate(year,month,day)
1356             Arguments:
1357             Input: The Julian Day: JD
1358             Output: Gregorian date: year:yy, month:mm, day:dd
1359             double JDToGCalendar(double JD, int *yy,int *mm, int *dd)
1360             =cut
1361             # (undef, $yg, $mg, $dg) = &JDToGCalendar($GJD);
1362             sub JDToGCalendar{
1363 0     0 0   my ($self, $JD) = @_;
1364 0           my ($yy, $mm, $dd);
1365 0           my ($A, $B, $F);
1366 0           my ($alpha, $C, $E);
1367 0           my ($D, $Z);
1368              
1369 0           $Z = floor ($JD + 0.5);
1370 0           $F = ($JD + 0.5) - $Z;
1371 0           $alpha = int (($Z - 1867216.25) / 36524.25);
1372 0           $A = $Z + 1 + $alpha - $alpha / 4;
1373 0           $B = $A + 1524;
1374 0           $C = int (($B - 122.1) / 365.25);
1375 0           $D = (365.25 * $C);
1376 0           $E = int (($B - $D) / 30.6001);
1377 0           $dd = $B - $D - floor (30.6001 * $E) + $F;
1378 0 0         if ($E < 14){
1379 0           $mm = $E - 1;
1380             }
1381             else{
1382 0           $mm = $E - 13;
1383             }
1384 0 0         if ($mm > 2){
1385 0           $yy = $C - 4716;
1386             }
1387             else{
1388 0           $yy = $C - 4715;
1389             }
1390              
1391 0           $F=$F*24.0;
1392 0           return ($F, $yy, $mm, $dd);
1393             }
1394             #==========================================================
1395             =GCalendarToJD
1396             Name: GCalendarToJD
1397             Type: Function
1398             Purpose: convert Gdate(year,month,day) to Julian Day
1399             Arguments:
1400             Input : Gregorian date: year:yy, month:mm, day:dd
1401             Output: The Julian Day: JD
1402             double GCalendarToJD(int yy,int mm, double dd)
1403             =cut
1404              
1405             sub GCalendarToJD{
1406 0     0 0   my ($self, $yy, $mm, $dd) = @_;
1407 0           my ($A, $B, $m, $y);
1408 0           my ($T1, $T2, $Tr);
1409             # it does not take care of 1582correction assumes correct calender from the past
1410              
1411 0 0         if ($mm > 2) {
1412 0           $y = int($yy);
1413 0           $m = int ($mm);
1414             }
1415             else {
1416 0           $y = int ($yy - 1);
1417 0           $m = $mm + 12;
1418             }
1419              
1420 0           $A = int($y / 100);
1421 0           $B = int(2 - $A + $A / 4);
1422 0           $T1 = $self->ip (365.25 * ($y + 4716));
1423 0           $T2 = $self->ip (30.6001 * ($m + 1));
1424 0           $Tr = $T1+ $T2 + $dd + $B - 1524.5 ;
1425 0           return (int $Tr);
1426             }
1427             #==========================================================
1428             =GLeapYear
1429             Name: GLeapYear
1430             Type: Function
1431             Purpose: Determines if Gdate(year) is leap or not
1432             Arguments:
1433             Input : Gregorian date: year
1434             Output: 0:year not leap 1:year is leap
1435             int GLeapYear(int year)
1436             =cut
1437             sub GLeapYear{
1438 0     0 0   my ($self, $year) = @_;
1439 0           my ($T);
1440              
1441 0           $T=0;
1442 0 0         if ($year % 4 ==0) {$T=1;} # leap_year=1;
  0            
1443 0 0         if ($year % 100 == 0)
1444             {
1445 0           $T=0; # years=100,200,300,500,... are not leap years
1446 0 0         if ($year % 400 ==0) {$T=1;} # years=400,800,1200,1600,2000,2400 are leap years
  0            
1447             }
1448 0           return ($T);
1449             }
1450             #==========================================================
1451             =GDateAjust
1452             Name: GDateAjust
1453             Type: Procedure
1454             Purpose: Adjust the G Dates by making sure that the month lengths
1455             are correct if not so take the extra days to next month or year
1456             Arguments:
1457             Input: Gregorian date: year:yg, month:mg, day:dg
1458             Output: corrected Gregorian date: year:yg, month:mg, day:dg
1459             void GDateAjust(int *yg,int *mg,int *dg)
1460             =cut
1461             sub GDateAjust{
1462 0     0 0   my ($self, $yg, $mg, $dg) = @_;
1463 0           my ($dys);
1464              
1465             # Make sure that dates are within the correct values
1466             # Underflow
1467 0 0         if ( $mg<1) #months underflow
1468             {
1469 0           $mg=12+$mg; # plus as the underflow months is negative
1470 0           $yg=$yg-1;
1471             }
1472              
1473 0 0         if ($dg<1) # days underflow
1474             {
1475 0           $mg= $mg-1; # month becomes the previous month
1476 0           $dg=$gmonth[$mg]+$dg; # number of days of the month less the underflow days (it is plus as the sign of the day is negative)
1477 0 0         if ($mg==2) {$dg=$dg+ $self->GLeapYear($yg)};
  0            
1478 0 0         if ($mg<1) # months underflow
1479             {
1480 0           $mg=12+$mg; #plus as the underflow months is negative
1481 0           $yg=$yg-1;
1482             }
1483             }
1484              
1485             #Overflow
1486 0 0         if ($mg>12) # months
1487             {
1488 0           $mg=$mg-12;
1489 0           $yg=$yg+1;
1490             }
1491              
1492 0 0         if($mg==2){
1493 0           $dys=int ($gmonth[$mg]+ $self->GLeapYear($yg) ); # number of days in the current month
1494             }
1495             else{
1496 0           $dys=int($gmonth[$mg]);
1497             }
1498              
1499 0 0         if ($dg>$dys) # days overflow
1500             {
1501 0           $dg=$dg-$dys;
1502 0           $mg=$mg+1;
1503 0 0         if ($mg==2)
1504             {
1505 0           $dys= int($gmonth[$mg]+ $self->GLeapYear($yg));# number of days in the current month
1506 0 0         if ($dg>$dys)
1507             {
1508 0           $dg=$dg-$dys;
1509 0           $mg=$mg+1;
1510             }
1511             }
1512              
1513 0 0         if ($mg>12) # months
1514             {
1515 0           $mg=$mg-12;
1516 0           $yg=$yg+1;
1517             }
1518             }
1519 0           return ($yg, $mg, $dg);
1520             }
1521             #==========================================================
1522             =DayWeek
1523             The day of the week is obtained as
1524             Dy=(Julian+1)%7
1525             Dy=0 Sunday
1526             Dy=1 Monday
1527             ...
1528             Dy=6 Saturday
1529             int DayWeek(long JulianD)
1530             =cut
1531             sub DayWeek{
1532 0     0 0   my ($self, $JulianD) = @_;
1533 0           my ($Dy);
1534 0           $Dy= int (($JulianD+1) % 7);
1535 0           return ($Dy);
1536             }
1537             #==========================================================
1538             =HCalendarToJD
1539             Name: HCalendarToJD
1540             Type: Function
1541             Purpose: convert Hdate(year,month,day) to estimated Julian Day
1542             Arguments:
1543             Input : Hijrah date: year:yh, month:mh, day:dh
1544             Output: The Estimated Julian Day: JD
1545             double HCalendarToJD(int yh,int mh,int dh)
1546             =cut
1547             sub HCalendarToJD{
1548 0     0 0   my ($self, $yh, $mh, $dh) = @_;
1549 0           my ($md, $yd);
1550              
1551             #Estimating The JD for hijrah dates
1552             #this is an approximate JD for the given hijrah date
1553 0           $md=($mh-1.0)*29.530589;
1554 0           $yd=($yh-1.0)*354.367068+$md+$dh-1.0;
1555 0           $yd=$yd+1948439.0; # add JD for 18/7/622 first Hijrah date
1556 0           return $yd;
1557             }
1558             #==========================================================
1559             =JDToHCalendar
1560             Name: JDToHCalendar
1561             Type: Procedure
1562             Purpose: convert Julian Day to estimated Hdate(year,month,day)
1563             Arguments:
1564             Input: The Julian Day: JD
1565             Output : Hijrah date: year:yh, month:mh, day:dh
1566             void JDToHCalendar(double JD,int *yh,int *mh,int *dh)
1567             =cut
1568             #Estimating the hijrah date from JD
1569             sub JDToHCalendar{
1570 0     0 0   my ($self, $JD) = @_;
1571 0           my ($yh, $mh, $dh);
1572 0           my ($md, $yd);
1573              
1574 0           $yd=$JD-1948439.0; # subtract JD for 18/7/622 first Hijrah date
1575 0           $md= $self->mod ($yd, 354.367068);
1576 0           $dh= int($self->mod($md+0.5, 29.530589)+1);
1577 0           $mh=int(($md/29.530589)+1);
1578 0           $yd=$yd-$md;
1579 0           $yh=int($yd/354.367068+1);
1580 0 0         if ($dh>30) {$dh=int($dh-30); $mh++;}
  0            
  0            
1581 0 0         if ($mh>12) {$mh=int($mh-12); $yh++;}
  0            
  0            
1582 0           return ($yh, $mh, $dh);
1583             }
1584             #==========================================================
1585             =JDToHACalendar
1586             Name: JDToHACalendar
1587             Type: Procedure
1588             Purpose: convert Julian Day to Hdate(year,month,day)
1589             Arguments:
1590             Input: The Julian Day: JD
1591             Output : Hijrah date: year:yh, month:mh, day:dh
1592             void JDToHACalendar(double JD,int *yh,int *mh,int *dh)
1593             =cut
1594             sub JDToHACalendar{
1595 0     0 0   my ($self, $JD) = @_;
1596 0           my ($yh, $mh, $dh);
1597 0           my ($yh1, $mh1,$dh1);
1598 0           my ($yh2,$mh2,$dh2);
1599 0           my ($yg1,$mg1,$dg1);
1600 0           my ($yg2,$mg2,$dg2);
1601 0           my ($df,$dw2);
1602 0           my ($flag);
1603 0           my ($J);
1604 0           my ($GJD, $HJD);
1605              
1606 0           ($yh1,$mh1,$dh1) = $self->JDToHCalendar($JD); # estimate the Hdate that correspond to the Gdate
1607 0           $HJD = $self->HCalendarToJDA(int($yh1), int($mh1), int($dh1)); #// get the exact Julian Day
1608 0           $df= int($JD+0.5-$HJD);
1609 0           $dh1= int($dh1+$df);
1610 0           while ($dh1>30)
1611             {
1612 0           $dh1= int($dh1-$self->HMonthLength($yh1,$mh1));
1613 0           $mh1++;
1614 0 0         if ($mh1>12) {$yh1++;$mh1=1;}
  0            
  0            
1615             }
1616 0 0 0       if ($dh1==30 && $self->HMonthLength($yh1,$mh1)<30)
1617             {
1618 0           $dh1=1;$mh1++;
  0            
1619             }
1620 0 0         if ($mh1>12)
1621             {
1622 0           $mh1=1;$yh1++;
  0            
1623             }
1624              
1625             #// J=JD+2; *dayweek=J%7;
1626 0           $yh=$yh1;
1627 0           $mh=$mh1;
1628 0           $dh=$dh1;
1629              
1630 0           return ($yh, $mh, $dh);
1631             }
1632             #==========================================================
1633             # Purpose: return the integral part of a double value.
1634             sub ip{
1635 0     0 0   my ($self, $x) = @_;
1636 0           my ($fractional, $integral);
1637 0           ($fractional, $integral) = POSIX::modf($x);
1638 0           return $integral;
1639             }
1640             #==========================================================
1641             # Name: mod
1642             # Purpose: The mod operation for doubles x mod y
1643             sub mod{
1644 0     0 0   my ($self, $x, $y) = @_;
1645 0           my ($r, $d);
1646              
1647 0           $d=$x/$y;
1648 0           $r=int ($d);
1649 0 0         if ($r<0) {$r--;}
  0            
1650 0           $d=$x-$y*$r;
1651 0           $r= int($d);
1652 0           return $r;
1653             }
1654             #==========================================================
1655             #Purpose: returns 0 for incorrect Hijri date and 1 for correct date
1656             sub IsValid{
1657 0     0 0   my ($self, $yh, $mh, $dh) = @_;
1658 0           my ($valid);
1659            
1660 0           $valid=1;
1661 0 0 0       if ($yh<$HStartYear || $yh>$HEndYear) {$valid=0;}
  0            
1662 0 0 0       if ($mh<1 || $mh>12 || $dh<1){
      0        
1663 0           $valid=0;
1664             }
1665             else{
1666 0 0         if ($dh>$self->HMonthLength($yh,$mh)) {$valid=0;}
  0            
1667             }
1668 0           return $valid;
1669             }
1670             #==========================================================
1671             sub TimeZoneUS{
1672 0     0 0   my ($self, $state) = @_;
1673            
1674 0 0         if (exists $TimeZoneUS{$state}) {
1675 0           return $TimeZoneUS{$state};
1676             }
1677 0           return undef;
1678             }
1679             #==========================================================
1680             sub TimeZoneCountry{
1681 0     0 0   my ($self, $country) = @_;
1682            
1683 0 0         if (exists $TimeZone{$country}) {
1684 0           return $TimeZone{$country};
1685             }
1686 0           return undef;
1687             }
1688             #==========================================================
1689             1;
1690             __END__