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 |