File Coverage

lib/Time/TAI/Simple.pm
Criterion Covered Total %
statement 98 151 64.9
branch 26 66 39.3
condition 5 11 45.4
subroutine 18 19 94.7
pod 8 9 88.8
total 155 256 60.5


line stmt bran cond sub pod time code
1             package Time::TAI::Simple;
2              
3             # ABSTRACT: Easily obtain current TAI time, using UNIX epoch.
4              
5 2     2   177686 use strict;
  2         9  
  2         41  
6 2     2   8 use warnings;
  2         2  
  2         42  
7             require v5.10.0;
8              
9 2     2   655 use POSIX::RT::Clock;
  2         1507  
  2         41  
10 2     2   9 use Time::HiRes;
  2         2  
  2         7  
11 2     2   1069 use HTTP::Tiny;
  2         75776  
  2         70  
12              
13 2     2   13 use base qw(Exporter);
  2         2  
  2         154  
14              
15             BEGIN {
16 2     2   7 @Time::TAI::Simple::EXPORT = qw(tai tai10 tai35);
17 2         2861 $Time::TAI::Simple::VERSION = '1.16';
18             }
19              
20             our @LEAPSECOND_UNIX_PATHNAME_LIST = (
21             '/etc/leap-seconds.list',
22             ($ENV{'HOME'} // '/root') . "/.leap-seconds.list",
23             "/var/tmp/leap-seconds.list",
24             "/tmp/leap-seconds.list"
25             );
26              
27             our @LEAPSECOND_WINDOWS_PATHNAME_LIST = (
28             ($ENV{'WINDIR'} // 'C:\WINDOWS') . '\leap-seconds.list',
29             ($ENV{'HOMEDRIVE'} // 'C:') . ($ENV{'HOMEPATH'} // '\Users') . '\.leap-seconds.list',
30             ($ENV{'TEMP'} // 'C:\TEMPDIR') . '\leap-seconds.list'
31             );
32              
33             our $LEAPSECOND_IETF_DELTA = 2208960000; # difference between IETF's leapseconds (since 1900-01-01 00:00:00) and equivalent UNIX epoch time.
34              
35             our @FALLBACK_LEAPSECONDS_LIST = ( # from https://www.ietf.org/timezones/data/leap-seconds.list
36             [2272060800, 10],
37             [2287785600, 11],
38             [2303683200, 12],
39             [2335219200, 13],
40             [2366755200, 14],
41             [2398291200, 15],
42             [2429913600, 16],
43             [2461449600, 17],
44             [2492985600, 18],
45             [2524521600, 19],
46             [2571782400, 20],
47             [2603318400, 21],
48             [2634854400, 22],
49             [2698012800, 23],
50             [2776982400, 24],
51             [2840140800, 25],
52             [2871676800, 26],
53             [2918937600, 27],
54             [2950473600, 28],
55             [2982009600, 29],
56             [3029443200, 30],
57             [3076704000, 31],
58             [3124137600, 32],
59             [3345062400, 33],
60             [3439756800, 34],
61             [3550089600, 35],
62             [3644697600, 36],
63             [3692217600, 37]
64             );
65              
66             our $TAI_OR = undef;
67             our $TAI10_OR = undef;
68             our $TAI35_OR = undef;
69              
70             sub new {
71 2004     2004 1 5987 my ($class, %opt_hr) = @_;
72 2004         1639 my $success = 0;
73 2004         1390 my $timer = undef;
74 2004         1492 eval { $timer = POSIX::RT::Clock->new('monotonic'); $success = 1; };
  2004         2428  
  2004         1673  
75 2004 50       2128 return undef unless ($success);
76              
77 2004         6700 my $self = {
78             opt_hr => \%opt_hr,
79             tm_or => undef, # POSIX::RT::Clock instance, for monotonic clock access.
80             ls_ar => [], # list of leap seconds, as tuples of [UTC epoch, $nsec].
81             ls_tm => 0, # epoch mtime of leap seconds list, so we know when it needs updating.
82             dl_tm => 0, # epoch time we last tried to download a new leapsecond list.
83             tm_base => 0.0, # add to monotonic clock time to get TAI epoch time.
84             mode => 'tai10', # one of: "tai", "tai10", or "tai35".
85             dl_fr => undef,
86             dl_to => undef,
87             ua_ar => [ # so IERS doesn't deny or redirect connection
88             'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62',
89             'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0',
90             'Mozilla/5.0 (Linux; Android 12; SAMSUNG SM-T870) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/17.0 Chrome/96.0.4664.104 Safari/537.36',
91             'Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36',
92             'Mozilla/5.0 (Android 12; Mobile; rv:102.0) Gecko/102.0 Firefox/102.0',
93             'Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0',
94             'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Goanna/5.1 Firefox/68.0 PaleMoon/31.1.0',
95             ],
96             };
97 2004         1943 bless ($self, $class);
98              
99             # Spoof the User-Agent string to get around IERS gatekeeping
100 2004 50       2071 $self->{ua_ar} = [$self->opt('agent')] if ($self->opt('agent'));
101 2004 50       1864 push @{$self->{ua_ar}}, split(/[\s,]+/, $self->opt('add_agent')) if ($self->opt('add_agent'));
  0         0  
102 2004         2049 $self->{ua_str} = $self->_pick_user_agent();
103 2004         2924 $self->{http_or} = HTTP::Tiny->new(max_redirect => 15, agent => $self->{ua_str});
104              
105 2004         78639 $self->{mode} = $self->opt('mode', 'tai10');
106 2004 50       1846 $self->download_leapseconds() if ($self->opt('download_leapseconds', 0));
107 2004 100       1903 $self->load_leapseconds() unless ($self->opt('do_not_load_leapseconds'));
108 2004         2422 $self->calculate_base(); # also sets tm_or and potentially ls_next
109 2004         5866 return $self;
110             }
111              
112             sub _pick_user_agent {
113 2005     2005   2467 my ($self) = @_;
114 2005 50       1850 my $k = $self->opt('force_edge') ? 0 : scalar(@{$self->{ua_ar}});
  2005         1980  
115 2005         3185 my $ua = $self->{ua_ar}->[int(rand() * $k)];
116 2005         2208 return $ua;
117             }
118              
119             sub time {
120 3     3 1 10 return $_[0]->{tm_or}->get_time() + $_[0]->{tm_base};
121             }
122              
123             sub tai {
124 1 50   1 1 9 $TAI_OR = Time::TAI::Simple->new(mode => 'tai') unless (defined($TAI_OR));
125 1         2 return $TAI_OR->time();
126             }
127              
128             sub tai10 {
129 1 50   1 1 18 $TAI10_OR = Time::TAI::Simple->new(mode => 'tai10') unless (defined($TAI10_OR));
130 1         4 return $TAI10_OR->time();
131             }
132              
133             sub tai35 {
134 1 50   1 1 443 $TAI35_OR = Time::TAI::Simple->new(mode => 'tai35') unless (defined($TAI35_OR));
135 1         4 return $TAI35_OR->time();
136             }
137              
138             sub calculate_base {
139 2004     2004 1 1617 my ($self, %opt_h) = @_;
140 2004 50       3746 $self->{tm_or} = POSIX::RT::Clock->new('monotonic') unless (defined($self->{tm_or}));
141 2004 50       2186 if (defined($self->opt('base_time', undef, \%opt_h))) {
142 0         0 $self->{tm_base} = $self->opt('base_time', undef, \%opt_h);
143 0         0 return;
144             }
145 2004         2348 my $tm = Time::HiRes::time();
146 2004         2364 my $mo = $self->{tm_or}->get_time();
147 2004         1413 my $delta = 0;
148 2004         2487 for (my $ix = 0; defined($self->{ls_ar}->[$ix]); $ix++) {
149 112         71 my ($ls_tm, $ls_delta) = @{$self->{ls_ar}->[$ix]};
  112         98  
150 112 50       114 if ($ls_tm > $tm - $delta) {
151 0         0 $self->{ls_next} = $ix;
152 0         0 last;
153             }
154 112         120 $delta = $ls_delta;
155             }
156 2004 100       2471 $delta -= 10 if ($self->{mode} eq 'tai10');
157 2004 100       2019 $delta -= 35 if ($self->{mode} eq 'tai35');
158 2004 100       1839 $delta -= $self->_fine_tune() if ($self->opt('fine_tune', 1));
159 2004         2100 $self->{tm_base} = $tm - $mo - $delta;
160 2004         1658 return;
161             }
162              
163             sub load_leapseconds {
164 4     4 1 4 my ($self, %opt_h) = @_;
165 4         9 my $filename = $self->_leapseconds_filename(\%opt_h);
166 4         6 my $fh = undef;
167 4         6 $self->{ls_ar} = [];
168 4 50       62 if (open($fh, '<', $filename)) {
169 0         0 while(defined(my $x = <$fh>)) {
170 0 0       0 next unless ($x =~ /^(\d{10})\s+(\d{2})/);
171 0         0 my ($iers_tm, $nsec) = ($1, $2);
172 0         0 my $epoch_tm = $iers_tm - $LEAPSECOND_IETF_DELTA;
173 0         0 push(@{$self->{ls_ar}}, [$epoch_tm, $nsec]);
  0         0  
174             # can't set ls_next here, because base tai time hasn't been computed yet.
175             }
176 0         0 close($fh);
177 0         0 $self->{ls_tm} = (stat($filename))[9];
178             } else {
179 4         9 foreach my $tup (@FALLBACK_LEAPSECONDS_LIST) {
180 112         85 my ($iers_tm, $nsec) = @{$tup};
  112         103  
181 112         84 my $epoch_tm = $iers_tm - $LEAPSECOND_IETF_DELTA;
182 112         70 push(@{$self->{ls_ar}}, [$epoch_tm, $nsec]);
  112         149  
183             }
184 4         6 $self->{ls_tm} = CORE::time();
185             }
186 4         13 return 1;
187             }
188              
189             sub download_leapseconds {
190 0     0 1 0 my ($self, %opt_h) = @_;
191 0         0 my $response = 0;
192 0         0 my @url_list = ();
193 0         0 $self->{dl_tm} = CORE::time();
194 0 0       0 if (defined(my $urls = $self->opt('download_urls', undef, \%opt_h))) {
195 0 0       0 if (ref($urls) eq 'ARRAY') {
    0          
196 0         0 push(@url_list, @{$urls});
  0         0  
197             }
198             elsif ($urls =~ /^(http:|ftp:|file:)/i) {
199 0         0 push(@url_list, $urls);
200             }
201             }
202              
203             # By default, fetch the cached file to avoid unduly annoying the IERS folks.
204             # Specify --primary to try the IERS file before the cached file.
205 0         0 my $primary_url = 'https://www.ietf.org/timezones/data/leap-seconds.list';
206 0         0 my $cached_url = 'http://www.ciar.org/ttk/codecloset/leap-seconds.list';
207 0 0       0 if ($self->opt('primary')) {
208 0         0 push @url_list, ($primary_url, $cached_url);
209             } else {
210 0         0 push @url_list, ($cached_url, $primary_url);
211             }
212              
213 0         0 eval {
214 0         0 my $http_or = $self->{http_or};
215 0         0 my $leapseconds_filename = $self->_leapseconds_filename(\%opt_h);
216 0         0 my $success = 0;
217 0         0 foreach my $url (@url_list) {
218 0         0 my $n_tries = $self->opt('retry', 0) + 1;
219 0         0 for my $trying (1..$n_tries) {
220 0 0       0 print "download_leapseconds: try $trying url $url\n" if ($self->opt('debug'));
221 0 0       0 print "download_leapseconds: user agent [$self->{ua_str}]\n" if ($self->opt('debug'));
222 0         0 my $reply = $http_or->mirror($url, $leapseconds_filename, {});
223 0 0 0     0 unless (defined($reply) && $reply->{success}) {
224 0 0       0 if ($self->opt('churn_agent')) {
225 0         0 $self->{ua_str} = $self->_pick_user_agent();
226 0         0 $self->{http_or} = HTTP::Tiny->new(max_redirect => 15, agent => $self->{ua_str});
227             }
228 0 0       0 Time::HiRes::sleep($self->opt('retry_delay', 0.5)) if ($n_tries > 1);
229 0         0 next;
230             }
231 0         0 $response = 1;
232 0         0 $self->{dl_fr} = $url;
233 0         0 $self->{dl_to} = $leapseconds_filename;
234 0         0 $success = 1;
235 0         0 last;
236             }
237 0 0       0 last if ($success);
238             }
239             };
240 0         0 return $response;
241             }
242              
243             sub opt {
244 16037     16037 0 14301 my ($self, $name, $default_value, $alt_hr) = @_;
245 16037 100       19293 return $self->{opt_hr}->{$name} if (defined($self->{opt_hr}->{$name}));
246 13034 50 66     16906 return $alt_hr->{$name} if (defined($alt_hr) && ref($alt_hr) eq 'HASH' && defined($alt_hr->{$name}));
      66        
247 13034         14827 return $default_value;
248             }
249              
250             sub _fine_tune {
251 1004     1004   899 my $self = shift(@_);
252 1004         664 my $sum = 0;
253 1004         1121 for (my $i = 0; $i < 100; $i++ ) {
254 100400         126952 $sum += 0 - Time::HiRes::time() + Time::HiRes::time();
255             }
256 1004         816 my $jitter = $sum * 0.17; # Correct for v5.18.1, need to test others for skew.
257             # printf ('jitter=%0.010f'."\n", $jitter);
258 1004         1026 return $jitter;
259             }
260              
261             sub _leapseconds_filename {
262 4     4   5 my($self, $opt_hr) = @_;
263 4   50     5 $opt_hr //= {};
264 4         5 my $pathname = $self->opt('leapseconds_pathname', undef, $opt_hr);
265 4 50       5 return $pathname if (defined($pathname));
266 4 50       10 if ($^O eq 'MSWin32') {
267 0         0 foreach my $f (@LEAPSECOND_WINDOWS_PATHNAME_LIST) {
268 0         0 $pathname = $f;
269 0 0       0 return $f if (-e $f);
270             }
271             } else {
272 4         6 foreach my $f (@LEAPSECOND_UNIX_PATHNAME_LIST) {
273 16         24 $pathname = $f;
274 16 50       5927 return $f if (-e $f);
275             }
276             }
277 4         11 return $pathname;
278             }
279              
280             1;
281              
282             =head1 NAME
283              
284             Time::TAI::Simple - High resolution UNIX epoch time without leapseconds
285              
286             =head1 VERSION
287              
288             1.16
289              
290             =head1 SYNOPSIS
291              
292             use Time::TAI::Simple; # imports tai, tai10, and tai35
293              
294             # simple and fast procedural interface:
295              
296             $seconds_since_epoch = tai();
297             $since_epoch_minus_ten = tai10(); # Probably what you want!
298             $close_to_utc_time_for_now = tai35();
299              
300             # You can likely skip the rest of this synopsis.
301              
302             # object-oriented interface:
303              
304             $tai = Time::TAI::Simple->new();
305              
306             $since_epoch_minus_ten = $tai->time();
307              
308             # download a more up-to-date leapsecond list, and recalculate time base:
309              
310             $tai->download_leapseconds() or die("cannot download leapseconds file");
311             $tai->load_leapseconds();
312             $tai->calculate_base();
313             $since_epoch_minus_ten = $tai->time();
314              
315             # .. or simply download the leapsecond list as part of instantiation.
316             # There is also an option for specifying where to put/find the list:
317              
318             $tai = Time::TAI::Simple->new(
319             download_leapseconds => 1,
320             leapseconds_pathname => '/etc/leap-seconds.list'
321             );
322             $since_epoch_minus_ten = $tai->time();
323              
324             # use mode parameter for TAI-00 time or TAI-35 time:
325              
326             $tai00 = Time::TAI::Simple->new(mode => 'tai');
327             $seconds_since_epoch = $tai00->time();
328              
329             $tai35 = Time::TAI::Simple->new(mode => 'tai35');
330             $close_to_utc_time_for_now = $tai35->time();
331              
332             # reduce processing overhead of instantiation, at the expense of
333             # some precision, by turning off fine-tuning step:
334              
335             $tai = Time::TAI::Simple->new(fine_tune => 0);
336             $nowish = $tai->time(); # accurate to a few milliseconds, not microseconds.
337              
338             =head1 DESCRIPTION
339              
340             The C module provides a very simple way to obtain the
341             number of seconds elapsed since the beginning of the UNIX epoch (January
342             1st, 1970).
343              
344             It differs from C in that it returns the actual number of
345             elapsed seconds, unmodified by the leap seconds introduced by the IETF
346             to make UTC time. These leap seconds can be problematic for automation
347             software, as they effectively make the system clock stand still for one
348             second every few years.
349              
350             D. J. Bernstein describes other problems with leapseconds-adjusted time
351             in this short and sweet article: L
352              
353             C provides a monotonically increasing count of seconds,
354             which means it will never stand still or leap forward or backward due to
355             system clock adjustments (such as from NTP), and avoids leapseconds-related
356             problems in general.
357              
358             This module differs from L
359             and L in a few
360             ways:
361              
362             =over 4
363              
364             * it is much simpler to use,
365              
366             * it uses the same epoch as perl's C
367              
368             * it is a "best effort" implementation, accurate to a few microseconds,
369              
370             * it depends on the local POSIX monotonic clock, not an external atomic clock.
371              
372             =back
373              
374             =head1 ABOUT TAI, TAI10, TAI35
375              
376             This module provides three I of TAI time:
377              
378             B is, very simply, the actual number of elapsed seconds since the epoch.
379              
380             B provides TAI-10 seconds, which is how TAI time has traditionally been
381             most commonly used, because when leapseconds were introduced in 1972, UTC was
382             TAI minus 10 seconds.
383              
384             It is the type of time provided by Arthur David Olson's popular time library,
385             and by the TAI patch currently proposed to the standard zoneinfo implementation.
386             When most people use TAI time, it is usually TAI-10.
387              
388             B provides TAI-35 seconds, which makes it exactly equal to the system
389             clock time returned by C before July 1 2015.
390             As the IETF introduces more leapseconds, B will be one second ahead
391             of the system clock time with each introduction.
392              
393             This mode is provided for use-cases where compatability with other TAI time
394             implementations is not required, and keeping the monotonically increasing time
395             relatively close to the system clock time is desirable.
396              
397             It was decided to provide three types of TAI time instead of allowing an
398             arbitrary seconds offset parameter to make it easier for different systems
399             with different users and different initialization times to pick compatible
400             time modes.
401              
402             =head1 FURTHER READING
403              
404             The following reading is recommended:
405              
406             L
407              
408             L
409              
410             L
411              
412             =head1 MODULE-SCOPE VARIABLES
413              
414             C defines a few externally-accessible variables so that
415             users may customize their values to fit their needs, or to use them in
416             other programming projects.
417              
418             =head2 C<@Time::TAI::Simple::LEAPSECOND_UNIX_PATHNAME_LIST>
419              
420             This list enumerates the pathnames where methods will look for the file
421             listing IETF-defined leapseconds on UNIX systems. The list is traversed
422             in order, and the first readable file will be used.
423              
424             =head2 C<@Time::TAI::Simple::LEAPSECOND_WINDOWS_PATHNAME_LIST>
425              
426             This list enumerates the pathnames where methods will look for the file
427             listing IETF-defined leapseconds on Windows systems. Like its UNIX
428             counterpart, the list is traversed in order, and the first readable file
429             will be used.
430              
431             =head2 C<@Time::TAI::Simple::FALLBACK_LEAPSECONDS_LIST>
432              
433             If no leapseconds list file can be found, C falls back on
434             using this hard-coded list of IETF-defined leapseconds.
435              
436             This is dangerous because if the module is too old to include recently
437             introduced leapseconds, TAI clock objects instantiated after the new
438             leapsecond will be one second ahead of the desired TAI time.
439              
440             This problem can be avoided by downloading the most recent leapsecond list
441             file, either by invoking the C method or by manually
442             downloading it from L
443             and putting it somewhere C will find it, such as
444             C or C.
445              
446             C<@Time::TAI::Simple::FALLBACK_LEAPSECONDS_LIST> is a list of arrayrefs,
447             each referenced array consisting of two elements, an IETF timestamp and a
448             time delta.
449              
450             =head2 C<$Time::TAI::Simple::LEAPSECOND_IETF_DELTA>
451              
452             The IETF represents TAI time as the number of seconds elapsed since 1900-01-01,
453             which is 2208960000 seconds greater than the number of seconds elapsed since
454             1971-01-01 (the UNIX epoch). C keeps this value in
455             C<$Time::TAI::Simple::LEAPSECOND_IETF_DELTA> and uses it internally to convert
456             IETF times to UNIX epoch times.
457              
458             =head2 C<$Time::TAI::Simple::TAI_OR>
459              
460             =head2 C<$Time::TAI::Simple::TAI10_OR>
461              
462             =head2 C<$Time::TAI::Simple::TAI35_OR>
463              
464             When using C's procedural interface, the first time
465             the C, C, and C functions are invoked, they instantiate
466             C with the appropriate C and assign it to these
467             module-scope variables. Subsequent invocations re-use these instants.
468              
469             Before the first invocation, these variables are C.
470              
471             =head1 PROCEDURAL INTERFACE
472              
473             =head2 C<$seconds = tai()>
474              
475             =head2 C<$seconds = tai10()>
476              
477             =head2 C<$seconds = tai35()>
478              
479             These functions return a floating-point number of seconds elapsed since the
480             epoch. They are equivalent to instantiating a C<$tai> object with the
481             corresponding mode and invoking its C
482              
483             B:
484              
485             use Time::TAI::Simple;
486              
487             my $start_time = tai();
488             do_something();
489             my $time_delta = tai() - $start_time;
490             print "doing something took $time_delta seconds\n";
491              
492             =head1 OBJECT ORIENTED INTERFACE
493              
494             =head2 INSTANTIATION
495              
496             =head3 C<$tai = Time::TAI::Simple-Enew(%options)>
497              
498             Instantiates and returns a new C object, hereafter referred
499             to as C<$tai>. Returns C on irrecoverable error.
500              
501             Without options, instantiation will:
502              
503             =over 4
504              
505             * find and load the local copy of the leapseconds file into C<$tai-E{ls_ar}>
506             (or load from C<@Time::TAI::Simple::FALLBACK_LEAPSECONDS_LIST> if no local file
507             is found),
508              
509             * instantiate a C object referencing the POSIX monotonic clock
510             and store it in C<$tai-E{tm_or}>,
511              
512             * calculate a value for C<$tai-E{tm_base}>, which is the number of seconds to
513             add to the POSIX monotonic clock time to derive the TAI-10 time, and
514              
515             * perform a "fine tuning" of this C, based on repeatedly sampling the
516             system clock and estimating the time difference between loading the value of the
517             system clock and loading the value of the monotonic clock.
518              
519             =back
520              
521             This behavior can be changed by passing optional parameters:
522              
523             =over 4
524              
525             =item C 'tai'>
526              
527             =item C 'tai10'> (default)
528              
529             =item C 'tai35'>
530              
531             Adjusts C<$tai-E{tm_base}> so that C<$tai-Etime()> returns the B,
532             B, or B time.
533              
534             =item C 0> (default)
535              
536             =item C 1>
537              
538             When set, causes C to try to http-download a new leapseconds list file
539             before loading the leapseconds file.
540              
541             C maintains an internal list of URLs from which to download
542             this file, and it goes down this list sequentially, stopping when the file has
543             been successfully downloaded. This list may be amended via the C
544             option.
545              
546             By default, no attempt is made to download a leapseconds file. This avoids
547             the potential for very long http timeouts and clobbering any existing
548             administrator-provided leapseconds file.
549              
550             C{ua_ar}> is an arrayref to a list of User-Agent strings,
551             and one of these will be picked at random for HTTP queries, stored in C{ua_str}>.
552              
553             User-Agent spoofing behavior is subject to the following options which can be
554             passed to C (see the documentation for C for a
555             description of their use):
556              
557             agent => ,
558             churn_agent => 1,
559             force_edge => 1,
560              
561             See C also for the download-related options:
562              
563             retry => ,
564             retry_delay =>
565              
566             =item C [$url1, $url2, ...]>
567              
568             Prepends the provided list of URLs to the list of remove locations from which
569             the leapseconds file is downloaded when the C option is
570             set. Use this if your administrator maintains a leapseconds file for
571             organizational use.
572              
573             =item C '/home/tai/leap-seconds.list'>
574              
575             Sets the pathname of the leapseconds list file. This is the pathname to which
576             the file will be stored when downloaded via the C option
577             or C method, and it is the pathname from which the file
578             will be loaded by the C method.
579              
580             By default, C will look for this file in several locations,
581             specified in C<@Time::TAI::Simple::LEAPSECOND_UNIX_PATHNAME_LIST> and
582             C<@Time::TAI::Simple::LEAPSECOND_WINDOWS_PATHNAME_LIST>. The user may opt
583             to replace the contents of these list variables as an alternative to using
584             the C option (for instance, before invoking the C,
585             C, C functions).
586              
587             =item C 0> (default)
588              
589             =item C 1>
590              
591             When set, prevents loading the timestamp list from the timestamp list file
592             or C<@Time::TAI::Simple::FALLBACK_LEAPSECONDS_LIST> into C<$tai-E{ls_ar}>.
593              
594             This only makes sense when setting the C option or when populating
595             C<$tai-E{ls_ar}> manually after instantiation and subsequently re-running the
596             C method.
597              
598             =item C $seconds>
599              
600             When set, circumvents the normal process of calculating C<$tai-E{tm_base}>
601             and uses the provided value instead. This should be the number of seconds
602             added to the time obtained from the POSIX monotonic clock to get the TAI
603             time returned by the C
604              
605             =item C 0>
606              
607             =item C 1> (default)
608              
609             When set (the default), adjusts C, based on repeatedly sampling the
610             system clock and estimating the time difference between loading the value of the
611             system clock and loading the value of the monotonic clock. This can add measurable
612             overhead to the C method -- about 35 microseconds on 2013-era
613             hardware, accounting for about 3/4 of instantiation time.
614              
615             When false, skips this fine-tuning, diminishing the precision of the C
616             method from a few microseconds to a few milliseconds.
617              
618             =back
619              
620             =head2 OBJECT ATTRIBUTES
621              
622             The following attributes of a C instance are public. Changes to
623             some attributes will do nothing until the C and/or C
624             methods are re-run.
625              
626             =head3 C (hash reference)
627              
628             Refers to the parameters passed to C.
629              
630             =head3 C (C object reference)
631              
632             Refers to the POSIX standard monotonic clock interface used by C
633             the current TAI time (along with C).
634              
635             =head3 C (array reference)
636              
637             Refers to the IETF leapseconds list. Its elements are arrayrefs to
638             C<[UTC epoch, seconds]> tuples, and they are ordered by C.
639              
640             =head3 C (integer)
641              
642             Value is the file modification time of the IETF leapseconds list file, if C
643             was loaded from a file, or the time C was loaded from
644             C<@Time::TAI::Simple::FALLBACK_LEAPSECONDS_LIST>, or C<0> if never loaded.
645              
646             =head3 C (floating point)
647              
648             Value is the system clock time the C method last attempted to
649             download the IETF leapseconds list file, or C<0.0> if never attempted.
650              
651             =head3 C (floating point)
652              
653             Value is the difference, in seconds, between the POSIX monotonic clock time
654             and the beginning of the epoch. It is used by C
655             TAI time. It is initialized by the C method, and is C<0.0> if
656             never initialized.
657              
658             =head3 C (string)
659              
660             Exactly one of "tai", "tai10", "tai35", indicating the C with which the
661             object was instantiated, and thus the type of TAI time returned by C
662             Its default value is "tai10".
663              
664             =head2 OBJECT METHODS
665              
666             =head3 C<$tai-Etime()>
667              
668             Returns a floating-point number of seconds elapsed since the epoch.
669              
670             =head3 C<$tai-Ecalculate_base(%options)>
671              
672             C uses the POSIX monotonic clock, the leapsecond list, and
673             the system clock to calculate C<$tai-E{tm_base}>, which is the difference
674             between the POSIX monotonic clock and the TAI time. This difference is used
675             by C
676              
677             This method is normally only called by C, but can be called explicitly
678             to recalculate C<$tai-E{tm_base}> if one of its dependencies is changed.
679              
680             It takes some of the same options as C, and they have the same effect:
681              
682             =over 4
683              
684             =item C $seconds>
685              
686             =item C 0 or 1>
687              
688             =back
689              
690             It has no return value.
691              
692             =head3 C<$tai-Eload_leapseconds(%options)>
693              
694             C finds the local copy of the IETF leapseconds list file,
695             reads it, and populates the object's C attribute. If it cannot find
696             any file it uses the values in C<@Time::TAI::Simple::FALLBACK_LEAPSECONDS_LIST>
697             instead.
698              
699             This method, too, is normally only called by C, but can be called
700             explicitly as needed to re-initialize C<$tai-E{ls_ar}>.
701              
702             For now it takes only one option, which has the same effect as passing it
703             to :
704              
705             =over 4
706              
707             =item C "/home/tai/leap-seconds.list">
708              
709             =back
710              
711             It returns 1 on success, 0 on failure.
712              
713             =head3 C<$tai-Edownload_leapseconds(%options)>
714              
715             C tries to download the IETF leapseconds file so it
716             can be loaded by the C method. It iterates through a
717             list of URLs (any provided via the C parameter first,
718             and an internal list after) and saves the first file it is able to download
719             to either the pathname specified by the C parameter
720             or a sensible location appropriate to the operating system type.
721              
722             This method can be called by C, but only when the C
723             parameter is passed to C with a value which resolves to C.
724              
725             It takes two options, which have the same effects as passing them to C:
726              
727             =over 4
728              
729             =item C [$url1, $url2, ...]>
730              
731             =item C "/home/tai/leap-seconds.list">
732              
733             =back
734              
735             It returns 1 on success, 0 on failure.
736              
737             =head1 EXAMPLES
738              
739             Some simple scripts wrapping this module can be found in C:
740              
741             =over 4
742              
743             =item C
744              
745             Attempts to download the IETF leapseconds file. Will write the pathname of
746             the downloaded file to STDOUT and exit C<0>, or write an error to STDERR and
747             exit C<1>. Pass it the C<-h> option to see its options.
748              
749             On UNIX hosts, it is recommended that a symlink be made in C
750             to C so that it updates the system's
751             leapseconds file as updates become available.
752              
753             =item C
754              
755             Prints the current time. Shows TAI-10 by default. Pass it the C<-h> option
756             to see its options.
757              
758             =back
759              
760             =head1 TODO
761              
762             Needs support for negative leapseconds.
763              
764             Needs support for Linux's POSIX CLOCK_TAI, when available, now that that's properly
765             exposed in recent Linux releases. Currently blocked on L but if
766             an update is too long in the coming I may just roll my own.
767              
768             Needs more unit tests.
769              
770             Does C need changes to be made thread-safe?
771              
772             Test C<_fine_tune> under other versions of perl, find out if the constant factor needs
773             to be version-specific.
774              
775             Do something smart with C and C, like an optional feature which tries to
776             refresh the leapsecond list periodically when stale.
777              
778             =head1 THREADS
779              
780             Not tested, but its dependencies are purportedly thread-safe, and I think the C
781             method, and the C, C, and C functions should be thread-safe. Not
782             so sure about C.
783              
784             =head1 BUGS
785              
786             Probably. In particular, the Windows compatability code is not tested, nor do I have
787             access to a Windows environment in which to test it. I doubt that the paths in
788             C<@Time::TAI::Simple::LEAPSECOND_WINDOWS_PATHNAME_LIST> are sufficient for all
789             environments.
790              
791             Also, some corners were cut in C, particularly in the C<--iso> code,
792             which means its output will not be precisely correct for locales with timezones
793             whose time offsets are not whole hours.
794              
795             Please report relevant bugs to .
796              
797             Bugfix patches are also welcome.
798              
799             =head1 SEE ALSO
800              
801             L has a C method which will give the actual
802             difference between two times, just like taking the difference between two TAI times.
803              
804             If you are a scientist, you might want
805             L or
806             L.
807              
808             An alternative approach to solving the problem of leapsecond-induced bugs
809             is L, "UTC with Smoothed
810             Leap Seconds".
811              
812             =head1 AUTHOR
813              
814             TTK Ciar,
815              
816             =head1 COPYRIGHT AND LICENSE
817              
818             Copyright 2014-2022 by TTK Ciar
819              
820             This library is free software; you can redistribute it and/or modify it under
821             the same terms as Perl itself.
822              
823             =cut