File Coverage

blib/lib/Time/UTC/Segment.pm
Criterion Covered Total %
statement 132 191 69.1
branch 20 70 28.5
condition 3 6 50.0
subroutine 36 43 83.7
pod 5 5 100.0
total 196 315 62.2


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Time::UTC::Segment - segments of UTC definition
4              
5             =head1 SYNOPSIS
6              
7             use Time::UTC::Segment;
8              
9             $seg = Time::UTC::Segment->start;
10              
11             $tai = $seg->start_tai_instant;
12             $tai = $seg->end_tai_instant;
13             $len = $seg->length_in_tai_seconds;
14              
15             $day = $seg->start_utc_day;
16             $day = $seg->last_utc_day;
17             $day = $seg->end_utc_day;
18              
19             $len = $seg->utc_second_length;
20             $secs = $seg->leap_utc_seconds;
21              
22             $secs = $seg->last_day_utc_seconds;
23             $secs = $seg->length_in_utc_seconds;
24              
25             $seg = $seg->prev;
26             $seg = $seg->next;
27              
28             if($seg->complete_p) { ...
29             $seg->when_complete(\&do_stuff);
30              
31             =head1 DESCRIPTION
32              
33             An object of this class represents a segment of the definition of UTC in
34             terms of TAI. Each segment is a period of time over which the relation
35             between UTC and TAI is stable. Each point where the relation changes
36             is a boundary between segments.
37              
38             Each segment consists of an integral number of consecutive UTC days.
39             Within each segment, the length of the UTC second is fixed relative
40             to the TAI second. Also, every UTC day in the segment except for the
41             last one contains exactly 86400 UTC seconds. The last day of a segment
42             commonly has some other length.
43              
44             Because UTC is only defined a few months ahead, the description of UTC
45             that is available at any particular time is necessarily incomplete.
46             Nevertheless, this API gives the superficial appearance of completeness.
47             The information-querying methods will C if asked for information
48             that is not yet available. There are additional methods to probe the
49             availability of information.
50              
51             =cut
52              
53             package Time::UTC::Segment;
54              
55 11     11   21771 { use 5.006; }
  11         40  
  11         475  
56 11     11   63 use warnings;
  11         27  
  11         363  
57 11     11   65 use strict;
  11         26  
  11         412  
58              
59 11     11   56 use Carp qw(croak);
  11         28  
  11         674  
60 11     11   21026 use Digest::SHA1 qw(sha1_hex);
  11         9275  
  11         699  
61 11     11   18716 use HTTP::Tiny 0.016 ();
  11         700408  
  11         484  
62 11     11   1671 use Math::BigRat 0.13;
  11         107281  
  11         136  
63 11     11   28110 use Net::FTP 1.21 ();
  11         273787  
  11         513  
64 11     11   15796 use Time::Unix 1.02 ();
  11         5563  
  11         6886  
65              
66             our $VERSION = "0.008";
67              
68             @Time::UTC::Segment::Complete::ISA = qw(Time::UTC::Segment);
69             @Time::UTC::Segment::Incomplete::ISA = qw(Time::UTC::Segment);
70              
71             =head1 CONSTRUCTOR
72              
73             Objects of this class are not created by users, but are generated
74             internally. New segments appear when updated UTC data is downloaded;
75             this is done automatically as required. Segments are accessed from each
76             other by means of the C and C methods.
77              
78             =over
79              
80             =item Time::UTC::Segment->start
81              
82             Returns the first segment of the UTC description.
83              
84             =back
85              
86             =cut
87              
88             my $incomplete_segment;
89              
90             {
91             my $start_segment = $incomplete_segment = bless({
92             start_utc_day => Math::BigRat->new(1096),
93             start_tai_instant =>
94             1096*86400 + Math::BigRat->new("1.4228180"),
95             utc_second_length => 1 + Math::BigRat->new("0.001296") / 86400,
96             }, "Time::UTC::Segment::Incomplete");
97             sub start {
98 38     38 1 155 return $start_segment;
99             }
100             }
101              
102             sub _add_data($$$$$$) {
103 195     195   4781 my($start_utc_day, $start_tai_instant, $utc_second_length,
104             $end_utc_day, $end_tai_instant, $next_utc_second_length) = @_;
105 195 50       826 die "backward UTC segment\n" if $end_utc_day <= $start_utc_day;
106 195         19757 my $seg = $incomplete_segment;
107 195 50       718 return if $end_utc_day <= $seg->start_utc_day;
108 195 50       17759 die "unexpected gap in UTC knowledge\n"
109             if $start_utc_day > $seg->start_utc_day;
110 195         18818 my $overlap_days = $seg->start_utc_day - $start_utc_day;
111 195         37948 $start_utc_day = $seg->start_utc_day;
112 195         602 $start_tai_instant += $overlap_days * 86400 * $utc_second_length;
113 195 50 33     88455 die "inconsistent UTC knowledge\n"
114             unless $start_tai_instant == $seg->start_tai_instant &&
115             $utc_second_length == $seg->utc_second_length;
116 195         19114 my $length_in_tai_seconds = $end_tai_instant - $start_tai_instant;
117 195         72608 my $length_in_utc_seconds = $length_in_tai_seconds /
118             $utc_second_length;
119 195         51622 my $leap_utc_seconds = $length_in_utc_seconds -
120             ($end_utc_day - $start_utc_day) * 86400;
121 195 50       128913 die "UTC leap too large\n"
122             if abs($leap_utc_seconds) >= 60;
123 195         44754 $seg->{length_in_tai_seconds} = $length_in_tai_seconds;
124 195         525 $seg->{length_in_utc_seconds} = $length_in_utc_seconds;
125 195         660 $seg->{leap_utc_seconds} = $leap_utc_seconds;
126 195         666 $seg->{last_utc_day} = $end_utc_day - 1;
127 195         64491 $seg->{last_day_utc_seconds} = 86400 + $seg->{leap_utc_seconds};
128 195         63194 $seg->{next} = $incomplete_segment = bless({
129             start_utc_day => $end_utc_day,
130             start_tai_instant => $end_tai_instant,
131             utc_second_length => $next_utc_second_length,
132             prev => $seg,
133             }, "Time::UTC::Segment::Incomplete");
134 195         798 bless $seg, "Time::UTC::Segment::Complete";
135 195         359 foreach my $what (@{$seg->{when_complete}}) {
  195         639  
136 352         602 eval { local $SIG{__DIE__}; $what->(); };
  352         1360  
  352         993  
137             }
138 195         2127 delete $seg->{when_complete};
139             }
140              
141 11     11   144 use constant _JD_TO_MJD => Math::BigRat->new("2400000.5");
  11         28  
  11         121  
142              
143 11     11   13226 use constant _TAI_EPOCH_MJD => Math::BigRat->new(36204);
  11         57  
  11         70  
144              
145             sub _add_data_from_tai_utc_dat($$) {
146 5     5   4342 my($dat, $end_mjd) = @_;
147 5         11 my $seg;
148 5         50 while($dat =~ /\G([^\n]*\n)/g) {
149 195         706 my $line = $1;
150 195 50       5228 $line =~ /\A[\ \t]*[0-9]+[\ \t]*[A-Z]+[\ \t]*[0-9]+[\ \t]*
151             =[\ \t]*
152             JD[\ \t]*([0-9]+\.?[0-9]*)[\ \t]*
153             TAI[\ \t]*-[\ \t]*UTC[\ \t]*=[\ \t]*
154             (-?[0-9]+\.?[0-9]*)[\ \t]*S[\ \t]*
155             ([\+\-])[\ \t]*
156             \([\ \t]*MJD[\ \t]*([\+\-])[\ \t]*
157             (-?[0-9]+\.?[0-9]*)[\ \t]*\)[\ \t]*
158             X[\ \t]*(-?[0-9]+\.?[0-9]*)[\ \t]*S[\ \t]*
159             \n\z/xi
160             or die "bad TAI-UTC data\n";
161 195         1581 my($start_jd, $base_difference, $tweak_sign, $base_mjd_sign,
162             $base_mjd, $day_tweak) = ($1, $2, $3, $4, $5, $6);
163 195         748 my $start_mjd = Math::BigRat->new($start_jd) - _JD_TO_MJD;
164 195 50       133860 die "bad UTC segment start date" unless $start_mjd->is_int;
165 195         3361 $base_difference = Math::BigRat->new($base_difference);
166 195         67706 $base_mjd = Math::BigRat->new($base_mjd);
167 195         46329 $day_tweak = Math::BigRat->new($day_tweak);
168 195 50       68011 $base_mjd = -$base_mjd if $base_mjd_sign eq "+";
169 195 50       566 $day_tweak = -$day_tweak if $tweak_sign eq "-";
170 195         720 my $nseg = {
171             start_utc_day => $start_mjd - _TAI_EPOCH_MJD,
172             utc_second_length => 1 + $day_tweak / 86400,
173             };
174 195         154827 $nseg->{start_tai_instant} =
175             $nseg->{start_utc_day} * 86400
176             + $base_difference
177             + ($start_mjd - $base_mjd) * $day_tweak;
178 195 100       218132 if(defined $seg) {
179 190         912 _add_data($seg->{start_utc_day},
180             $seg->{start_tai_instant},
181             $seg->{utc_second_length},
182             $nseg->{start_utc_day},
183             $nseg->{start_tai_instant},
184             $nseg->{utc_second_length});
185             }
186 195         2870 $seg = $nseg;
187             }
188 5 50       28 die "no TAI-UTC data\n" unless defined $seg;
189             # Final segment: we have a minimal start date for the start of
190             # the next real UTC segment ($end_mjd), but don't know what UTC
191             # will be from that date onwards. Consequently we don't know
192             # the length of the preceding UTC day, and must knock off a day
193             # from the segment that we build here.
194 5         21 my $end_utc_day = $end_mjd - 1 - _TAI_EPOCH_MJD;
195 5 50       3129 if($end_utc_day > $seg->{start_utc_day}) {
196 5         536 _add_data($seg->{start_utc_day},
197             $seg->{start_tai_instant},
198             $seg->{utc_second_length},
199             $end_utc_day,
200             $seg->{start_tai_instant} +
201             (($end_utc_day - $seg->{start_utc_day}) *
202             86400) *
203             $seg->{utc_second_length},
204             $seg->{utc_second_length});
205             }
206             }
207              
208             {
209             my $init_dat = do { local $/ = undef; };
210             close(DATA);
211             sub _use_builtin_knowledge() {
212 5 50   5   2963 $init_dat =~ s/^[\ \t]*[0-9]+[\ \t]*[A-Z]+[\ \t]*[0-9]+[\ \t]*
213             =[\ \t]*
214             JD[\ \t]*([0-9]+\.?[0-9]*)[\ \t]*
215             unknown[\ \t]*\n\z//xim
216             or die "broken built-in TAI-UTC data\n";
217 5         27 my $end_jd = $1;
218 5         306 _add_data_from_tai_utc_dat($init_dat, $end_jd - _JD_TO_MJD);
219 5         66 $init_dat = undef;
220             }
221             }
222              
223 11     11   11732 use constant _UNIX_EPOCH_MJD => Math::BigRat->new(40586);
  11         25  
  11         71  
224              
225             sub _download_tai_utc_dat() {
226             # Annoyingly, TAI-UTC data is not published with any
227             # indicator of the extent of its future validity.
228             # The IERS never says "there will be no leap second
229             # until at least 2005-06-30"; the latest TAI-UTC offset
230             # is always valid "until further notice". However,
231             # leap seconds are supposed to be announced at least
232             # eight weeks in advance, so here we assume validity of
233             # the downloaded data seven weeks into the future.
234             # For this reason we only do a direct get from USNO;
235             # we do not use proxies which might serve old data.
236 0     0   0 my $unix_time = Time::Unix::time();
237 0         0 my $httpresp = HTTP::Tiny->new->get(
238             "http://maia.usno.navy.mil/ser7/tai-utc.dat");
239 0 0       0 unless($httpresp->{status} == 200) {
240 0         0 die "failed to download tai-utc.dat: ".
241 0         0 "@{[$httpresp->{status}]} @{[$httpresp->{reason}]}\n";
  0         0  
242             }
243 11     11   2968 use integer;
  11         22  
  11         103  
244 0         0 my $now_mjd = $unix_time/86400 + _UNIX_EPOCH_MJD;
245 0         0 _add_data_from_tai_utc_dat($httpresp->{content}, $now_mjd + 7*7);
246             }
247              
248 11     11   1022 use constant _NTP_EPOCH_MJD => Math::BigRat->new(15020);
  11         25  
  11         47  
249              
250             sub _ntp_second_to_tai_day($) {
251 0     0   0 my($ntp_sec_str) = @_;
252 0         0 return Math::BigRat->new($ntp_sec_str) / 86400
253             + _NTP_EPOCH_MJD - _TAI_EPOCH_MJD;
254             }
255              
256 11     11   2878 use constant _BIGRAT_ONE => Math::BigRat->new(1);
  11         21  
  11         48  
257              
258             sub _download_leap_seconds_list() {
259 0 0   0   0 my $ftp = Net::FTP->new("utcnist2.colorado.edu")
260             or die "failed to download leap-seconds.list: FTP error: $@\n";
261 0 0       0 $ftp->login("anonymous","-anonymous\@")
262             or die "failed to download leap-seconds.list: FTP error: ".
263             $ftp->message;
264 0 0       0 $ftp->binary
265             or die "failed to download leap-seconds.list: FTP error: ".
266             $ftp->message;
267 0 0       0 $ftp->cwd("pub")
268             or die "failed to download leap-seconds.list: FTP error: ".
269             $ftp->message;
270 0 0       0 my $ftpd = $ftp->retr("leap-seconds.list")
271             or die "failed to download leap-seconds.list: FTP error: ".
272             $ftp->message;
273 0         0 my $list = "";
274 0         0 while(1) {
275 0         0 my $n = $ftpd->sysread($list, 4096, length($list));
276 0 0       0 defined $n or die "failed to download leap-seconds.list: $!\n";
277 0 0       0 last if $n == 0;
278             }
279             $ftpd->close
280 0 0       0 or die "failed to download leap-seconds.list: FTP error: ".
281             $ftp->message;
282 0 0       0 die "malformed leap-seconds.list" unless $list =~ /\n\z/;
283 0 0       0 $list =~ /^\#h([ \t0-9a-fA-F]+)$/m
284             or die "no hash in leap-seconds.list";
285 0         0 (my $hash = $1) =~ tr/A-F \t/a-f/d;
286 0         0 my $data_to_hash = "";
287 0         0 while($list =~ /^(?:\#[\$\@])?[ \t]*([0-9][^\#\n]*)[#\n]/mg) {
288 0         0 $data_to_hash .= $1;
289             }
290 0         0 $data_to_hash =~ tr/0-9//cd;
291 0 0       0 die "hash mismatch in leap-seconds.list"
292             unless sha1_hex($data_to_hash) eq $hash;
293 0         0 my($start_utc_day, $start_tai_instant);
294 0         0 while($list =~ /^([^#\n][^\n]*)$/mg) {
295 0         0 my $line = $1;
296 0 0       0 $line =~ /\A[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]*(?:\#|\z)/
297             or die "malformed data line in leap-seconds.list";
298 0         0 my($next_start_ntp_sec, $ndiff) = ($1, $2);
299 0         0 my $next_start_utc_day =
300             _ntp_second_to_tai_day($next_start_ntp_sec);
301 0 0       0 die "bad transition date in leap-seconds.list"
302             unless $next_start_utc_day->is_int;
303 0         0 my $next_start_tai_instant =
304             $next_start_utc_day*86400 + Math::BigRat->new($ndiff);
305 0 0       0 if(defined $start_utc_day) {
306 0         0 _add_data($start_utc_day,
307             $start_tai_instant,
308             _BIGRAT_ONE,
309             $next_start_utc_day,
310             $next_start_tai_instant,
311             _BIGRAT_ONE);
312             }
313 0         0 $start_utc_day = $next_start_utc_day;
314 0         0 $start_tai_instant = $next_start_tai_instant;
315             }
316 0 0       0 $list =~ /^\#\@[ \t]*([0-9]+)[ \t]*$/m
317             or die "no expiry date in leap-seconds.list";
318 0         0 my $expsec = $1;
319 0         0 my $end_utc_day = _ntp_second_to_tai_day($expsec)->bfloor - 1;
320 0 0       0 if(defined $start_utc_day) {
321 0         0 _add_data($start_utc_day,
322             $start_tai_instant,
323             _BIGRAT_ONE,
324             $end_utc_day,
325             $start_tai_instant +
326             ($end_utc_day - $start_utc_day) * 86400,
327             _BIGRAT_ONE);
328             }
329             }
330              
331             sub _download_latest_data() {
332 0           eval { local $SIG{__DIE__}; _download_leap_seconds_list(); 1 }
  0            
  0            
333 0 0   0     or eval { local $SIG{__DIE__}; _download_tai_utc_dat(); 1 };
  0            
  0            
  0            
334             }
335              
336             {
337             my $last_download = 0;
338             my $wait_until = 0;
339             sub _maybe_download_latest_data() {
340 18     18   60 my $time = time;
341 18 100 66     155 return unless $time >= $wait_until || $time < $last_download;
342 3         7 $last_download = $time;
343 3         1153 $wait_until = $last_download + 3600 + rand(3600);
344 3 50       61 _download_latest_data() and
345             $wait_until = $last_download + 20*86400 + rand(2*86400);
346             }
347             }
348              
349             my $try_to_extend_knowledge = \&_use_builtin_knowledge;
350              
351             =head1 METHODS
352              
353             =head2 Information querying
354              
355             Most methods merely query the segment data. The data are strictly
356             read-only.
357              
358             The methods will C if information is requested that is not available.
359             This happens when looking further ahead than UTC has been defined.
360             UTC is defined only a few months in advance.
361              
362             All numeric values are returned as C objects.
363              
364             =over
365              
366             =cut
367              
368             sub _data_unavailable {
369 22     22   47 my($self, $method) = @_;
370 22 50       69 if(defined $try_to_extend_knowledge) {
371 22         34 eval { local $SIG{__DIE__};
  22         98  
372 22         37 my $ttek = $try_to_extend_knowledge;
373 22         31 $try_to_extend_knowledge = undef;
374 22         51 $ttek->();
375             };
376 22         52 $try_to_extend_knowledge = \&_maybe_download_latest_data;
377 22 100       86 if(ref($self) eq "Time::UTC::Segment::Complete") {
378 4         28 return $self->$method;
379             }
380             }
381 18         2478 croak "data not available yet";
382             }
383              
384             sub _data_unavailable_method($) {
385 88     88   147 my($method) = @_;
386 88     22   406 return sub { $_[0]->_data_unavailable($method) };
  22         4514  
387             }
388              
389             =item $seg->start_tai_instant
390              
391             The instant at which this segment starts, in TAI form: a C
392             giving the number of TAI seconds since the TAI epoch.
393              
394             =cut
395              
396             sub start_tai_instant {
397 754     754 1 6080 $_[0]->{start_tai_instant}
398             }
399              
400             =item $seg->end_tai_instant
401              
402             The instant at which this segment ends, in TAI form: a C
403             giving the number of TAI seconds since the TAI epoch.
404              
405             =cut
406              
407             sub Time::UTC::Segment::Complete::end_tai_instant {
408 117     117   632 $_[0]->{next}->{start_tai_instant}
409             }
410              
411             *Time::UTC::Segment::Incomplete::end_tai_instant =
412             _data_unavailable_method("end_tai_instant");
413              
414             =item $seg->length_in_tai_seconds
415              
416             The duration of this segment, measured in TAI seconds, as a
417             C.
418              
419             =cut
420              
421             sub Time::UTC::Segment::Complete::length_in_tai_seconds {
422 78     78   32236 $_[0]->{length_in_tai_seconds}
423             }
424              
425             *Time::UTC::Segment::Incomplete::length_in_tai_seconds =
426             _data_unavailable_method("length_in_tai_seconds");
427              
428             =item $seg->start_utc_day
429              
430             The first UTC day of this segment: a C giving the number
431             of days since the TAI epoch.
432              
433             =cut
434              
435             sub start_utc_day {
436 2307     2307 1 211166 $_[0]->{start_utc_day}
437             }
438              
439             =item $seg->last_utc_day
440              
441             The last UTC day of this segment: a C giving the number
442             of days since the TAI epoch.
443              
444             =cut
445              
446             sub Time::UTC::Segment::Complete::last_utc_day {
447 585     585   182550 $_[0]->{last_utc_day}
448             }
449              
450             *Time::UTC::Segment::Incomplete::last_utc_day =
451             _data_unavailable_method("last_utc_day");
452              
453             =item $seg->end_utc_day
454              
455             The first UTC day after the end of this segment: a C
456             giving the number of days since the TAI epoch.
457              
458             =cut
459              
460             sub Time::UTC::Segment::Complete::end_utc_day {
461 234     234   15362 $_[0]->{next}->{start_utc_day}
462             }
463              
464             *Time::UTC::Segment::Incomplete::end_utc_day =
465             _data_unavailable_method("end_utc_day");
466              
467             =item $seg->utc_second_length
468              
469             The length of the UTC second in this segment, measured in TAI seconds,
470             as a C.
471              
472             =cut
473              
474             sub utc_second_length {
475 235     235 1 22705 $_[0]->{utc_second_length}
476             }
477              
478             =item $seg->leap_utc_seconds
479              
480             The number of extra UTC seconds inserted at the end of the last day of
481             this segment, as a C. May be negative.
482              
483             =cut
484              
485             sub Time::UTC::Segment::Complete::leap_utc_seconds {
486 234     234   943 $_[0]->{leap_utc_seconds}
487             }
488              
489             *Time::UTC::Segment::Incomplete::leap_utc_seconds =
490             _data_unavailable_method("leap_utc_seconds");
491              
492             =item $seg->last_day_utc_seconds
493              
494             The number of UTC seconds in the last day of this segment, as a
495             C.
496              
497             =cut
498              
499             sub Time::UTC::Segment::Complete::last_day_utc_seconds {
500 117     117   50008 $_[0]->{last_day_utc_seconds}
501             }
502              
503             *Time::UTC::Segment::Incomplete::last_day_utc_seconds =
504             _data_unavailable_method("last_day_utc_seconds");
505              
506             =item $seg->length_in_utc_seconds
507              
508             The duration of this segment, measured in UTC seconds, as a
509             C.
510              
511             =cut
512              
513             sub Time::UTC::Segment::Complete::length_in_utc_seconds {
514 78     78   44340 $_[0]->{length_in_utc_seconds}
515             }
516              
517             *Time::UTC::Segment::Incomplete::length_in_utc_seconds =
518             _data_unavailable_method("length_in_utc_seconds");
519              
520             =item $seg->prev
521              
522             The immediately preceding segment. C if there is no preceding
523             segment, because this segment covers the start of UTC service at the
524             beginning of 1961.
525              
526             =cut
527              
528             sub prev {
529 39     39 1 151 $_[0]->{prev}
530             }
531              
532             =item $seg->next
533              
534             The immediately following segment. In the event that UTC ever becomes
535             fully defined, either by being defined for the entire future or by being
536             withdrawn altogether, there will be a final segment for which this will
537             be C.
538              
539             =cut
540              
541             sub Time::UTC::Segment::Complete::next {
542 784     784   142279 $_[0]->{next}
543             }
544              
545             *Time::UTC::Segment::Incomplete::next = _data_unavailable_method("next");
546              
547             =back
548              
549             =head2 Completeness
550              
551             Segments can be classified as "complete" and "incomplete". For complete
552             segments, all information-querying methods give answers. For incomplete
553             segments, only a few data are available: the methods C,
554             C, C, and C will give correct
555             answers, but other methods will C.
556              
557             An incomplete segment can become complete, as new information becomes
558             available, when updated UTC data is (automatically) downloaded. When this
559             happens, the resulting complete segment cannot be distinguished from
560             any other complete segment.
561              
562             Only one incomplete segment exists at a time.
563              
564             =over
565              
566             =item $seg->complete_p
567              
568             Returns a truth value indicating whether the segment is currently complete.
569              
570             =cut
571              
572 0     0   0 sub Time::UTC::Segment::Complete::complete_p { 1 }
573              
574 0     0   0 sub Time::UTC::Segment::Incomplete::complete_p { 0 }
575              
576             =item $seg->when_complete(WHAT)
577              
578             I must be a reference to a function which takes no arguments.
579             When the segment is complete, the function will be called. If the
580             segment is already complete then the function is called immediately;
581             otherwise the function is noted and will be called when the segment
582             becomes complete due to the availability of new information.
583              
584             This is a one-shot operation. To do something similar for all segments,
585             see C in C.
586              
587             =cut
588              
589             sub Time::UTC::Segment::Complete::when_complete {
590 0     0   0 my($self, $what) = @_;
591 0         0 eval { local $SIG{__DIE__}; $what->(); };
  0         0  
  0         0  
592             }
593              
594             sub Time::UTC::Segment::Incomplete::when_complete {
595 373     373   602 my($self, $what) = @_;
596 373         474 push @{$self->{when_complete}}, $what;
  373         2379  
597             }
598              
599             =back
600              
601             =head1 INVARIANTS
602              
603             The following relations hold for all segments:
604              
605             $seg->length_in_tai_seconds ==
606             $seg->end_tai_instant - $seg->start_tai_instant
607              
608             $seg->last_utc_day + 1 == $seg->end_utc_day
609              
610             $seg->last_day_utc_seconds == 86400 + $seg->leap_utc_seconds
611              
612             $seg->length_in_utc_seconds ==
613             86400 * ($seg->last_utc_day - $seg->start_utc_day) +
614             $seg->last_day_utc_seconds
615              
616             $seg->length_in_tai_seconds ==
617             $seg->length_in_utc_seconds * $seg->utc_second_length
618              
619             $seg->next->prev == $seg
620              
621             $seg->end_tai_instant == $seg->next->start_tai_instant
622              
623             $seg->end_utc_day == $seg->next->start_utc_day
624              
625             =head1 SEE ALSO
626              
627             L
628              
629             =head1 AUTHOR
630              
631             Andrew Main (Zefram)
632              
633             =head1 COPYRIGHT
634              
635             Copyright (C) 2005, 2006, 2007, 2009, 2010, 2012
636             Andrew Main (Zefram)
637              
638             =head1 LICENSE
639              
640             This module is free software; you can redistribute it and/or modify it
641             under the same terms as Perl itself.
642              
643             =cut
644              
645             1;
646              
647             __DATA__