File Coverage

lib/Astro/Montenbruck/Time.pm
Criterion Covered Total %
statement 72 73 98.6
branch 15 16 93.7
condition 6 8 75.0
subroutine 21 22 95.4
pod 13 15 86.6
total 127 134 94.7


line stmt bran cond sub pod time code
1             package Astro::Montenbruck::Time;
2              
3 9     9   178098 use warnings;
  9         31  
  9         273  
4 9     9   40 use strict;
  9         32  
  9         301  
5              
6             our $VERSION = 0.01;
7              
8 9     9   48 use Exporter qw/import/;
  9         15  
  9         1295  
9              
10             our $SEC_PER_DAY = 86400; # Seconds per day
11             our $SEC_PER_CEN = 3155760000;
12             our $J2000 = 2451545; # Standard Julian Date for 1.1.2000 12:00
13             our $J1900 = 2415020
14             ; # Standard Julian Date for 31.12.1899 12:00 (astronomical epoch 1900.0)
15             our $SOLAR_TO_SIDEREAL = 1.002737909350795
16             ; # Difference in between Sidereal and Solar hour (the former is shorter)
17             our $GREGORIAN_START = 15821004; # Start of Gregorian calendar (YYYYMMDD)
18             our $JD_UNIX_EPOCH = _gmtime2jd( gmtime(0) )
19             ; # Standard Julian date for the beginning of Unix epoch, Jan 1 1970 on most Unix systems
20              
21             our %EXPORT_TAGS = (
22             all => [
23             qw/jd_cent after_gregorian cal2jd jd2cal jd0 unix2jd jd2mjd mjd2jd
24             jd2unix jdnow t1900 jd2gst jd2lst
25             is_leapyear day_of_year
26             $SEC_PER_DAY $SEC_PER_CEN $J2000 $J1900 $GREGORIAN_START $JD_UNIX_EPOCH/
27             ],
28             );
29              
30             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
31              
32 9     9   55 use POSIX;
  9         17  
  9         89  
33 9     9   18772 use Astro::Montenbruck::MathUtils qw/polynome ddd frac to_range/;
  9         34  
  9         7820  
34              
35             sub after_gregorian {
36 75     75 1 1972 my $y = shift;
37 75         87 my $m = shift;
38 75         114 my $d = shift;
39 75         168 my %arg = ( gregorian_start => $GREGORIAN_START, @_ );
40 75 100       208 return 0 unless defined $arg{gregorian_start};
41 74         197 polynome( 100, $d, $m, $y ) >= $arg{gregorian_start};
42             }
43              
44             sub cal2jd {
45 72     72 1 11808 my $ye = shift;
46 72         103 my $mo = shift;
47 72         102 my $da = shift;
48 72         192 my %arg = ( gregorian_start => $GREGORIAN_START, @_ );
49              
50 72         148 my $j = $da + 1720996.5;
51 72 100       215 my ( $m, $y ) = ( $mo > 2 ) ? ( $mo, $ye ) : ( $mo + 12, $ye - 1 );
52 72 100       223 if ( after_gregorian( $ye, $mo, $da, %arg ) ) {
53 68         232 $j += int( $y / 400 ) - int( $y / 100 ) + int( $y / 4 );
54             }
55             else {
56 4         10 $j += int( ( $y + 4716 ) / 4 ) - 1181;
57             }
58 72         361 $j + 365 * $y + floor( 30.6001 * ( $m + 1 ) );
59             }
60              
61             sub jd2cal {
62 18     18 1 7418 my $jd = shift;
63 18         44 my %arg = ( gregorian => 1, @_ );
64              
65 18         65 my ( $f, $i ) = modf( $jd - $J1900 + 0.5 );
66 18 100 100     92 if ( $arg{gregorian} && $i > -115860 ) {
67 13         44 my $a = floor( $i / 36524.25 + 9.9835726e-1 ) + 14;
68 13         44 $i += 1 + $a - floor( $a / 4 );
69             }
70              
71 18         39 my $b = floor( $i / 365.25 + 8.02601e-1 );
72 18         40 my $c = $i - floor( 365.25 * $b + 7.50001e-1 ) + 416;
73 18         35 my $g = floor( $c / 30.6001 );
74 18         38 my $da = $c - floor( 30.6001 * $g ) + $f;
75 18 100       34 my $mo = $g - ( $g > 13.5 ? 13 : 1 );
76 18 100       38 my $ye = $b + ( $mo < 2.5 ? 1900 : 1899 );
77 18         53 $ye, $mo, $da;
78             }
79              
80             sub jd0 {
81 8     8 1 3007 my $j = shift;
82 8         37 floor( $j - 0.5 ) + 0.5;
83             }
84              
85             sub unix2jd {
86 3     3 1 3830 $JD_UNIX_EPOCH + $_[0] / $SEC_PER_DAY;
87             }
88              
89             sub jd2unix {
90 3     3 1 4946 int( ( $_[0] - $JD_UNIX_EPOCH ) * $SEC_PER_DAY );
91             }
92              
93             sub _gmtime2jd {
94 9     9   77 cal2jd( $_[5] + 1900, $_[4] + 1, $_[3] + ddd( @_[ 2, 1, 0 ] ) / 24 );
95             }
96              
97             sub jdnow {
98 0     0 1 0 _gmtime2jd( gmtime() );
99             }
100              
101             sub jd2mjd {
102 3     3 1 6958 $_[0] - $J2000;
103             }
104              
105             sub mjd2jd {
106 3     3 1 3799 $_[0] + $J2000;
107             }
108              
109             # converts Julian date to period in centuries since epoch
110             # Arguments:
111             # julian date
112             # julian date corresponding to the epoch start
113             sub _t {
114 1367     1367   2387 my ( $jd, $epoch ) = @_;
115 1367         3088 ( $jd - $epoch ) / 36525;
116             }
117              
118             sub jd_cent {
119 1362     1362 1 5645 _t( $_[0], $J2000 );
120             }
121              
122             sub t1900 {
123 5     5 1 351 _t( $_[0], $J1900 );
124             }
125              
126             sub jd2gst {
127 4     4 1 6 my $jh = shift;
128 4         7 my $j0 = jd0($jh);
129 4         7 my $s0 = polynome( t1900($j0), 0.276919398, 100.0021359, 0.000001075 );
130 4         17 24 * ( frac($s0) + abs( $jh - $j0 ) * $SOLAR_TO_SIDEREAL );
131             }
132              
133             sub jd2lst {
134 4     4 1 5438 my ( $jd, $lon ) = @_;
135 4   50     19 $lon //= 0;
136 4         8 to_range( jd2gst($jd) - $lon / 15, 24 );
137             }
138              
139             sub is_leapyear {
140 14     14 0 7622 my $yr = shift;
141 14         29 my %arg = ( gregorian => 1, @_ );
142 14         19 $yr = int($yr);
143             return $arg{gregorian}
144 14 50 66     93 ? ( $yr % 4 == 0 ) && ( ( $yr % 100 != 0 ) || ( $yr % 400 == 0 ) )
145             : $yr % 4 == 0;
146             }
147              
148             sub day_of_year {
149 4     4 0 4904 my $yr = shift;
150 4         6 my $mo = shift;
151 4         5 my $dy = shift;
152              
153 4 100       9 my $k = is_leapyear($yr, @_) ? 1 : 2;
154 4         8 $dy = int($dy);
155 4         28 int(275 * $mo / 9.0) - ($k * int(($mo + 9) / 12.0)) + $dy - 30
156             }
157              
158             1;
159              
160             __END__
161              
162             =pod
163              
164             =encoding UTF-8
165              
166             =head1 NAME
167              
168             Astro::Montenbruck::Time - Time-related routines
169              
170             =head1 VERSION
171              
172             Version 0.01
173              
174             =head1 SYNOPSIS
175              
176             use Astro::Montenbruck::Time qw/:all/;
177              
178             # Convert Gregorian (new-style) date to old-style date
179             my $j = cal2jd(1799, 6, 6); # Standard Julian date of A.Pushkin's birthday
180             my $d = jd2cal($j, gregorian => 0) # (1799, 5, 25) = May 26, 1799.
181              
182             # Julian date in centuries since epoch 2000.0
183             my $t = jd_cent($j); # -2.0056810403833
184             ...
185              
186             =head1 DESCRIPTION
187              
188             Library of date/time manipulation routines for practical astronomy. Most of them
189             are based on so called I<Julian date (JD)>, which is the number of days elapsed
190             since mean UT noon of B<January 1st 4713 BC>. This system of time measurement is
191             widely adopted by the astronomers.
192              
193             =head2 JD and MJD
194              
195             Many routines use Modified Julian date, which starts at B<2000 January 0>
196             (2000 January 1.0) as the starting point.
197              
198             =head2 Civil year vs astronomical year
199              
200             There is disagreement between astronomers and historians about how to count the
201             years preceding the year 1. Astronomers generally use zero-based system. The
202             year before the year +1, is the year zero, and the year preceding the latter is
203             the year -1. The year which the historians call 585 B.C. is actually the year
204             -584.
205              
206             In this module all subroutines accepting year assume that B<there is year zero>.
207             Thus, the sequence of years is: C<BC -3, -2, -1, 0, 1, 2, 3, AD>.
208              
209             =head2 Date and Time
210              
211             Time is represented by fractional part of a day. For example, 7h30m UT
212             is C<(7 + 30 / 60) / 24 = 0.3125>.
213              
214             =head3 Gregorian calendar
215              
216             I<Civil calendar> in most cases means I<proleptic Gregorian calendar>. it is
217             assumed that Gregorian calendar started at Oct. 4, 1582, when it was first
218             adopted in several European countries. Many other countries still used the older
219             Julian calendar. In Soviet Russia, for instance, Gregorian system was accepted
220             on Jan 26, 1918. See:
221             L<https://en.wikipedia.org/wiki/Gregorian_calendar#Adoption_of_the_Gregorian_Calendar>
222              
223              
224             =head1 EXPORTED CONSTANTS
225              
226             =over
227              
228             =item * C<$SEC_PER_DAY> seconds per day (86400)
229              
230             =item * C<$SEC_PER_CEN> seconds per century (3155760000)
231              
232             =item * C<$J2000> Standard Julian date for start of epoch 2000,0 (2451545)
233              
234             =item * C<$J1900> Standard Julian date for start of epoch 1900,0 (2415020)
235              
236             =item * C<$GREGORIAN_START> Start of Gregorian calendar, YYYYMMDD (15821004)
237              
238             =item * C<$JD_UNIX_EPOCH> Standard Julian date for start of the Unix epoch
239              
240             =back
241              
242             =head1 EXPORTED FUNCTIONS
243              
244             =over
245              
246             =item * L</jd_cent($jd)>
247              
248             =item * L</after_gregorian($year, $month, $date)>
249              
250             =item * L</cal2jd($year, $month, $date)>
251              
252             =item * L</jd2cal($jd)>
253              
254             =item * L</jd0($jd)>
255              
256             =item * L</unix2jd($seconds)>
257              
258             =item * L</jd2unix($jd)>
259              
260             =item * L</jdnow()>
261              
262             =item * L</jd2mjd($jd)>
263              
264             =item * L</mjd2jd($mjd)>
265              
266             =item * L</jd_cent($jd)>
267              
268             =item * L</t1900($jd)>
269              
270              
271             =item * L</jd2dt($jd)>
272              
273             =item * L</jd2te($jd)>
274              
275             =item * L</jd2gst($jd)>
276              
277             =item * L</jd2lst($jd, $lng)>
278              
279             =back
280              
281             =head1 FUNCTIONS
282              
283             =head2 jd_cent($jd)
284              
285             Convert Standard Julian Date to centuries passed since epoch 2000.0
286              
287             =head2 after_gregorian($year, $month, $date, gregorian_start => $YYYYMMDD )
288              
289             Does the given date fall to period after Gregorian calendar?
290              
291             =head3 Positional Arguments
292              
293             =over
294              
295             =item * B<year> (astronomic, zero-based)
296              
297             =item * B<month> (1-12)
298              
299             =item * B<date> UTC date (1-31) with hours and minutes as decimal part
300              
301             =back
302              
303             =head3 Optional Named Arguments
304              
305             =over
306              
307             =item *
308              
309             B<gregorian_start> — start of Gregorian calendar. Default value is
310             B<15821004> If the date is Julian ("old style"), use C<undef> value.
311             To provide non-standard start of Gregorian calendar, provide a number
312             in format YYYYMMDDD, e.g. C<19180126> for Jan 26, 1918.
313              
314             =back
315              
316             =head3 Returns
317              
318             I<true> or I<false>=.
319              
320             =head2 cal2jd($year, $month, $date)
321              
322             Convert civil date/time to Standard Julian date.
323              
324             If C<gregorian_start> argument is not provided, it is assumed that this is a date
325             of I<Proleptic Gregorian calendar>, which started at Oct. 4, 1582.
326              
327             =head3 Positional Arguments:
328              
329             =over
330              
331             =item * B<year> (astronomic, zero-based)
332              
333             =item * B<month> (1-12)
334              
335             =item * B<date> UTC date (1-31) with hours and minutes as decimal part
336              
337             =back
338              
339             =head3 Optional Named Arguments
340              
341             =over
342              
343             =item *
344              
345             gregorian_start — start of Gregorian calendar. Default value is
346             B<15821004> If the date is Julian ("old style"), use C<undef> value.
347             To provide non-standard start of Gregorian calendar, provide a number
348             in format YYYYMMDDD, e.g. C<19180126> for Jan 26, 1918.
349              
350             =back
351              
352             =head3 Returns
353              
354             Standard Julian date
355              
356             =head2 jd2cal($jd)
357              
358             Convert Standard Julian date to civil date/time
359              
360             =head3 Positional Arguments
361              
362             Standard Julian Date
363              
364             =head3 Optional Named Arguments
365              
366             =over
367              
368             =item * gregorian — if i<true>, the result will be old-style (Julian) date
369              
370             =back
371              
372             =head3 Returns
373              
374             A list corresponding to the input values of L</cal2jd($year, $month, $date)> function.
375             The date is given in the proleptic Gregorian calendar system unless B<gregorian>
376             flag is set to I<true>.
377              
378             =head2 jd0($jd)
379              
380             Given Standard Julian Date, calculate Standard Julian date for midnight of the same date.
381              
382             =head2 unix2jd($seconds)
383              
384             Given Unix (POSIX) time, in seconds, convert it to Standard Julian date.
385              
386             =head2 jd2unix($jd)
387              
388             Given a Standard Julian Date, convert it to Unix time, in seconds since start of
389             Unix epoch.
390              
391             If JD falls before start of the epoch, result will be negative and thus, unusable
392             for Unix-specific functions like B<localtime()>.
393              
394             =head2 jdnow()
395              
396             Standard Julian date for the current moment.
397              
398             =head2 jd2mjd($jd)
399              
400             Standard to Modified Julian date.
401              
402             =head2 mjd2jd($mjd)
403              
404             Modified to Standard Julian date.
405              
406             =head2 jd_cent($jd)
407              
408             Given aI<Standard Julian date>, calculate time in centuries since epoch 2000.0.
409              
410             =head2 t1900($jd)
411              
412             Given a I<Standard Julian date>, calculate time in centuries since epoch 1900.0.
413              
414              
415             =head2 jd2gst($jd)
416              
417             Given I<Standard Julian date>, calculate I<True Greenwich Sidereal time>.
418              
419             =head2 jd2lst($jd, $lng)
420              
421             Givan I<Standard Julian date>, calculate true I<Local Sidereal time>.
422              
423             =head3 Arguments
424              
425             =over
426              
427             =item * $jd — Standard Julian date
428              
429             =item * $lng — Geographic longitude, negative for Eastern longitude, 0 by default
430              
431             =back
432              
433              
434             =head1 AUTHOR
435              
436             Sergey Krushinsky, C<< <krushi at cpan.org> >>
437              
438             =head1 LICENSE AND COPYRIGHT
439              
440             Copyright 2010-2019 Sergey Krushinsky.
441              
442             This program is free software; you can redistribute it and/or modify it
443             under the terms of either: the GNU General Public License as published
444             by the Free Software Foundation; or the Artistic License.
445              
446             See http://dev.perl.org/licenses/ for more information.
447              
448             =cut