| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Elive::Util; |
|
2
|
36
|
|
|
36
|
|
30455
|
use warnings; use strict; |
|
|
36
|
|
|
36
|
|
71
|
|
|
|
36
|
|
|
|
|
1119
|
|
|
|
36
|
|
|
|
|
198
|
|
|
|
36
|
|
|
|
|
80
|
|
|
|
36
|
|
|
|
|
1623
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
36
|
|
|
36
|
|
38891
|
use Term::ReadKey; |
|
|
36
|
|
|
|
|
199851
|
|
|
|
36
|
|
|
|
|
3773
|
|
|
5
|
36
|
|
|
36
|
|
51249
|
use Term::ReadLine; |
|
|
36
|
|
|
|
|
245577
|
|
|
|
36
|
|
|
|
|
1296
|
|
|
6
|
36
|
|
|
36
|
|
37277
|
use IO::Interactive; |
|
|
36
|
|
|
|
|
518714
|
|
|
|
36
|
|
|
|
|
264
|
|
|
7
|
36
|
|
|
36
|
|
1710
|
use Scalar::Util; |
|
|
36
|
|
|
|
|
84
|
|
|
|
36
|
|
|
|
|
1696
|
|
|
8
|
36
|
|
|
36
|
|
33689
|
use Clone; |
|
|
36
|
|
|
|
|
37697
|
|
|
|
36
|
|
|
|
|
2004
|
|
|
9
|
36
|
|
|
36
|
|
2308
|
use YAML::Syck; |
|
|
36
|
|
|
|
|
5049
|
|
|
|
36
|
|
|
|
|
2517
|
|
|
10
|
36
|
|
|
36
|
|
2334
|
use Try::Tiny; |
|
|
36
|
|
|
|
|
3266
|
|
|
|
36
|
|
|
|
|
2927
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
|
13
|
|
|
|
|
|
|
|
|
14
|
36
|
|
|
36
|
|
24902
|
use Elive::Util::Type; |
|
|
36
|
|
|
|
|
125
|
|
|
|
36
|
|
|
|
|
68230
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Elive::Util - Utility functions for Elive |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 METHODS |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=cut |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head2 inspect_type |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$type = Elive::Util::inspect_type('Elive::Entity::Participants'); |
|
29
|
|
|
|
|
|
|
if ($type->is_array) { |
|
30
|
|
|
|
|
|
|
# ... |
|
31
|
|
|
|
|
|
|
} |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
Returns an object of type L. |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=cut |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub inspect_type { |
|
38
|
58
|
|
|
58
|
1
|
109
|
my $type_union = shift; |
|
39
|
|
|
|
|
|
|
|
|
40
|
58
|
|
|
|
|
206
|
my @types = split(/\|/, $type_union); |
|
41
|
|
|
|
|
|
|
|
|
42
|
58
|
|
|
|
|
476
|
return Elive::Util::Type->new($types[0]) |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub _freeze { |
|
46
|
254
|
|
|
254
|
|
448
|
my ($val, $type) = @_; |
|
47
|
|
|
|
|
|
|
|
|
48
|
254
|
|
|
|
|
463
|
for ($val) { |
|
49
|
|
|
|
|
|
|
|
|
50
|
254
|
50
|
|
|
|
514
|
if (!defined) { |
|
51
|
0
|
|
|
|
|
0
|
warn "undefined value of type $type\n" |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
else { |
|
54
|
254
|
|
|
|
|
482
|
$_ = string($_, $type); |
|
55
|
254
|
|
|
|
|
389
|
my $raw_val = $_; |
|
56
|
|
|
|
|
|
|
|
|
57
|
254
|
50
|
|
|
|
1065
|
if ($type =~ m{^Bool}ix) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# |
|
60
|
|
|
|
|
|
|
# DBize boolean flags.. |
|
61
|
|
|
|
|
|
|
# |
|
62
|
0
|
0
|
|
|
|
0
|
$_ = $_ ? 'true' : 'false'; |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
elsif ($type =~ m{^(Str|enum)}ix) { |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# |
|
67
|
|
|
|
|
|
|
# low level check for taintness. Only applicible when |
|
68
|
|
|
|
|
|
|
# perl program is running in taint mode |
|
69
|
|
|
|
|
|
|
# |
|
70
|
126
|
50
|
|
|
|
1304
|
die "attempt to freeze tainted data (type $type): $_" |
|
71
|
|
|
|
|
|
|
if _tainted($_); |
|
72
|
|
|
|
|
|
|
# |
|
73
|
|
|
|
|
|
|
# l-r trim |
|
74
|
|
|
|
|
|
|
# |
|
75
|
126
|
50
|
|
|
|
842
|
$_ = $1 |
|
76
|
|
|
|
|
|
|
if m{^ \s* (.*?) \s* $}x; |
|
77
|
126
|
100
|
|
|
|
377
|
$_ = lc if $type =~ m{^enum}; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
elsif ($type =~ m{^(Int|HiResDate)}ix) { |
|
80
|
128
|
|
|
|
|
3135
|
$_ = _tidy_decimal("$_"); |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
elsif ($type =~ m{^Ref|Any}ix) { |
|
83
|
0
|
|
|
|
|
0
|
$_ = undef; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
else { |
|
86
|
0
|
0
|
|
|
|
0
|
die "unable to convert $raw_val to $type\n" |
|
87
|
|
|
|
|
|
|
unless defined; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
}; |
|
91
|
|
|
|
|
|
|
|
|
92
|
254
|
|
|
|
|
1958
|
return $val; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# |
|
96
|
|
|
|
|
|
|
# thawing of elementry datatypes |
|
97
|
|
|
|
|
|
|
# |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub _thaw { |
|
100
|
0
|
|
|
0
|
|
0
|
my ($val, $type) = @_; |
|
101
|
|
|
|
|
|
|
|
|
102
|
0
|
0
|
0
|
|
|
0
|
return $val if $type =~ m{Ref}i |
|
103
|
|
|
|
|
|
|
|| ref( $val); |
|
104
|
|
|
|
|
|
|
|
|
105
|
0
|
0
|
|
|
|
0
|
return unless defined $val; |
|
106
|
|
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
0
|
for ($val) { |
|
108
|
|
|
|
|
|
|
|
|
109
|
0
|
0
|
|
|
|
0
|
if ($type =~ m{^Bool}i) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# |
|
111
|
|
|
|
|
|
|
# Perlise boolean flags.. |
|
112
|
|
|
|
|
|
|
# |
|
113
|
0
|
0
|
|
|
|
0
|
$_ = m{^(true|1)$}i ? 1 : 0; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
elsif ($type =~ m{^(Str|enum)}i) { |
|
116
|
|
|
|
|
|
|
# |
|
117
|
|
|
|
|
|
|
# l-r trim |
|
118
|
|
|
|
|
|
|
# |
|
119
|
0
|
0
|
|
|
|
0
|
$_ = $1 |
|
120
|
|
|
|
|
|
|
if m{^ \s* (.*?) \s* $}x; |
|
121
|
0
|
0
|
|
|
|
0
|
$_ = lc if $type =~ m{^enum}i; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
elsif ($type =~ m{^Int|HiResDate}i) { |
|
124
|
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
0
|
$_ = _tidy_decimal("$_"); |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
elsif ($type eq 'Any') { |
|
129
|
|
|
|
|
|
|
# more or less a placeholder type |
|
130
|
0
|
|
|
|
|
0
|
$_ = string($_); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
else { |
|
133
|
0
|
|
|
|
|
0
|
die "unknown type: $type"; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
}; |
|
136
|
|
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
0
|
return $val; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# |
|
141
|
|
|
|
|
|
|
# _tidy_decimal(): general cleanup and normalisation of an integer. |
|
142
|
|
|
|
|
|
|
# used to clean up numbers for data storage or comparison |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub _tidy_decimal { |
|
145
|
128
|
|
|
128
|
|
382
|
my ($i) = @_; |
|
146
|
|
|
|
|
|
|
# |
|
147
|
|
|
|
|
|
|
# well a number really. don't convert or sprintf etc |
|
148
|
|
|
|
|
|
|
# to avoid overflow. Just normalise it for potential |
|
149
|
|
|
|
|
|
|
# string comparisons |
|
150
|
|
|
|
|
|
|
# |
|
151
|
|
|
|
|
|
|
# l-r trim, also untaint |
|
152
|
|
|
|
|
|
|
# |
|
153
|
128
|
50
|
|
|
|
757
|
if ($i =~ m{^ [\s\+]* (-?\d+) \s* $}x) { |
|
154
|
128
|
|
|
|
|
330
|
$i = $1; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
else { |
|
157
|
0
|
|
|
|
|
0
|
return; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# |
|
161
|
|
|
|
|
|
|
# remove any leading zeros: |
|
162
|
|
|
|
|
|
|
# 000123 => 123 |
|
163
|
|
|
|
|
|
|
# -00045 => -45 |
|
164
|
|
|
|
|
|
|
# -000 => 0 |
|
165
|
|
|
|
|
|
|
# |
|
166
|
|
|
|
|
|
|
|
|
167
|
128
|
|
|
|
|
1077
|
$i =~ s{^ |
|
168
|
|
|
|
|
|
|
(-?) # leading minus retained (for now) |
|
169
|
|
|
|
|
|
|
0* # leading zeros discarded |
|
170
|
|
|
|
|
|
|
(\d+?) # number - retained |
|
171
|
|
|
|
|
|
|
$} |
|
172
|
|
|
|
|
|
|
{$1$2}x; |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# |
|
175
|
|
|
|
|
|
|
# reduce -0 => 0 |
|
176
|
128
|
50
|
|
|
|
515
|
$i = 0 if ($i eq '-0'); |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# |
|
179
|
|
|
|
|
|
|
# sanity check. |
|
180
|
|
|
|
|
|
|
# |
|
181
|
128
|
50
|
|
|
|
650
|
die "bad integer: $_[0]" |
|
182
|
|
|
|
|
|
|
unless $i =~ m{^[+-]?\d+$}; |
|
183
|
|
|
|
|
|
|
|
|
184
|
128
|
|
|
|
|
632
|
return $i; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 prompt |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
my $password = Elive::Util::prompt('Password: ', password => 1) |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Prompt for user input |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=cut |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub prompt { |
|
196
|
0
|
|
|
0
|
1
|
0
|
my ($prompt,%opt) = @_; |
|
197
|
|
|
|
|
|
|
|
|
198
|
0
|
|
0
|
|
|
0
|
chomp($prompt ||= 'input:'); |
|
199
|
|
|
|
|
|
|
|
|
200
|
0
|
0
|
|
|
|
0
|
ReadMode $opt{password}? 2: 1; # Turn off controls keys |
|
201
|
|
|
|
|
|
|
|
|
202
|
0
|
|
|
|
|
0
|
my $input; |
|
203
|
0
|
|
|
|
|
0
|
my $n = 0; |
|
204
|
|
|
|
|
|
|
|
|
205
|
0
|
|
0
|
|
|
0
|
do { |
|
206
|
0
|
0
|
|
|
|
0
|
die "giving up on input of $prompt" if ++$n > 100; |
|
207
|
0
|
0
|
|
|
|
0
|
print $prompt if IO::Interactive::is_interactive(); |
|
208
|
0
|
|
|
|
|
0
|
$input = ReadLine(0); |
|
209
|
|
|
|
|
|
|
return |
|
210
|
0
|
0
|
|
|
|
0
|
unless (defined $input); |
|
211
|
0
|
|
|
|
|
0
|
chomp($input); |
|
212
|
|
|
|
|
|
|
} until (defined($input) && length($input)); |
|
213
|
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
0
|
ReadMode 0; # Reset tty mode before exiting |
|
215
|
|
|
|
|
|
|
|
|
216
|
0
|
|
|
|
|
0
|
return $input; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub _reftype { |
|
220
|
1013
|
|
100
|
1013
|
|
5646
|
return Scalar::Util::reftype( shift() ) || ''; |
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub _clone { |
|
224
|
5
|
|
|
5
|
|
6436
|
return Clone::clone(shift); |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub _tainted { |
|
228
|
126
|
|
|
126
|
|
220
|
return grep { Scalar::Util::tainted($_) } @_; |
|
|
126
|
|
|
|
|
543
|
|
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# |
|
232
|
|
|
|
|
|
|
# Hex encoding/decoding. Use for data streaming. E.g. upload & download |
|
233
|
|
|
|
|
|
|
# of preload data. |
|
234
|
|
|
|
|
|
|
# |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub _hex_decode { |
|
237
|
0
|
|
|
0
|
|
0
|
my $data = shift; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
return |
|
240
|
0
|
0
|
|
|
|
0
|
unless defined $data; |
|
241
|
|
|
|
|
|
|
|
|
242
|
0
|
0
|
|
|
|
0
|
$data = '0'.$data |
|
243
|
|
|
|
|
|
|
unless length($data) % 2 == 0; |
|
244
|
|
|
|
|
|
|
|
|
245
|
0
|
|
|
|
|
0
|
my ($non_hex_char) = ($data =~ m{([^0-9a-f])}ix); |
|
246
|
|
|
|
|
|
|
|
|
247
|
0
|
0
|
|
|
|
0
|
die "non hex character in data: ".$non_hex_char |
|
248
|
|
|
|
|
|
|
if (defined $non_hex_char); |
|
249
|
|
|
|
|
|
|
# |
|
250
|
|
|
|
|
|
|
# Works for simple ascii |
|
251
|
0
|
|
|
|
|
0
|
$data =~ s{(..)}{chr(hex($1))}gex; |
|
|
0
|
|
|
|
|
0
|
|
|
252
|
|
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
0
|
return $data; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub _hex_encode { |
|
257
|
0
|
|
|
0
|
|
0
|
my $data = shift; |
|
258
|
|
|
|
|
|
|
|
|
259
|
0
|
|
|
|
|
0
|
$data =~ s{(.)}{sprintf("%02x", ord($1))}gesx; |
|
|
0
|
|
|
|
|
0
|
|
|
260
|
|
|
|
|
|
|
|
|
261
|
0
|
|
|
|
|
0
|
return $data; |
|
262
|
|
|
|
|
|
|
} |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=head2 string |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
print Elive::Util::string($myscalar); |
|
267
|
|
|
|
|
|
|
print Elive::Util::string($myobj); |
|
268
|
|
|
|
|
|
|
print Elive::Util::string($myref, $datatype); |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
Return a string for an object. This method is widely used for casting |
|
271
|
|
|
|
|
|
|
objects to ids. |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=over 4 |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=item |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
If it's a simple scalar, just pass the value back. |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=item |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
If it's an object use the C method. |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=item |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
If it's a reference, resolve datatype to a class, and use its |
|
286
|
|
|
|
|
|
|
C method. |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=back |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=cut |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub string { |
|
293
|
418
|
|
|
418
|
1
|
728
|
my $obj = shift; |
|
294
|
418
|
|
|
|
|
607
|
my $data_type = shift; |
|
295
|
|
|
|
|
|
|
|
|
296
|
418
|
|
|
|
|
734
|
for ($obj) { |
|
297
|
|
|
|
|
|
|
|
|
298
|
418
|
100
|
|
|
|
1430
|
if ($data_type) { |
|
299
|
360
|
|
|
|
|
1156
|
my ($dt) = ($data_type =~ m{(.*)}); |
|
300
|
|
|
|
|
|
|
|
|
301
|
360
|
|
|
360
|
|
15636
|
return $dt->stringify($_) |
|
302
|
360
|
100
|
|
|
|
3263
|
if try {$dt->can('stringify')}; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
312
|
|
|
|
|
5131
|
my $reftype = _reftype($_); |
|
306
|
|
|
|
|
|
|
|
|
307
|
312
|
50
|
|
|
|
1593
|
return $_ |
|
308
|
|
|
|
|
|
|
unless $reftype; |
|
309
|
|
|
|
|
|
|
|
|
310
|
0
|
0
|
0
|
|
|
|
return $_->stringify |
|
311
|
|
|
|
|
|
|
if (Scalar::Util::blessed($_) && $_->can('stringify')); |
|
312
|
|
|
|
|
|
|
|
|
313
|
0
|
0
|
|
|
|
|
if ($reftype eq 'ARRAY') { |
|
314
|
0
|
|
|
|
|
|
return join(',', map {string($_ => $data_type)} @$_) |
|
|
0
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# |
|
319
|
|
|
|
|
|
|
# Nothing else worked; dump it. |
|
320
|
|
|
|
|
|
|
# |
|
321
|
0
|
|
|
|
|
|
return YAML::Syck::Dump($obj); |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=head2 next_quarter_hour |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Quarter hour advancement for the Time Module impoverished. |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
my $start = Elive::Util::next_quarter_hour(); |
|
329
|
|
|
|
|
|
|
my $end = Elive::Util::next_quarter_hour($start); |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Advance to the next quarter hour without the use of any supporting |
|
332
|
|
|
|
|
|
|
time modules. We just simply increment in seconds until C |
|
333
|
|
|
|
|
|
|
indicates that we're exactly on a quarter hour and ahead of the start time. |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
A small initial increment is added to ensure that the date remains |
|
336
|
|
|
|
|
|
|
in the future, allowing for minor gotchas such as leap seconds, general |
|
337
|
|
|
|
|
|
|
latency and smallish time drifts between the client and server. |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=cut |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
sub next_quarter_hour { |
|
342
|
0
|
|
0
|
0
|
1
|
|
my $time = shift || time(); |
|
343
|
|
|
|
|
|
|
|
|
344
|
0
|
|
|
|
|
|
$time += 30; |
|
345
|
|
|
|
|
|
|
|
|
346
|
0
|
|
|
|
|
|
for (;;) { |
|
347
|
0
|
|
|
|
|
|
my @t = localtime(++$time); |
|
348
|
0
|
|
|
|
|
|
my $sec = $t[0]; |
|
349
|
0
|
|
|
|
|
|
my $min = $t[1]; |
|
350
|
|
|
|
|
|
|
|
|
351
|
0
|
0
|
0
|
|
|
|
last unless $min % 15 || $sec; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
0
|
|
|
|
|
|
return $time; |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
1; |