| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
require 5; |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
# This file contains embedded documentation in POD format. |
|
5
|
|
|
|
|
|
|
# Use 'perldoc' to read it. |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package XML::RSS::Timing; |
|
8
|
8
|
|
|
8
|
|
62472
|
use strict; |
|
|
8
|
|
|
|
|
20
|
|
|
|
8
|
|
|
|
|
292
|
|
|
9
|
8
|
|
|
8
|
|
47
|
use Carp (); |
|
|
8
|
|
|
|
|
20
|
|
|
|
8
|
|
|
|
|
157
|
|
|
10
|
8
|
|
|
8
|
|
43
|
use vars qw($VERSION); |
|
|
8
|
|
|
|
|
21
|
|
|
|
8
|
|
|
|
|
329
|
|
|
11
|
8
|
|
|
8
|
|
8028
|
use Time::Local (); |
|
|
8
|
|
|
|
|
15596
|
|
|
|
8
|
|
|
|
|
468
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
$VERSION = '1.07'; |
|
14
|
8
|
50
|
|
8
|
|
229
|
BEGIN { *DEBUG = sub () {0} unless defined &DEBUG; } # set DEBUG level |
|
15
|
|
|
|
|
|
|
|
|
16
|
8
|
|
|
8
|
|
60
|
use constant HOUR_SEC => 60 * 60; |
|
|
8
|
|
|
|
|
20
|
|
|
|
8
|
|
|
|
|
452
|
|
|
17
|
8
|
|
|
8
|
|
40
|
use constant DAY_SEC => 60 * 60 * 24; |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
362
|
|
|
18
|
8
|
|
|
8
|
|
40
|
use constant WEEK_SEC => 60 * 60 * 24 * 7; |
|
|
8
|
|
|
|
|
14
|
|
|
|
8
|
|
|
|
|
422
|
|
|
19
|
8
|
|
|
8
|
|
44
|
use constant MONTH_SEC => 60 * 60 * 24 * 28; |
|
|
8
|
|
|
|
|
15
|
|
|
|
8
|
|
|
|
|
390
|
|
|
20
|
8
|
|
|
8
|
|
40
|
use constant YEAR_SEC => 60 * 60 * 24 * 365; |
|
|
8
|
|
|
|
|
15
|
|
|
|
8
|
|
|
|
|
12652
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
8
|
|
|
8
|
|
45
|
use constant HOURS_IN_WEEK => 24 * 7; |
|
|
8
|
|
|
|
|
13
|
|
|
|
8
|
|
|
|
|
27206
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my @day_names = ( |
|
25
|
|
|
|
|
|
|
"Sunday", "Monday", "Tuesday", "Wednesday", |
|
26
|
|
|
|
|
|
|
"Thursday", "Friday", "Saturday", |
|
27
|
|
|
|
|
|
|
); |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my %day_name2number; |
|
30
|
|
|
|
|
|
|
@day_name2number{@day_names} = (0..6); |
|
31
|
|
|
|
|
|
|
# and going the other way, just look at $day_names[ daynumber ] |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
########################################################################### |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 NAME |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
XML::RSS::Timing - understanding RSS skipHours, skipDays, sy:update* |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
...after getting an RSS/RDF feed that contains the following: |
|
43
|
|
|
|
|
|
|
3 |
|
44
|
|
|
|
|
|
|
hourly |
|
45
|
|
|
|
|
|
|
1970-01-01T08:20+00:00 |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
use XML::RSS::Timing; |
|
48
|
|
|
|
|
|
|
my $timing = XML::RSS::Timing->new; |
|
49
|
|
|
|
|
|
|
$timing->lastPolled( time() ); |
|
50
|
|
|
|
|
|
|
$timing->updatePeriod( 'hourly' ); |
|
51
|
|
|
|
|
|
|
$timing->updateFrequency( 3 ); |
|
52
|
|
|
|
|
|
|
$timing->updateBase( '1970-01-01T08:20+00:00' ); |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# Find out the soonest I can expect new content: |
|
55
|
|
|
|
|
|
|
my $then = $timing->nextUpdate; |
|
56
|
|
|
|
|
|
|
print "I can next poll the feed after $then (", |
|
57
|
|
|
|
|
|
|
scalar(localtime($then)), " local time)\n"; |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Polling it before C<$then> is unlikely to return any new content, according |
|
60
|
|
|
|
|
|
|
to the C elements' values. |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
RSS/RDF modules can use the elements C, C, C, |
|
65
|
|
|
|
|
|
|
C, C, and C |
|
66
|
|
|
|
|
|
|
to express what days/times they won't update, so |
|
67
|
|
|
|
|
|
|
that RSS/RDF clients can conserve network resources by not bothering to |
|
68
|
|
|
|
|
|
|
poll a feed more than once during such a period. |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
This Perl module is for taking in the RSS/RDF C, C, |
|
71
|
|
|
|
|
|
|
C, and C elements' values, and figuring out when they |
|
72
|
|
|
|
|
|
|
say new content might be available. |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
Note: |
|
75
|
|
|
|
|
|
|
This module doesn't depend on XML::RSS, nor in fact have any |
|
76
|
|
|
|
|
|
|
particular relationship with it. |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=head1 OVERVIEW |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
There are two perspectives on this problem: |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=over |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item The "When To Ignore Until?" Perspective |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
With this perspective, you have just polled the given RSS/RDF feed |
|
88
|
|
|
|
|
|
|
(regardless of whether its content turns out to be new), and you want to |
|
89
|
|
|
|
|
|
|
see if the feed says you can skip polling it until some other future |
|
90
|
|
|
|
|
|
|
time. With this perspective, you extract the C fields' |
|
91
|
|
|
|
|
|
|
values and/or the C, C, and C values and pass |
|
92
|
|
|
|
|
|
|
them to a new XML::RSS::Timing object, and then ask when you should |
|
93
|
|
|
|
|
|
|
avoid polling this until. And in the end you'll probably do this: |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my $wait_until = $timing->nextUpdate; |
|
96
|
|
|
|
|
|
|
$wait_until = time() + $Default_Polling_Delay |
|
97
|
|
|
|
|
|
|
# where $Default_Polling_Delay is some reader-defined value |
|
98
|
|
|
|
|
|
|
if $wait_until <= time(); |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
...and then file away C<$wait_until>'s value in some internal table |
|
101
|
|
|
|
|
|
|
that is consulted before polling things, like so: |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
foreach my $feed (@FeedObjects) { |
|
104
|
|
|
|
|
|
|
next if $feed->wait_until > time(); |
|
105
|
|
|
|
|
|
|
# Don't poll it, there'll be nothing new |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
...Else go ahead and poll it, there could be something new... |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=item The "Is It Time Yet?" Perspective |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
With this perspective, you polled the RSS feed at some time in the past, |
|
113
|
|
|
|
|
|
|
and are now considering whether its C fields' values and/or |
|
114
|
|
|
|
|
|
|
the C and C values (which you stored somewhere) say |
|
115
|
|
|
|
|
|
|
you can I poll the feed (or whether there'd be no point, if the |
|
116
|
|
|
|
|
|
|
C fields say you shouldn't expect any new content). With |
|
117
|
|
|
|
|
|
|
this perspective, you use code like this: |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
...after calling ->skipHours and/or ->updatePeriod, etc |
|
120
|
|
|
|
|
|
|
$timing->lastPolled( $when_last_polled ); |
|
121
|
|
|
|
|
|
|
if( time() < $timing->nextUpdate ) { |
|
122
|
|
|
|
|
|
|
# ...Don't poll it, there'll be nothing new... |
|
123
|
|
|
|
|
|
|
} else { |
|
124
|
|
|
|
|
|
|
... go ahead and poll it, there could be something new... |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Of the two perspectives, this second one seems less efficient to me, |
|
128
|
|
|
|
|
|
|
but your mileage may vary. |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=back |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=head1 METHODS |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
This class defines the following methods: |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=over |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=cut |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
########################################################################### |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=item C<< $timing = XML::RSS::Timing->new(); >> |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
This constructor method creates a new object to be used on figuring feed |
|
145
|
|
|
|
|
|
|
timing. You should use a new object for each feed you're considering. |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=cut |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub new { # Vanilla constructor |
|
150
|
154
|
|
|
154
|
1
|
35270
|
my $self = $_[0]; |
|
151
|
154
|
|
33
|
|
|
887
|
$self = bless { }, ref($self) || $self; |
|
152
|
154
|
|
|
|
|
331
|
$self->init(); |
|
153
|
154
|
|
|
|
|
302
|
return $self; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub init { |
|
159
|
154
|
|
|
154
|
0
|
183
|
my $self = $_[0]; |
|
160
|
154
|
|
|
|
|
310
|
$self->use_exceptions(1); |
|
161
|
154
|
|
|
|
|
321
|
$self->updateBase('1970-01-01T00:00+00:00'); |
|
162
|
154
|
|
|
|
|
199
|
return; |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
########################################################################### |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=item C<< $timing->skipHours( I ) >> |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
This adds to this C<$timing> object the given list of hours from |
|
170
|
|
|
|
|
|
|
the given feed's C element. Hours are expressed as |
|
171
|
|
|
|
|
|
|
integers between 0 to 23 inclusive. |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=cut |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub skipHours { |
|
176
|
283
|
100
|
|
283
|
1
|
662
|
return @{ $_[0]{'skipHours'} || [] } if @_ == 1; # as a read list-accessor |
|
|
199
|
100
|
|
|
|
926
|
|
|
177
|
|
|
|
|
|
|
|
|
178
|
84
|
|
|
|
|
220
|
my( $self, @hours ) = @_; |
|
179
|
84
|
|
|
|
|
140
|
foreach my $h (@hours) { |
|
180
|
287
|
50
|
33
|
|
|
3307
|
return $self->boom("Usage: \$timingobj->skipHours( hournumbers... )" ) |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
181
|
|
|
|
|
|
|
unless defined $h and length $h and $h =~ m/^\d\d?$/s |
|
182
|
|
|
|
|
|
|
and $h >= 0 and $h <= 23; # Don't use 24 for midnight. use 0. |
|
183
|
|
|
|
|
|
|
} |
|
184
|
84
|
|
|
|
|
112
|
push @{ $self->{'skipHours'} }, @hours; |
|
|
84
|
|
|
|
|
274
|
|
|
185
|
84
|
|
|
|
|
200
|
return; |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=item C<< $timing->skipDays( I ) >> |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
This adds to this C<$timing> object the given list of days from |
|
193
|
|
|
|
|
|
|
the given feed's C element. The day name strings have |
|
194
|
|
|
|
|
|
|
to be from the set: |
|
195
|
|
|
|
|
|
|
"Sunday", "Monday", "Tuesday", "Wednesday", |
|
196
|
|
|
|
|
|
|
"Thursday", "Friday", "Saturday". |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub skipDays { |
|
201
|
223
|
100
|
|
223
|
1
|
529
|
return @{ $_[0]{'skipDays'} || [] } if @_ == 1; # as a read list-accessor |
|
|
151
|
100
|
|
|
|
692
|
|
|
202
|
|
|
|
|
|
|
|
|
203
|
72
|
|
|
|
|
143
|
my( $self, @daynames ) = @_; |
|
204
|
72
|
|
|
|
|
103
|
foreach my $d (@daynames) { |
|
205
|
141
|
50
|
33
|
|
|
1110
|
return $self->boom("Usage: \$timingobj->skipDays( daynames... )" ) |
|
206
|
|
|
|
|
|
|
unless defined $d and length $d; |
|
207
|
141
|
50
|
|
|
|
359
|
return $self->boom("Usage: \$timingobj->skipDays( daynames... ) -- \"$d\" isn't a day name" ) |
|
208
|
|
|
|
|
|
|
unless exists $day_name2number{$d}; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
72
|
|
|
|
|
106
|
push @{ $self->{'skipDays'} }, @daynames; |
|
|
72
|
|
|
|
|
212
|
|
|
211
|
72
|
|
|
|
|
163
|
return; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
215
|
|
|
|
|
|
|
|
|
216
|
0
|
|
|
0
|
0
|
0
|
sub skipHours_clear { delete $_[0]{'skipHours'}; return; } |
|
|
0
|
|
|
|
|
0
|
|
|
217
|
0
|
|
|
0
|
0
|
0
|
sub skipDays_clear { delete $_[0]{'skipDays' }; return; } |
|
|
0
|
|
|
|
|
0
|
|
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
#========================================================================== |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=item C<< $timing->updateFrequency( I ) >> |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
This sets the given C<$timing> object's |
|
224
|
|
|
|
|
|
|
updateFrequency value from the feed's (optional) C |
|
225
|
|
|
|
|
|
|
element. This has to be a nonzero positive integer. |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=cut |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub updateFrequency { |
|
230
|
178
|
|
|
178
|
1
|
327
|
my($self, $freq) = @_; |
|
231
|
178
|
100
|
|
|
|
797
|
return $self->{'updateFrequency'} if @_ == 1; # as a read accessor |
|
232
|
|
|
|
|
|
|
|
|
233
|
44
|
50
|
33
|
|
|
353
|
return $self->boom( "Usage: \$timingobj->updateFrequency( integer )" ) |
|
|
|
|
33
|
|
|
|
|
|
234
|
|
|
|
|
|
|
unless @_ == 2 and defined($freq) and $freq =~ m/^\d{1,5}$/s; |
|
235
|
|
|
|
|
|
|
# sanity limit: 1-99999 |
|
236
|
|
|
|
|
|
|
|
|
237
|
44
|
|
|
|
|
63
|
$freq += 0; # numerify the string |
|
238
|
44
|
|
50
|
|
|
111
|
$self->{'updateFrequency'} = $freq || 1; |
|
239
|
44
|
|
|
|
|
90
|
return $self->{'updateFrequency'}; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
#========================================================================== |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=item C<< $timing->updateBase( I ) >> |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
This sets the given C<$timing> object's |
|
247
|
|
|
|
|
|
|
updateFrequency value from the feed's (optional) C |
|
248
|
|
|
|
|
|
|
element. This has to be a date in one of these formats: |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
1997 |
|
251
|
|
|
|
|
|
|
1997-07 |
|
252
|
|
|
|
|
|
|
1997-07-16 |
|
253
|
|
|
|
|
|
|
1997-07-16T19:20 |
|
254
|
|
|
|
|
|
|
1997-07-16T19:20Z |
|
255
|
|
|
|
|
|
|
1997-07-16T19:20+01:00 |
|
256
|
|
|
|
|
|
|
1997-07-16T19:20:30+01:00 |
|
257
|
|
|
|
|
|
|
1997-07-16T19:20:30.45+01:00 |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
The default value is "1970-01-01T00:00Z". |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=cut |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub updateBase { |
|
264
|
209
|
|
|
209
|
1
|
392
|
my($self, $base) = @_; |
|
265
|
209
|
50
|
|
|
|
429
|
return $self->{'updateBase'} if @_ == 1; # as a read accessor |
|
266
|
209
|
50
|
33
|
|
|
1314
|
return $self->boom("Usage: \$timingobj->updateBase( 'yyyy-mm-ddThh:mm' )") |
|
|
|
|
33
|
|
|
|
|
|
267
|
|
|
|
|
|
|
unless @_ == 2 and defined($base) and length($base); |
|
268
|
|
|
|
|
|
|
|
|
269
|
209
|
|
|
|
|
415
|
my $date = $self->_iso_date_to_epoch($base); |
|
270
|
|
|
|
|
|
|
|
|
271
|
209
|
50
|
|
|
|
497
|
return $self->boom("\"$base\" isn't a valid time format.") |
|
272
|
|
|
|
|
|
|
unless defined $date; |
|
273
|
|
|
|
|
|
|
|
|
274
|
209
|
|
|
|
|
327
|
$self->{'updateBase_sec'} = $date; |
|
275
|
209
|
|
|
|
|
379
|
$self->{'updateBase'} = $base; |
|
276
|
209
|
|
|
|
|
216
|
DEBUG and print "Setting updateBase to $base and updateBase_sec to $date\n"; |
|
277
|
|
|
|
|
|
|
|
|
278
|
209
|
|
|
|
|
327
|
return $base; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
#========================================================================== |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=item C<< $timing->updatePeriod( I ) >> |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
This sets the given C<$timing> object's |
|
286
|
|
|
|
|
|
|
updatePeriod value from the feed's (optional) C |
|
287
|
|
|
|
|
|
|
element. This has to be a string from the set: |
|
288
|
|
|
|
|
|
|
"hourly", "daily", "weekly", "monthly", "yearly". |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=cut |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub updatePeriod { |
|
293
|
65
|
|
|
65
|
1
|
224
|
my($self, $period) = @_; |
|
294
|
65
|
50
|
|
|
|
144
|
return $self->{'updatePeriod'} if @_ == 1; # as a read accessor |
|
295
|
|
|
|
|
|
|
|
|
296
|
65
|
50
|
33
|
|
|
431
|
return $self->boom("Usage: \$timingobj->updatePeriod( interval_string )") |
|
|
|
|
33
|
|
|
|
|
|
297
|
|
|
|
|
|
|
unless @_ == 2 and defined($period) and length($period); |
|
298
|
|
|
|
|
|
|
|
|
299
|
65
|
|
|
|
|
71
|
my $sec; |
|
300
|
|
|
|
|
|
|
|
|
301
|
65
|
100
|
|
|
|
205
|
if( $period eq 'hourly' ) { $sec = HOUR_SEC } |
|
|
18
|
100
|
|
|
|
22
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
302
|
18
|
|
|
|
|
23
|
elsif( $period eq 'daily' ) { $sec = DAY_SEC } |
|
303
|
18
|
|
|
|
|
23
|
elsif( $period eq 'weekly' ) { $sec = WEEK_SEC } |
|
304
|
7
|
|
|
|
|
10
|
elsif( $period eq 'yearly' ) { $sec = YEAR_SEC; |
|
305
|
7
|
|
|
|
|
21
|
$self->_complain("updatePeriod of 'yearly' is somewhat ill-advised"); |
|
306
|
|
|
|
|
|
|
} |
|
307
|
4
|
|
|
|
|
6
|
elsif( $period eq 'monthly') { $sec = MONTH_SEC; |
|
308
|
4
|
|
|
|
|
13
|
$self->_complain("updatePeriod of 'monthly' is ill-advised"); |
|
309
|
|
|
|
|
|
|
} |
|
310
|
|
|
|
|
|
|
else { |
|
311
|
0
|
|
|
|
|
0
|
$self->boom("updatePeriod value \"$period\" is invalid.\n" |
|
312
|
|
|
|
|
|
|
. "Use (hourly|daily|weekly|monthly|yearly)" ); |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
|
|
315
|
65
|
|
|
|
|
65
|
DEBUG and print "Setting update period to $sec ($period)\n"; |
|
316
|
65
|
|
|
|
|
97
|
$self->{'updatePeriod_sec'} = $sec; |
|
317
|
|
|
|
|
|
|
|
|
318
|
65
|
|
|
|
|
223
|
return $self->{'updatePeriod'} = $period; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=item C<< $timing->lastPolled( I ) >> |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
This sets the time when you last polled this feed. If you don't set |
|
326
|
|
|
|
|
|
|
this, the current time (C |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Note that by "polling", I mean not just requesting the feed, but |
|
329
|
|
|
|
|
|
|
requesting the feed and getting a successful response (regardless of |
|
330
|
|
|
|
|
|
|
whether it's an HTTP 200 "OK" response or an HTTP 304 "Not Modified" |
|
331
|
|
|
|
|
|
|
response). If you request a feed and get any sort of error, then don't |
|
332
|
|
|
|
|
|
|
count that as actually polling the feed. |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
=cut |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub lastPolled { |
|
337
|
545
|
100
|
|
545
|
1
|
1297
|
$_[0]{'lastPolled'} = $_[1] if @_ > 1; # Simple read/write scalar accessor |
|
338
|
545
|
|
|
|
|
1328
|
$_[0]{'lastPolled'}; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
#========================================================================== |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=item C<< $timing->ttl( I ) >> |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
This sets the given C<$timing> object's "ttl" value from the feed's |
|
346
|
|
|
|
|
|
|
(optional) C element. This has to be a nonzero positive integer. |
|
347
|
|
|
|
|
|
|
It represents the minimum number of I that a reader can go between |
|
348
|
|
|
|
|
|
|
times it polls the given feed. It is a somewhat obsolescent (but common) |
|
349
|
|
|
|
|
|
|
predecessor to the C fields. |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
("TTL" stands for "time to live", a term borrowed from DNS cache jargon.) |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=cut |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub ttl { |
|
356
|
152
|
|
|
152
|
1
|
186
|
my($self, $ttl) = @_; |
|
357
|
152
|
100
|
|
|
|
779
|
return $self->{'ttl'} if @_ == 1; # as a read accessor |
|
358
|
|
|
|
|
|
|
|
|
359
|
2
|
50
|
33
|
|
|
20
|
return $self->boom( "Usage: \$timingobj->ttl( integer )" ) |
|
|
|
|
33
|
|
|
|
|
|
360
|
|
|
|
|
|
|
unless @_ == 2 and defined($ttl) and $ttl =~ m/^\d{1,6}$/s; |
|
361
|
|
|
|
|
|
|
# sanity limit: six digits (almost two years!) |
|
362
|
|
|
|
|
|
|
|
|
363
|
2
|
|
|
|
|
4
|
$ttl += 0; # numerify the string |
|
364
|
2
|
|
|
|
|
4
|
$self->{'ttl'} = $ttl; |
|
365
|
2
|
|
|
|
|
3
|
return $ttl; |
|
366
|
|
|
|
|
|
|
# "All those moments will be lost in time, like tears in rain. Time to die." |
|
367
|
|
|
|
|
|
|
# -- Roy Batty in /Blade Runner/ |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
#========================================================================== |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=item C<< $timing->maxAge( I ) >> |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
This sets the given C<$timing> object's "maxAge" value. |
|
375
|
|
|
|
|
|
|
This has to be a nonzero positive integer. |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
This value comes not from the feed, but is an (optional) attribute of |
|
378
|
|
|
|
|
|
|
your client: it denotes the I amount of time (in seconds) that |
|
379
|
|
|
|
|
|
|
your client will go between polling, I whatever this feed |
|
380
|
|
|
|
|
|
|
says. |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
For example, if a feed says it updates only once a year, C is a |
|
383
|
|
|
|
|
|
|
two months, then this timing object will act as if the feed really said |
|
384
|
|
|
|
|
|
|
to update every two months. |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
If you set this, you should probably set it only to a large value, like |
|
387
|
|
|
|
|
|
|
the number of seconds in two months (62*24*60*60). By default, this is |
|
388
|
|
|
|
|
|
|
not set, meaning no maximum is enforced. (So if a feed says to update |
|
389
|
|
|
|
|
|
|
only once a year, then that's what this timing object faithfully |
|
390
|
|
|
|
|
|
|
implements.) |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=cut |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
sub maxAge { |
|
395
|
137
|
|
|
137
|
1
|
158
|
my($self, $max) = @_; |
|
396
|
137
|
100
|
|
|
|
366
|
return $self->{'maxAge'} if @_ == 1; # as a read accessor |
|
397
|
|
|
|
|
|
|
|
|
398
|
3
|
50
|
33
|
|
|
36
|
return $self->boom( "Usage: \$timingobj->maxAge( integer )" ) |
|
|
|
|
33
|
|
|
|
|
|
399
|
|
|
|
|
|
|
unless @_ == 2 and defined($max) and $max =~ m/^\d{1,9}$/s; |
|
400
|
|
|
|
|
|
|
# sanity limit: nine digits (about thirty years!) |
|
401
|
|
|
|
|
|
|
|
|
402
|
3
|
|
|
|
|
4
|
$max += 0; # numerify the string |
|
403
|
3
|
|
|
|
|
6
|
$self->{'maxAge'} = $max; |
|
404
|
3
|
|
|
|
|
7
|
return $max; |
|
405
|
|
|
|
|
|
|
# "All those moments will be lost in time, like tears in rain. Time to die." |
|
406
|
|
|
|
|
|
|
# -- Roy Batty in /Blade Runner/ |
|
407
|
|
|
|
|
|
|
} |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
#========================================================================== |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=item C<< $timing->minAge( I ) >> |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
This sets the given C<$timing> object's "minAge" value. |
|
415
|
|
|
|
|
|
|
This has to be a nonzero positive integer. |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
This value comes not from the feed, but is an (optional) attribute of your |
|
418
|
|
|
|
|
|
|
client: it denotes the I amount of time (in seconds) that your |
|
419
|
|
|
|
|
|
|
client will go between polling, I whatever this feed says. |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
For example, if a feed says it can update every 5 minutes, but your |
|
422
|
|
|
|
|
|
|
C is a half hour, then this timing object will act as if the feed |
|
423
|
|
|
|
|
|
|
really said to update only half hour at most. |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
If you set minAge, you should probably set it only to a smallish value, like |
|
426
|
|
|
|
|
|
|
the number of seconds in an hour (60*60). By default, this is |
|
427
|
|
|
|
|
|
|
not set, meaning no minimum is enforced. |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=cut |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
sub minAge { |
|
432
|
142
|
|
|
142
|
1
|
162
|
my($self, $min) = @_; |
|
433
|
142
|
100
|
|
|
|
1034
|
return $self->{'minAge'} if @_ == 1; # as a read accessor |
|
434
|
|
|
|
|
|
|
|
|
435
|
5
|
50
|
33
|
|
|
37
|
return $self->boom( "Usage: \$timingobj->minAge( integer )" ) |
|
|
|
|
33
|
|
|
|
|
|
436
|
|
|
|
|
|
|
unless @_ == 2 and defined($min) and $min =~ m/^\d{1,9}$/s; |
|
437
|
|
|
|
|
|
|
# sanity limit: nine digits (about thirty years!) |
|
438
|
|
|
|
|
|
|
|
|
439
|
5
|
|
|
|
|
5
|
$min += 0; # numerify the string |
|
440
|
5
|
|
|
|
|
7
|
$self->{'minAge'} = $min; |
|
441
|
5
|
|
|
|
|
9
|
return $min; |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
#========================================================================== |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item C<< $epochtime = $timing->nextUpdate(); >> |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
This method returns the time (in seconds since the epoch) that's the soonest |
|
449
|
|
|
|
|
|
|
that this feed could return new content. |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Note that this doesn't mean you have to actually poll the feed right |
|
452
|
|
|
|
|
|
|
at that second! (That's why this is called "nextUpdate", not something like |
|
453
|
|
|
|
|
|
|
"nextPoll".) Instead, I presume your RSS-reader will do something like |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
run at random intervals |
|
458
|
|
|
|
|
|
|
and will just look for what feeds' nextUpdate times are less than C |
|
459
|
|
|
|
|
|
|
.) |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
Note that C might return the same as this |
|
462
|
|
|
|
|
|
|
feed's C value, in the case of a feed without any ttl/sy:*/update* |
|
463
|
|
|
|
|
|
|
information and where you haven't specified a C. |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=cut |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
sub nextUpdate { |
|
468
|
137
|
|
|
137
|
1
|
317
|
my($self) = @_; |
|
469
|
|
|
|
|
|
|
# Returns a time when we can next poll this feed |
|
470
|
|
|
|
|
|
|
|
|
471
|
137
|
50
|
|
|
|
241
|
$self->lastPolled( time() ) unless defined $self->lastPolled; |
|
472
|
|
|
|
|
|
|
|
|
473
|
137
|
100
|
100
|
|
|
438
|
unless( |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
474
|
|
|
|
|
|
|
defined($self->{'updatePeriod_sec'}) |
|
475
|
|
|
|
|
|
|
or $self->ttl |
|
476
|
|
|
|
|
|
|
or $self->skipHours or $self->skipDays |
|
477
|
|
|
|
|
|
|
) { |
|
478
|
3
|
|
|
|
|
5
|
DEBUG and print "No constraints. Can update whenever.\n"; |
|
479
|
3
|
|
50
|
|
|
5
|
return $self->lastPolled() + ($self->minAge || 0); |
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
|
|
482
|
134
|
100
|
100
|
|
|
1678
|
if( ($self->{'updateBase_sec'} || 0) > $self->lastPolled) { |
|
483
|
6
|
|
|
|
|
10
|
DEBUG and print "updateBase is in the future!\n"; |
|
484
|
6
|
|
|
|
|
8
|
$self->{'updateBase_sec'} = $self->lastPolled; |
|
485
|
|
|
|
|
|
|
# Having an updateBase in the future would do strange things to |
|
486
|
|
|
|
|
|
|
# our math. |
|
487
|
|
|
|
|
|
|
} |
|
488
|
|
|
|
|
|
|
|
|
489
|
134
|
|
|
|
|
285
|
my $then = $self->_unskipped_time_after( |
|
490
|
|
|
|
|
|
|
$self->_enforce_min_max( |
|
491
|
|
|
|
|
|
|
$self->_reckon_next_update_starts() |
|
492
|
|
|
|
|
|
|
) |
|
493
|
|
|
|
|
|
|
); |
|
494
|
134
|
|
|
|
|
148
|
DEBUG and printf "Next open time is %s (%s GMT = %s local)\n", |
|
495
|
|
|
|
|
|
|
$then, scalar(gmtime( $then )), scalar(localtime( $then )); |
|
496
|
134
|
|
|
|
|
316
|
return $then; |
|
497
|
|
|
|
|
|
|
} |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
500
|
|
|
|
|
|
|
sub _enforce_min_max { |
|
501
|
|
|
|
|
|
|
# If we have a maxAge attribute, and if the given time violates that |
|
502
|
|
|
|
|
|
|
# constraint, then enforce that. |
|
503
|
|
|
|
|
|
|
# If we have a maxAge attribute, and if the given time violates that |
|
504
|
|
|
|
|
|
|
# constraint, then enforce that. |
|
505
|
|
|
|
|
|
|
# Otherwise just pass thru the given time. |
|
506
|
|
|
|
|
|
|
# |
|
507
|
134
|
|
|
134
|
|
170
|
my($self, $later) = @_; |
|
508
|
|
|
|
|
|
|
|
|
509
|
134
|
|
|
|
|
215
|
my $min = $self->minAge; |
|
510
|
134
|
100
|
|
|
|
249
|
if($min) { |
|
511
|
4
|
|
|
|
|
7
|
my $soon = $min + $self->lastPolled(); |
|
512
|
4
|
|
|
|
|
5
|
DEBUG and printf " MinTime: %s (%s). Cf later %s (%s)\n", |
|
513
|
|
|
|
|
|
|
$soon, scalar(gmtime($soon)), $later, scalar(gmtime($later)); |
|
514
|
4
|
50
|
|
|
|
9
|
$later = $soon if $soon > $later; # take the later of the two |
|
515
|
|
|
|
|
|
|
} |
|
516
|
|
|
|
|
|
|
|
|
517
|
134
|
|
|
|
|
269
|
my $max = $self->maxAge; |
|
518
|
134
|
100
|
|
|
|
244
|
if($max) { |
|
519
|
9
|
|
|
|
|
18
|
my $far = $max + $self->lastPolled(); |
|
520
|
9
|
|
|
|
|
10
|
DEBUG and printf " MaxTime: %s (%s). Cf later %s (%s)\n", |
|
521
|
|
|
|
|
|
|
$far, scalar(gmtime($far)), $later, scalar(gmtime($later)); |
|
522
|
9
|
100
|
|
|
|
23
|
$later = $far if $far < $later; # take the earlier of the two |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
|
|
525
|
134
|
|
|
|
|
294
|
return $later; |
|
526
|
|
|
|
|
|
|
} |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
sub _unskipped_time_after { |
|
531
|
134
|
|
|
134
|
|
163
|
my($self, $start_time) = @_; |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
# Now see when the next moment is which isn't excluded |
|
534
|
|
|
|
|
|
|
# by a skipHours or skipDays constraint. |
|
535
|
|
|
|
|
|
|
|
|
536
|
134
|
|
|
|
|
144
|
my $then = $start_time; |
|
537
|
|
|
|
|
|
|
|
|
538
|
134
|
|
|
|
|
134
|
my(@hour_is_skippable, @day_is_skippable); |
|
539
|
134
|
|
|
|
|
228
|
foreach my $h ($self->skipHours) { |
|
540
|
287
|
|
|
|
|
494
|
$hour_is_skippable[ $h ] = 1; |
|
541
|
|
|
|
|
|
|
} |
|
542
|
134
|
|
|
|
|
307
|
foreach my $d ($self->skipDays ) { |
|
543
|
141
|
|
|
|
|
295
|
$day_is_skippable[ $day_name2number{$d} ] = 1; |
|
544
|
|
|
|
|
|
|
} |
|
545
|
|
|
|
|
|
|
|
|
546
|
134
|
|
|
|
|
207
|
my($s,$m,$h,$d, $start_hour, $start_day); |
|
547
|
|
|
|
|
|
|
|
|
548
|
134
|
|
|
|
|
141
|
while(1) { |
|
549
|
|
|
|
|
|
|
|
|
550
|
1266
|
|
|
|
|
2520
|
($s,$m,$h, $d) = (gmtime($then))[ 0,1,2, 6 ]; |
|
551
|
|
|
|
|
|
|
# That moment's hournumber and daynumber (and minutes and seconds) |
|
552
|
|
|
|
|
|
|
|
|
553
|
1266
|
100
|
66
|
|
|
4018
|
if(!defined $start_hour) { |
|
|
|
50
|
|
|
|
|
|
|
554
|
134
|
|
|
|
|
139
|
$start_hour = $h; |
|
555
|
134
|
|
|
|
|
145
|
$start_day = $d; |
|
556
|
|
|
|
|
|
|
} elsif($h == $start_hour and $d == $start_day) { |
|
557
|
|
|
|
|
|
|
# The whole week was skipped! |
|
558
|
0
|
|
|
|
|
0
|
$self->_complain("Aborting after revisiting $h h on $day_names[$d]"); |
|
559
|
0
|
|
|
|
|
0
|
return $start_time; |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
|
|
562
|
1266
|
100
|
100
|
|
|
3041
|
unless( $day_is_skippable[$d] or $hour_is_skippable[$h] ) { |
|
563
|
134
|
|
|
|
|
121
|
DEBUG and print " Accepting $h H on $day_names[$d] (", |
|
564
|
|
|
|
|
|
|
scalar(gmtime($then)), ")!\n"; |
|
565
|
134
|
|
|
|
|
345
|
return $then; |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
1132
|
|
|
|
|
917
|
DEBUG > 1 and print " Skipping $h H on $day_names[$d] (", |
|
569
|
|
|
|
|
|
|
scalar(gmtime($then)), ")\n"; |
|
570
|
1132
|
|
|
|
|
1277
|
$then += (HOUR_SEC - ($s + 60 * $m)); |
|
571
|
|
|
|
|
|
|
# Get to the start of the next hour. |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# And loop around again |
|
574
|
|
|
|
|
|
|
} |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
sub _reckon_next_update_starts { |
|
581
|
134
|
|
|
134
|
|
164
|
my($self) = @_; |
|
582
|
|
|
|
|
|
|
|
|
583
|
134
|
|
100
|
|
|
1019
|
my $interval = int( |
|
|
|
|
100
|
|
|
|
|
|
584
|
|
|
|
|
|
|
($self->{'updatePeriod_sec'} || 0) |
|
585
|
|
|
|
|
|
|
/ ($self->updateFrequency || 1) |
|
586
|
|
|
|
|
|
|
); |
|
587
|
|
|
|
|
|
|
# So if we update 5 times daily, our "interval" is (DAY_SEC / 5) seconds |
|
588
|
|
|
|
|
|
|
|
|
589
|
134
|
|
|
|
|
247
|
my $last_update = $self->lastPolled; |
|
590
|
|
|
|
|
|
|
|
|
591
|
134
|
100
|
100
|
|
|
312
|
if( $interval ) { |
|
|
|
100
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# OK, fall thru... |
|
593
|
|
|
|
|
|
|
} elsif( ($self->ttl || 0) > 0 ) { |
|
594
|
9
|
|
|
|
|
11
|
my $ttl = $self->ttl; |
|
595
|
9
|
|
|
|
|
10
|
DEBUG and print "No updateWhatever fields, but using TTL: $ttl minutes\n"; |
|
596
|
9
|
|
|
|
|
21
|
return $last_update + ($ttl * 60); # just conv to seconds |
|
597
|
|
|
|
|
|
|
} else { |
|
598
|
60
|
|
|
|
|
142
|
return $last_update; |
|
599
|
|
|
|
|
|
|
} |
|
600
|
|
|
|
|
|
|
|
|
601
|
65
|
|
100
|
|
|
184
|
my $base = $self->{'updateBase_sec'} || 0; |
|
602
|
65
|
|
|
|
|
123
|
my $start_of_current_interval |
|
603
|
|
|
|
|
|
|
= int( ($last_update-$base) / $interval) * $interval + $base; |
|
604
|
|
|
|
|
|
|
|
|
605
|
65
|
|
|
|
|
80
|
my $then = $start_of_current_interval + $interval; |
|
606
|
|
|
|
|
|
|
|
|
607
|
65
|
|
|
|
|
59
|
if(DEBUG) { |
|
608
|
|
|
|
|
|
|
print " Update interval: $interval s\n", |
|
609
|
|
|
|
|
|
|
" Update base : $base s\n", |
|
610
|
|
|
|
|
|
|
" The current interval started on $start_of_current_interval s\n"; |
|
611
|
|
|
|
|
|
|
printf " = (scalar gmtime(%s * %s + %s))\n", |
|
612
|
|
|
|
|
|
|
( $start_of_current_interval - $base ) / $interval, $interval, $base; |
|
613
|
|
|
|
|
|
|
print " The next interval starts on $then s\n"; |
|
614
|
|
|
|
|
|
|
printf " = (scalar gmtime(%s * %s + %s))\n", |
|
615
|
|
|
|
|
|
|
( $then - $base ) / $interval, $interval, $base; |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
|
|
618
|
65
|
|
|
|
|
169
|
return $then; |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
sub _iso_date_to_epoch { |
|
624
|
486
|
|
|
486
|
|
6039
|
my($self, $date) = @_; |
|
625
|
486
|
50
|
|
|
|
964
|
return undef unless defined $date; |
|
626
|
|
|
|
|
|
|
|
|
627
|
486
|
100
|
|
|
|
3965
|
if( |
|
628
|
|
|
|
|
|
|
my( $Y,$M,$D, $h,$m, $s, $s_fract, $tz_sign, $tz_h, $tz_m ) = |
|
629
|
|
|
|
|
|
|
$date =~ |
|
630
|
|
|
|
|
|
|
# This regexp matches basically ISO 8601 except that the "Z" is optional. |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
m<^ |
|
633
|
|
|
|
|
|
|
(\d\d\d\d) # year |
|
634
|
|
|
|
|
|
|
(?: |
|
635
|
|
|
|
|
|
|
-([01]\d) # month |
|
636
|
|
|
|
|
|
|
(?: |
|
637
|
|
|
|
|
|
|
-([0123]\d) # day |
|
638
|
|
|
|
|
|
|
(?: |
|
639
|
|
|
|
|
|
|
T([012]\d):([012345]\d) # hh:mm |
|
640
|
|
|
|
|
|
|
(?: |
|
641
|
|
|
|
|
|
|
:([0123456]\d) # seconds |
|
642
|
|
|
|
|
|
|
(?: |
|
643
|
|
|
|
|
|
|
(\.\d+) # fractions of a second |
|
644
|
|
|
|
|
|
|
)? |
|
645
|
|
|
|
|
|
|
)? |
|
646
|
|
|
|
|
|
|
# |
|
647
|
|
|
|
|
|
|
# And now the TZ: |
|
648
|
|
|
|
|
|
|
# |
|
649
|
|
|
|
|
|
|
(?: |
|
650
|
|
|
|
|
|
|
Z # Zulu |
|
651
|
|
|
|
|
|
|
| |
|
652
|
|
|
|
|
|
|
(?: # or by offset: |
|
653
|
|
|
|
|
|
|
([-+]) |
|
654
|
|
|
|
|
|
|
([012]\d):([012345]\d) # hh:mm, with leading '+' or '-' |
|
655
|
|
|
|
|
|
|
) |
|
656
|
|
|
|
|
|
|
)? |
|
657
|
|
|
|
|
|
|
)? |
|
658
|
|
|
|
|
|
|
)? |
|
659
|
|
|
|
|
|
|
)? |
|
660
|
|
|
|
|
|
|
$ |
|
661
|
|
|
|
|
|
|
>sx |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
) { |
|
664
|
|
|
|
|
|
|
|
|
665
|
472
|
|
|
|
|
481
|
if(DEBUG) { |
|
666
|
|
|
|
|
|
|
printf "# Date %s matches => %s-%s-%s T%s:%s:%s.%s TZ: %s%s:%s\n", |
|
667
|
|
|
|
|
|
|
$date, |
|
668
|
|
|
|
|
|
|
map defined($_) ? $_ : "_", |
|
669
|
|
|
|
|
|
|
( $Y,$M,$D, $h,$m, $s, $s_fract, $tz_sign, $tz_h, $tz_m ) |
|
670
|
|
|
|
|
|
|
; |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
|
|
673
|
472
|
100
|
|
|
|
975
|
$M = 1 unless defined $M; |
|
674
|
472
|
100
|
|
|
|
794
|
$D = 1 unless defined $D; |
|
675
|
472
|
100
|
|
|
|
744
|
$h = 0 unless defined $h; |
|
676
|
472
|
100
|
|
|
|
821
|
$m = 0 unless defined $m; |
|
677
|
472
|
100
|
|
|
|
904
|
$s = 0 unless defined $s; |
|
678
|
|
|
|
|
|
|
|
|
679
|
472
|
100
|
100
|
|
|
2102
|
return $self->boom("Year out of range: $Y") if $Y < 1902 or $Y > 2037; |
|
680
|
470
|
100
|
66
|
|
|
1942
|
return $self->boom("Month out of range: $M") if $M < 1 or $M > 12; |
|
681
|
468
|
100
|
66
|
|
|
1711
|
return $self->boom("Day out of range: $D") if $D < 1 or $D > 31; |
|
682
|
467
|
50
|
33
|
|
|
1829
|
return $self->boom("Hour out of range: $h") if $h < 0 or $h > 23; |
|
683
|
467
|
50
|
33
|
|
|
1632
|
return $self->boom("Minute out of range: $m") if $h < 0 or $h > 59; |
|
684
|
467
|
50
|
33
|
|
|
1717
|
return $self->boom("Second out of range: $s") if $h < 0 or $h > 60; |
|
685
|
|
|
|
|
|
|
|
|
686
|
467
|
|
|
|
|
551
|
my $tz_offset = 0; |
|
687
|
467
|
100
|
|
|
|
835
|
if(defined $tz_sign) { |
|
688
|
201
|
|
|
|
|
408
|
$tz_offset = ($tz_h * 60 + $tz_m) * 60; |
|
689
|
201
|
100
|
|
|
|
437
|
$tz_offset = 0 - $tz_offset if $tz_sign eq '-'; |
|
690
|
|
|
|
|
|
|
} |
|
691
|
|
|
|
|
|
|
|
|
692
|
467
|
|
|
|
|
543
|
my $time = eval { Time::Local::timegm( $s,$m,$h, $D,$M-1,$Y-1900 ) }; |
|
|
467
|
|
|
|
|
1582
|
|
|
693
|
467
|
100
|
|
|
|
10834
|
return $self->boom("Couldn't convert $date to an exact moment") |
|
694
|
|
|
|
|
|
|
unless defined $time; |
|
695
|
|
|
|
|
|
|
|
|
696
|
466
|
50
|
66
|
|
|
1068
|
$time++ if $s_fract and $s_fract >= .5; |
|
697
|
466
|
|
|
|
|
587
|
$time -= $tz_offset; |
|
698
|
466
|
|
|
|
|
1237
|
return $time; |
|
699
|
|
|
|
|
|
|
} else { |
|
700
|
14
|
|
|
|
|
15
|
DEBUG and print "# Date $date doesn't match.\n"; |
|
701
|
14
|
|
|
|
|
33
|
return undef; |
|
702
|
|
|
|
|
|
|
} |
|
703
|
|
|
|
|
|
|
} |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
706
|
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=item C<< $timing->use_exceptions( 0 ) >> |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
=item C<< $timing->use_exceptions( 1 ) >> |
|
710
|
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
This sets whether this object will (with a 1) or won't (with a 0) use |
|
712
|
|
|
|
|
|
|
exceptions (C's) to signal errors, or whether it will simply |
|
713
|
|
|
|
|
|
|
muddle through and collect them in C. |
|
714
|
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
Basically, errors can come from passing invalid parameters to this |
|
716
|
|
|
|
|
|
|
module's methods, such as passing "friday" to C (instead of |
|
717
|
|
|
|
|
|
|
"Friday"), or passing 123 to C (instead of an integer |
|
718
|
|
|
|
|
|
|
in the range 0-23), etc. |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
B |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=cut |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
sub use_exceptions { |
|
725
|
306
|
50
|
|
306
|
1
|
1774
|
$_[0]{'_die'} = $_[1] if @_ > 1; # Simple read/write scalar accessor |
|
726
|
306
|
|
|
|
|
539
|
$_[0]{'_die'}; |
|
727
|
|
|
|
|
|
|
} |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=item C<< @complaints = $timing->complaints() >> |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
This returns a list of any errors that were encountered in dealing with |
|
734
|
|
|
|
|
|
|
this C<$timing> object. Errors can result from blocking exceptions |
|
735
|
|
|
|
|
|
|
(if C is off), or from non-fatal warnings of interest |
|
736
|
|
|
|
|
|
|
while debugging (like if C was told to skip all 24 hours). |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
If there were no complaints, this will simply return an empty list. |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=cut |
|
741
|
|
|
|
|
|
|
|
|
742
|
6
|
100
|
|
6
|
1
|
24
|
sub complaints { return @{ $_[0]->{'complaints'} || [] }; } |
|
|
6
|
|
|
|
|
34
|
|
|
743
|
|
|
|
|
|
|
# Simple list read-accessor |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
########################################################################### |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
sub boom { |
|
748
|
6
|
|
|
6
|
0
|
29
|
my($self, @error) = @_; |
|
749
|
6
|
50
|
|
|
|
39
|
if( $self->{'_die'} ) { |
|
750
|
0
|
|
|
|
|
0
|
Carp::confess(join '', @error) |
|
751
|
|
|
|
|
|
|
} else { |
|
752
|
3
|
|
|
|
|
9
|
$self->_complain(@error); |
|
753
|
|
|
|
|
|
|
} |
|
754
|
3
|
|
|
|
|
10
|
return; |
|
755
|
|
|
|
|
|
|
} |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
#-------------------------------------------------------------------------- |
|
758
|
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
sub _complain { |
|
760
|
14
|
|
|
14
|
|
27
|
my($self, @complaint) = @_; |
|
761
|
14
|
|
|
|
|
21
|
push @{ $self->{'complaints'} }, join '', @complaint; |
|
|
14
|
|
|
|
|
49
|
|
|
762
|
14
|
|
|
|
|
18
|
DEBUG and print join '', @complaint, "\n"; |
|
763
|
14
|
|
|
|
|
28
|
return; |
|
764
|
|
|
|
|
|
|
} |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
########################################################################### |
|
767
|
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# Aliases for the more Perly foo_bar_baz style. See "perldoc perlstyle" |
|
769
|
|
|
|
|
|
|
|
|
770
|
0
|
|
|
0
|
0
|
|
sub skip_days { shift->skipDays( @_) } |
|
771
|
0
|
|
|
0
|
0
|
|
sub skip_hours { shift->skipHours( @_) } |
|
772
|
0
|
|
|
0
|
0
|
|
sub update_base { shift->updateBase( @_) } |
|
773
|
0
|
|
|
0
|
0
|
|
sub update_period { shift->updatePeriod( @_) } |
|
774
|
0
|
|
|
0
|
0
|
|
sub update_frequency { shift->updateFrequency(@_) } |
|
775
|
0
|
|
|
0
|
0
|
|
sub next_update { shift->nextUpdate( @_) } |
|
776
|
0
|
|
|
0
|
0
|
|
sub last_polled { shift->lastPolled( @_) } |
|
777
|
0
|
|
|
0
|
0
|
|
sub max_age { shift->maxAge( @_) } |
|
778
|
0
|
|
|
0
|
0
|
|
sub min_age { shift->minAge( @_) } |
|
779
|
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
########################################################################### |
|
781
|
|
|
|
|
|
|
1; |
|
782
|
|
|
|
|
|
|
__END__ |