File Coverage

blib/lib/Time/UTC_SLS.pm
Criterion Covered Total %
statement 49 49 100.0
branch 20 22 90.9
condition 8 12 66.6
subroutine 11 11 100.0
pod 2 2 100.0
total 90 96 93.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Time::UTC_SLS - UTC with Smoothed Leap Seconds
4              
5             =head1 SYNOPSIS
6              
7             use Time::UTC_SLS qw(utc_to_utcsls utcsls_to_utc);
8              
9             $mjd = utc_to_utcsls($day, $secs);
10             ($day, $secs) = utcsls_to_day($mjd);
11              
12             use Time::UTC_SLS qw(
13             utc_day_to_mjdn utc_mjdn_to_day
14             utc_day_to_cjdn utc_cjdn_to_day
15             );
16              
17             $mjdn = utc_day_to_mjdn($day);
18             $day = utc_mjdn_to_day($mjdn);
19              
20             $cjdn = utc_day_to_cjdn($day);
21             $day = utc_cjdn_to_day($cjdn);
22              
23             =head1 DESCRIPTION
24              
25             Coordinated Universal Time (UTC) is a time scale with days of unequal
26             lengths, due to leap seconds, in order to keep in step with both Terran
27             rotation (Universal Time, UT) and International Atomic Time (TAI).
28             Some applications that wish to use a time scale that maintains both of
29             these relations can't cope with unequal day lengths, and so cannot use
30             UTC properly. UTC with Smoothed Leap Seconds (UTC-SLS) is another option
31             in such cases. UTC-SLS is a time scale that usually matches UTC exactly
32             but changes rate in the time leading up to a leap second in order to
33             make every day appear to be exactly the same length.
34              
35             On a normal UTC day, of length 86400 UTC seconds, UTC and UTC-SLS
36             behave identically. On a day with a leap second, thus having 86401 or
37             (theoretically) 86399 UTC seconds, UTC and UTC-SLS behave identically
38             for most of the day, but the last 1000 UTC seconds correspond to 999 or
39             (theoretically) 1001 UTC-SLS seconds. Thus every UTC-SLS day has exactly
40             86400 UTC-SLS seconds. UTC and UTC-SLS are equal on every half hour,
41             and in particular the day boundaries (at midnight) are in the same place
42             on both time scales. See L
43             for further explanation.
44              
45             UTC-SLS is defined for the post-1972 form of UTC, using leap seconds.
46             The prior form, from 1961, using `rubber seconds' as well as leaps,
47             could be treated in a similar manner, but the exact algorithm has not
48             been defined. The rubber seconds system was itself trying to achieve
49             part of what UTC-SLS does.
50              
51             This module represents instants on the UTC scale by the combination of
52             a day number and a number of seconds since midnight within the day.
53             In this module the day number is the integral number of days since
54             1958-01-01, which is the epoch of TAI. This is the convention used by
55             the C module. Instants on the UTC-SLS scale are represented
56             by a Modified Julian Date, which is a fractional count of days since
57             1858-11-17T00Z. The MJD is a suitable interchange format between
58             date-manipulation modules.
59              
60             All numbers in this API are C objects. All numeric function
61             arguments must be Cs, and all numeric values returned are
62             likewise Cs.
63              
64             =cut
65              
66             package Time::UTC_SLS;
67              
68 3     3   75894 { use 5.006; }
  3         10  
  3         124  
69 3     3   17 use warnings;
  3         5  
  3         88  
70 3     3   24 use strict;
  3         13  
  3         240  
71              
72 3     3   18 use Carp qw(croak);
  3         5  
  3         266  
73 3     3   3540 use Math::BigRat 0.04;
  3         251972  
  3         20  
74 3         467 use Time::UTC 0.007 qw(
75             utc_day_seconds
76             utc_day_to_mjdn utc_mjdn_to_day
77             utc_day_to_cjdn utc_cjdn_to_day
78 3     3   7504 );
  3         350399  
79              
80             our $VERSION = "0.004";
81              
82 3     3   44 use parent "Exporter";
  3         8  
  3         21  
83             our @EXPORT_OK = qw(
84             utc_to_utcsls utcsls_to_utc
85             utc_day_to_mjdn utc_mjdn_to_day
86             utc_day_to_cjdn utc_cjdn_to_day
87             );
88              
89             =head1 FUNCTIONS
90              
91             =over
92              
93             =item utc_to_utcsls(DAY, SECS)
94              
95             Converts from UTC to UTC-SLS. The input is a UTC instant expressed as a
96             day number and a number of seconds since midnight, both as C
97             objects. Returns the corresponding UTC-SLS instant expressed as a
98             Modified Julian Date, as a C object.
99              
100             =cut
101              
102 3     3   257 use constant UTCSLS_START_DAY => Math::BigRat->new(5113);
  3         6  
  3         15  
103 3     3   530 use constant TAI_EPOCH_MJD => Math::BigRat->new(36204);
  3         15  
  3         13  
104              
105             sub utc_to_utcsls($$) {
106 26     26 1 25096 my($day, $secs) = @_;
107 26 100       95 croak "day $day precedes the start of UTC-SLS"
108             unless $day >= UTCSLS_START_DAY;
109 22 100 100     2070 unless($secs >= 0 && $secs <= 85399) {
110 13         3984 my $day_len = utc_day_seconds($day);
111 13 100 100     247099 croak "$secs seconds is out of range for a $day_len second day"
112             if $secs < 0 || $secs >= $day_len;
113 9 100       2224 if($day_len != 86400) {
114 7 50 33     1397 croak "UTC-SLS is not defined for a $day_len ".
115             "second day"
116             unless $day_len == 86399 || $day_len == 86401;
117 7         2693 my $slew_from = $day_len - 1000;
118 7 100       2174 $secs = $slew_from + (86400 - $slew_from) *
119             ($secs - $slew_from)/1000
120             if $secs > $slew_from;
121             }
122             }
123 18         9581 return utc_day_to_mjdn($day) + $secs/86400;
124             }
125              
126             =item utcsls_to_utc(MJD)
127              
128             Converts from UTC-SLS to UTC. The input is a UTC-SLS instant expressed
129             as a Modified Julian Date, as a C object. Returns a list of
130             two values, giving the corresponding UTC instant expressed as a day number
131             and a number of seconds since midnight, both as C objects.
132              
133             =cut
134              
135             sub utcsls_to_utc($) {
136 19     19 1 21601 my($mjd) = @_;
137 19         66 my $mjdn = $mjd->copy->bfloor;
138 19         1042 my $secs = ($mjd - $mjdn) * 86400;
139 19         9857 my $day = $mjdn - TAI_EPOCH_MJD;
140 19 100       3174 croak "day $day precedes the start of UTC-SLS"
141             unless $day >= UTCSLS_START_DAY;
142 18 100       1533 unless($secs <= 85399) {
143 9         1649 my $day_len = utc_day_seconds($day);
144 9 100       1966 if($day_len != 86400) {
145 7 50 33     1287 croak "UTC-SLS is not defined for a $day_len ".
146             "second day"
147             unless $day_len == 86399 || $day_len == 86401;
148 7         2755 my $slew_from = $day_len - 1000;
149 7 100       2000 $secs = $slew_from + 1000 * ($secs - $slew_from)/
150             (86400 - $slew_from)
151             if $secs > $slew_from;
152             }
153             }
154 18         7895 return ($day, $secs);
155             }
156              
157             =item utc_day_to_mjdn(DAY)
158              
159             Takes a day number (days since the TAI epoch), as a C
160             object, and returns the corresponding Modified Julian Day Number
161             (a number of days since 1858-11-17 UT), as a C object.
162             MJDN is a standard numbering for days in Universal Time. There is no
163             bound on the permissible day numbers; the function is not limited to
164             days for which UTC-SLS is defined.
165              
166             =item utc_mjdn_to_day(MJDN)
167              
168             This performs the reverse of the translation that C does.
169             It takes a Modified Julian Day Number, as a C object,
170             and returns the number of days since the TAI epoch, as a C
171             object. It does not impose any limit on the range.
172              
173             =item utc_day_to_cjdn(DAY)
174              
175             Takes a day number (days since the TAI epoch), as a C
176             object, and returns the corresponding Chronological Julian Day Number
177             (a number of days since -4713-11-24), as a C object.
178             CJDN is a standard day numbering that is useful as an interchange format
179             between implementations of different calendars. There is no bound on
180             the permissible day numbers; the function is not limited to days for
181             which UTC-SLS is defined.
182              
183             =item utc_cjdn_to_day(CJDN)
184              
185             This performs the reverse of the translation that C does.
186             It takes a Chronological Julian Day Number, as a C object,
187             and returns the number of days since the TAI epoch, as a C
188             object. It does not impose any limit on the range.
189              
190             =back
191              
192             =head1 SEE ALSO
193              
194             L,
195             L,
196             L
197              
198             =head1 AUTHOR
199              
200             Andrew Main (Zefram)
201              
202             =head1 COPYRIGHT
203              
204             Copyright (C) 2006, 2007, 2009, 2010, 2012
205             Andrew Main (Zefram)
206              
207             =head1 LICENSE
208              
209             This module is free software; you can redistribute it and/or modify it
210             under the same terms as Perl itself.
211              
212             =cut
213              
214             1;