File Coverage

blib/lib/Time/UTC_SLS.pm
Criterion Covered Total %
statement 48 48 100.0
branch 20 22 90.9
condition 8 12 66.6
subroutine 11 11 100.0
pod 2 2 100.0
total 89 95 93.6


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