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__ |