line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
use 5.006; |
3
|
8
|
|
|
8
|
|
866923
|
use strict; |
|
8
|
|
|
|
|
88
|
|
4
|
8
|
|
|
8
|
|
48
|
use warnings; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
171
|
|
5
|
8
|
|
|
8
|
|
37
|
use feature qw(state); |
|
8
|
|
|
|
|
8
|
|
|
8
|
|
|
|
|
207
|
|
6
|
8
|
|
|
8
|
|
36
|
|
|
8
|
|
|
|
|
13
|
|
|
8
|
|
|
|
|
1056
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Date::Utility - A class that represents a datetime in various format |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=cut |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $VERSION = '1.11'; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use Date::Utility; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Date::Utility->new(); # Use current time |
20
|
|
|
|
|
|
|
Date::Utility->new(1249637400); |
21
|
|
|
|
|
|
|
Date::Utility->new('dd-mmm-yy'); |
22
|
|
|
|
|
|
|
Date::Utility->new('dd-mmm-yyyy'); |
23
|
|
|
|
|
|
|
Date::Utility->new('dd-Mmm-yy hh:mm:ssGMT'); |
24
|
|
|
|
|
|
|
Date::Utility->new('dd-Mmm-yy hhhmm'); |
25
|
|
|
|
|
|
|
Date::Utility->new('YYYY-MM-DD'); |
26
|
|
|
|
|
|
|
Date::Utility->new('YYYYMMDD'); |
27
|
|
|
|
|
|
|
Date::Utility->new('YYYYMMDDHHMMSS'); |
28
|
|
|
|
|
|
|
Date::Utility->new('YYYY-MM-DD HH:MM:SS'); |
29
|
|
|
|
|
|
|
Date::Utility->new('YYYY-MM-DDTHH:MM:SSZ'); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 DESCRIPTION |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
A class that represents a datetime in various format |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=cut |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
use Moose; |
38
|
8
|
|
|
8
|
|
5558
|
use Carp qw( confess croak ); |
|
8
|
|
|
|
|
3244247
|
|
|
8
|
|
|
|
|
40
|
|
39
|
8
|
|
|
8
|
|
48994
|
use POSIX qw( floor ); |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
484
|
|
40
|
8
|
|
|
8
|
|
47
|
use Scalar::Util qw(looks_like_number); |
|
8
|
|
|
|
|
13
|
|
|
8
|
|
|
|
|
60
|
|
41
|
8
|
|
|
8
|
|
14188
|
use Tie::Hash::LRU; |
|
8
|
|
|
|
|
13
|
|
|
8
|
|
|
|
|
328
|
|
42
|
8
|
|
|
8
|
|
5837
|
use Time::Local qw(timegm); |
|
8
|
|
|
|
|
4772
|
|
|
8
|
|
|
|
|
248
|
|
43
|
8
|
|
|
8
|
|
3935
|
use Syntax::Keyword::Try; |
|
8
|
|
|
|
|
11692
|
|
|
8
|
|
|
|
|
510
|
|
44
|
8
|
|
|
8
|
|
4638
|
use Time::Duration::Concise::Localize; |
|
8
|
|
|
|
|
18864
|
|
|
8
|
|
|
|
|
43
|
|
45
|
8
|
|
|
8
|
|
4470
|
use POSIX qw(floor); |
|
8
|
|
|
|
|
112679
|
|
|
8
|
|
|
|
|
284
|
|
46
|
8
|
|
|
8
|
|
55
|
|
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
46
|
|
47
|
|
|
|
|
|
|
my %popular; |
48
|
|
|
|
|
|
|
my $lru = tie %popular, 'Tie::Hash::LRU', 300; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
has epoch => ( |
51
|
|
|
|
|
|
|
is => 'ro', |
52
|
|
|
|
|
|
|
isa => 'Int', |
53
|
|
|
|
|
|
|
required => 1, |
54
|
|
|
|
|
|
|
); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
has [qw( |
57
|
|
|
|
|
|
|
datetime_ddmmmyy_hhmmss_TZ |
58
|
|
|
|
|
|
|
datetime_ddmmmyy_hhmmss |
59
|
|
|
|
|
|
|
datetime_yyyymmdd_hhmmss |
60
|
|
|
|
|
|
|
datetime_yyyymmdd_hhmmss_TZ |
61
|
|
|
|
|
|
|
datetime_iso8601 |
62
|
|
|
|
|
|
|
date |
63
|
|
|
|
|
|
|
datetime |
64
|
|
|
|
|
|
|
date_ddmmyy |
65
|
|
|
|
|
|
|
date_ddmmyyyy |
66
|
|
|
|
|
|
|
date_ddmmmyy |
67
|
|
|
|
|
|
|
date_yyyymmdd |
68
|
|
|
|
|
|
|
date_ddmmmyyyy |
69
|
|
|
|
|
|
|
date_ddmonthyyyy |
70
|
|
|
|
|
|
|
days_in_month |
71
|
|
|
|
|
|
|
db_timestamp |
72
|
|
|
|
|
|
|
day_as_string |
73
|
|
|
|
|
|
|
full_day_name |
74
|
|
|
|
|
|
|
month_as_string |
75
|
|
|
|
|
|
|
full_month_name |
76
|
|
|
|
|
|
|
http_expires_format |
77
|
|
|
|
|
|
|
iso8601 |
78
|
|
|
|
|
|
|
time |
79
|
|
|
|
|
|
|
time_hhmm |
80
|
|
|
|
|
|
|
time_hhmmss |
81
|
|
|
|
|
|
|
time_cutoff |
82
|
|
|
|
|
|
|
timezone |
83
|
|
|
|
|
|
|
second |
84
|
|
|
|
|
|
|
minute |
85
|
|
|
|
|
|
|
hour |
86
|
|
|
|
|
|
|
day_of_month |
87
|
|
|
|
|
|
|
quarter_of_year |
88
|
|
|
|
|
|
|
month |
89
|
|
|
|
|
|
|
year |
90
|
|
|
|
|
|
|
_gmtime_attrs |
91
|
|
|
|
|
|
|
year_in_two_digit |
92
|
|
|
|
|
|
|
day_of_week |
93
|
|
|
|
|
|
|
day_of_year |
94
|
|
|
|
|
|
|
days_since_epoch |
95
|
|
|
|
|
|
|
seconds_after_midnight |
96
|
|
|
|
|
|
|
is_a_weekend |
97
|
|
|
|
|
|
|
is_a_weekday |
98
|
|
|
|
|
|
|
) |
99
|
|
|
|
|
|
|
] => ( |
100
|
|
|
|
|
|
|
is => 'ro', |
101
|
|
|
|
|
|
|
lazy_build => 1, |
102
|
|
|
|
|
|
|
); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
my $self = shift; |
105
|
|
|
|
|
|
|
my %params; |
106
|
409
|
|
|
409
|
|
635
|
|
107
|
409
|
|
|
|
|
804
|
@params{qw(second minute hour day_of_month month year day_of_week day_of_year)} = gmtime($self->{epoch}); |
108
|
|
|
|
|
|
|
|
109
|
409
|
|
|
|
|
3494
|
return \%params; |
110
|
|
|
|
|
|
|
} |
111
|
409
|
|
|
|
|
8856
|
|
112
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 second |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=cut |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
my $self = shift; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
return sprintf '%02d', $self->_gmtime_attrs->{second}; |
121
|
8
|
|
|
8
|
|
14
|
} |
122
|
|
|
|
|
|
|
|
123
|
8
|
|
|
|
|
174
|
=head2 minute |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
my $self = shift; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
return sprintf '%02d', $self->_gmtime_attrs->{minute}; |
130
|
|
|
|
|
|
|
} |
131
|
8
|
|
|
8
|
|
14
|
|
132
|
|
|
|
|
|
|
=head2 hour |
133
|
8
|
|
|
|
|
160
|
|
134
|
|
|
|
|
|
|
=cut |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
my $self = shift; |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
return sprintf '%02d', $self->_gmtime_attrs->{hour}; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
8
|
|
|
8
|
|
15
|
=head2 day_of_month |
142
|
|
|
|
|
|
|
|
143
|
8
|
|
|
|
|
169
|
=cut |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
my $self = shift; |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
return $self->_gmtime_attrs->{day_of_month}; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head2 month |
151
|
408
|
|
|
408
|
|
604
|
|
152
|
|
|
|
|
|
|
=cut |
153
|
408
|
|
|
|
|
8106
|
|
154
|
|
|
|
|
|
|
my $self = shift; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
my $gm_mon = $self->_gmtime_attrs->{month}; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
return ++$gm_mon; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
408
|
|
|
408
|
|
598
|
=head2 quarter_of_year |
162
|
|
|
|
|
|
|
|
163
|
408
|
|
|
|
|
8191
|
=cut |
164
|
|
|
|
|
|
|
|
165
|
408
|
|
|
|
|
7641
|
my $self = shift; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
return int(($self->month - 0.0000001) / 3) + 1; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head2 day_of_week |
172
|
|
|
|
|
|
|
|
173
|
3
|
|
|
3
|
|
6
|
return day of week begin with 0 |
174
|
|
|
|
|
|
|
|
175
|
3
|
|
|
|
|
61
|
=cut |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
return ((shift->{epoch} / 86400) + 4) % 7; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 day_of_year |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=cut |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
my $self = shift; |
185
|
|
|
|
|
|
|
|
186
|
684
|
|
|
684
|
|
14320
|
return $self->_gmtime_attrs->{day_of_year} + 1; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head2 year |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=cut |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
my $self = shift; |
194
|
3
|
|
|
3
|
|
6
|
|
195
|
|
|
|
|
|
|
return $self->_gmtime_attrs->{year} + 1900; |
196
|
3
|
|
|
|
|
70
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head2 time |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=cut |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
my $self = shift; |
203
|
|
|
|
|
|
|
|
204
|
408
|
|
|
408
|
|
692
|
return $self->hour . 'h' . $self->minute; |
205
|
|
|
|
|
|
|
} |
206
|
408
|
|
|
|
|
9035
|
|
207
|
|
|
|
|
|
|
=head2 time_hhmm |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Returns time in hh:mm format |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
my $self = shift; |
214
|
3
|
|
|
3
|
|
7
|
|
215
|
|
|
|
|
|
|
return join(':', ($self->hour, $self->minute)); |
216
|
3
|
|
|
|
|
62
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=head2 time_hhmmss |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
Returns time in hh:mm:ss format |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=cut |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
my $self = shift; |
225
|
|
|
|
|
|
|
|
226
|
13
|
|
|
13
|
|
22
|
return join(':', ($self->time_hhmm, $self->second)); |
227
|
|
|
|
|
|
|
} |
228
|
13
|
|
|
|
|
310
|
|
229
|
|
|
|
|
|
|
=head2 time_cutoff |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Set the timezone for cutoff to UTC |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=cut |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
my $self = shift; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
return 'UTC ' . $self->time_hhmm; |
238
|
13
|
|
|
13
|
|
23
|
} |
239
|
|
|
|
|
|
|
|
240
|
13
|
|
|
|
|
289
|
=head2 year_in_two_digit |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Returns year in two digit format. Example: 15 |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=cut |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
my $self = shift; |
247
|
|
|
|
|
|
|
my $two_digit_year = $self->year - 2000; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
if ($two_digit_year < 0) { |
250
|
3
|
|
|
3
|
|
6
|
$two_digit_year += 100; |
251
|
|
|
|
|
|
|
} |
252
|
3
|
|
|
|
|
63
|
|
253
|
|
|
|
|
|
|
return sprintf '%02d', $two_digit_year; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=head2 timezone |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
Set the timezone to GMT |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=cut |
261
|
|
|
|
|
|
|
|
262
|
3
|
|
|
3
|
|
6
|
return 'GMT'; |
263
|
3
|
|
|
|
|
71
|
} |
264
|
|
|
|
|
|
|
|
265
|
3
|
100
|
|
|
|
8
|
=head2 datetime |
266
|
1
|
|
|
|
|
3
|
|
267
|
|
|
|
|
|
|
See, db_timestamp |
268
|
|
|
|
|
|
|
|
269
|
3
|
|
|
|
|
77
|
=cut |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
my $self = shift; |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
return $self->db_timestamp; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
=head2 datetime_ddmmmyy_hhmmss_TZ |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
Returns datetime in "dd-mmm-yy hh:mm:ssGMT" format |
279
|
6
|
|
|
6
|
|
118
|
|
280
|
|
|
|
|
|
|
=cut |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
my $self = shift; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
return $self->date_ddmmmyy . ' ' . $self->time_hhmmss . $self->timezone; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head2 datetime_ddmmmyy_hhmmss |
288
|
|
|
|
|
|
|
|
289
|
3
|
|
|
3
|
|
8
|
Returns datetime in "dd-mmm-yy hh:mm:ss" format |
290
|
|
|
|
|
|
|
|
291
|
3
|
|
|
|
|
71
|
=cut |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
my $self = shift; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
return $self->date_ddmmmyy . ' ' . $self->time_hhmmss; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head2 date_ddmmmyyyy |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
Returns date in dd-mmm-yyyy format |
301
|
3
|
|
|
3
|
|
6
|
|
302
|
|
|
|
|
|
|
=cut |
303
|
3
|
|
|
|
|
66
|
|
304
|
|
|
|
|
|
|
my $self = shift; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
return join('-', ($self->day_of_month, $self->month_as_string, $self->year)); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=head2 date_ddmonthyyyy |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
Returns date in dd-month-yyyy format |
312
|
|
|
|
|
|
|
|
313
|
0
|
|
|
0
|
|
0
|
=cut |
314
|
|
|
|
|
|
|
|
315
|
0
|
|
|
|
|
0
|
my $self = shift; |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
return join(' ', ($self->day_of_month, $self->full_month_name, $self->year)); |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=head2 date |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Returns datetime in YYYY-MM-DD format |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=cut |
325
|
3
|
|
|
3
|
|
7
|
|
326
|
|
|
|
|
|
|
my $self = shift; |
327
|
3
|
|
|
|
|
63
|
|
328
|
|
|
|
|
|
|
return $self->date_yyyymmdd; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=head2 date_ddmmmyy |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Returns datetime in dd-Mmm-yy format |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=cut |
336
|
|
|
|
|
|
|
|
337
|
3
|
|
|
3
|
|
6
|
my $self = shift; |
338
|
|
|
|
|
|
|
|
339
|
3
|
|
|
|
|
68
|
return join('-', ($self->day_of_month, $self->month_as_string, $self->year_in_two_digit)); |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head2 days_since_epoch |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Returns number of days since 1970-01-01 |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=cut |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
my $self = shift; |
349
|
818
|
|
|
818
|
|
1162
|
|
350
|
|
|
|
|
|
|
return floor($self->{epoch} / 86400); |
351
|
818
|
|
|
|
|
17001
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=head2 seconds_after_midnight |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Returns number of seconds after midnight of the same day. |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=cut |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
my $self = shift; |
360
|
|
|
|
|
|
|
|
361
|
3
|
|
|
3
|
|
5
|
return $self->{epoch} % 86400; |
362
|
|
|
|
|
|
|
} |
363
|
3
|
|
|
|
|
68
|
|
364
|
|
|
|
|
|
|
=head2 is_a_weekend |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=cut |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
my $self = shift; |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
return ($self->day_of_week == 0 || $self->day_of_week == 6) ? 1 : 0; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
8
|
|
|
8
|
|
17
|
=head2 is_a_weekday |
374
|
|
|
|
|
|
|
|
375
|
8
|
|
|
|
|
204
|
=cut |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
my $self = shift; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
return ($self->is_a_weekend) ? 0 : 1; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
my $EPOCH_RE = qr/^-?[0-9]{1,13}$/; |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=head2 new |
385
|
3
|
|
|
3
|
|
6
|
|
386
|
|
|
|
|
|
|
Returns a Date::Utility object. |
387
|
3
|
|
|
|
|
68
|
|
388
|
|
|
|
|
|
|
=cut |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
## no critic (ProhibitNewMethod) |
391
|
|
|
|
|
|
|
my ($self, $params_ref) = @_; |
392
|
|
|
|
|
|
|
my $new_params = {}; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
if (not defined $params_ref) { |
395
|
3
|
|
|
3
|
|
6
|
$new_params->{epoch} = time; |
396
|
|
|
|
|
|
|
} elsif (ref $params_ref eq 'Date::Utility') { |
397
|
3
|
100
|
100
|
|
|
61
|
return $params_ref; |
398
|
|
|
|
|
|
|
} elsif (ref $params_ref eq 'HASH') { |
399
|
|
|
|
|
|
|
if (not($params_ref->{'datetime'} or $params_ref->{epoch})) { |
400
|
|
|
|
|
|
|
confess 'Must pass either datetime or epoch to the Date object constructor'; |
401
|
|
|
|
|
|
|
} elsif ($params_ref->{'datetime'} and $params_ref->{epoch}) { |
402
|
|
|
|
|
|
|
confess 'Must pass only one of datetime or epoch to the Date object constructor'; |
403
|
|
|
|
|
|
|
} elsif ($params_ref->{epoch}) { |
404
|
|
|
|
|
|
|
#strip other potential parameters |
405
|
3
|
|
|
3
|
|
7
|
$new_params->{epoch} = $params_ref->{epoch}; |
406
|
|
|
|
|
|
|
|
407
|
3
|
100
|
|
|
|
82
|
} else { |
408
|
|
|
|
|
|
|
#strip other potential parameters |
409
|
|
|
|
|
|
|
$new_params = _parse_datetime_param($params_ref->{'datetime'}); |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} elsif ($params_ref =~ $EPOCH_RE) { |
412
|
|
|
|
|
|
|
$new_params->{epoch} = $params_ref; |
413
|
|
|
|
|
|
|
} else { |
414
|
|
|
|
|
|
|
$new_params = _parse_datetime_param($params_ref); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
my $obj = $popular{$new_params->{epoch}}; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
if (not $obj) { |
420
|
224637
|
|
|
224637
|
1
|
252398944
|
$obj = $self->_new($new_params); |
421
|
224637
|
|
|
|
|
358549
|
$popular{$new_params->{epoch}} = $obj; |
422
|
|
|
|
|
|
|
} |
423
|
224637
|
100
|
|
|
|
2010757
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
424
|
4
|
|
|
|
|
12
|
return $obj; |
425
|
|
|
|
|
|
|
|
426
|
1
|
|
|
|
|
3
|
} |
427
|
|
|
|
|
|
|
|
428
|
47
|
100
|
100
|
|
|
256
|
=head2 _parse_datetime_param |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
|
|
|
|
|
429
|
1
|
|
|
|
|
16
|
|
430
|
|
|
|
|
|
|
User may supplies datetime parameters but it currently only supports the following formats: |
431
|
1
|
|
|
|
|
9
|
dd-mmm-yy ddhddGMT, dd-mmm-yy, dd-mmm-yyyy, dd-Mmm-yy hh:mm:ssGMT, YYYY-MM-DD, YYYYMMDD, YYYYMMDDHHMMSS, yyyy-mm-dd hh:mm:ss, yyyy-mm-ddThh:mm:ss or yyyy-mm-ddThh:mm:ssZ. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
|
434
|
7
|
|
|
|
|
20
|
=cut |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
my $mon_re = qr/j(?:an|u[nl])|feb|ma[ry]|a(?:pr|ug)|sep|oct|nov|dec/i; |
437
|
|
|
|
|
|
|
my $sub_second = qr/^[0-9]+\.[0-9]+$/; |
438
|
38
|
|
|
|
|
82
|
my $date_only = qr/^([0-3]?[0-9])-($mon_re)-([0-9]{2}|[0-9]{4})$/; |
439
|
|
|
|
|
|
|
my $time_only_tz = qr/([0-2]?[0-9])[h:]([0-5][0-9])(?::)?([0-5][0-9])?(?:GMT)?/; |
440
|
|
|
|
|
|
|
my $date_with_time = qr /^([0-3]?[0-9])-($mon_re)-([0-9]{2}) $time_only_tz$/; |
441
|
200417
|
|
|
|
|
497391
|
my $numeric_date_regex = qr/([12][0-9]{3})-?([01]?[0-9])-?([0-3]?[0-9])/; |
442
|
|
|
|
|
|
|
my $numeric_date_only = qr/^$numeric_date_regex$/; |
443
|
24168
|
|
|
|
|
41567
|
my $fully_specced = qr/^([12][0-9]{3})-?([01]?[0-9])-?([0-3]?[0-9])(?:T|\s)?([0-2]?[0-9]):?([0-5]?[0-9]):?([0-5]?[0-9])(\.[0-9]+)?(?:Z)?$/; |
444
|
|
|
|
|
|
|
my $numeric_date_only_dd_mm_yyyy = qr/^([0-3]?[0-9])-([01]?[0-9])-([12][0-9]{3})$/; |
445
|
|
|
|
|
|
|
my $datetime_yyyymmdd_hhmmss_TZ = qr/^$numeric_date_regex $time_only_tz$/; |
446
|
224620
|
|
|
|
|
1238624
|
|
447
|
|
|
|
|
|
|
my $datetime = shift; |
448
|
224620
|
100
|
|
|
|
589070
|
|
449
|
200909
|
|
|
|
|
5248645
|
# If it's date only, take the epoch at midnight. |
450
|
200909
|
|
|
|
|
6372708
|
my ($hour, $minute, $second) = (0, 0, 0); |
451
|
|
|
|
|
|
|
my ($day, $month, $year); |
452
|
|
|
|
|
|
|
|
453
|
224620
|
|
|
|
|
1145847
|
# The ordering of these regexes is an attempt to match early |
454
|
|
|
|
|
|
|
# to avoid extra comparisons. If our mix of supplied datetimes changes |
455
|
|
|
|
|
|
|
# it might be worth revisiting this. |
456
|
|
|
|
|
|
|
if ($datetime =~ $sub_second) { |
457
|
|
|
|
|
|
|
# We have an epoch with sub second precision which we can't handle |
458
|
|
|
|
|
|
|
return {epoch => int($datetime)}; |
459
|
|
|
|
|
|
|
} elsif ($datetime =~ $date_only) { |
460
|
|
|
|
|
|
|
$day = $1; |
461
|
|
|
|
|
|
|
$month = month_abbrev_to_number($2); |
462
|
|
|
|
|
|
|
$year = $3; |
463
|
|
|
|
|
|
|
} elsif ($datetime =~ $date_with_time) { |
464
|
|
|
|
|
|
|
$day = $1; |
465
|
|
|
|
|
|
|
$month = month_abbrev_to_number($2); |
466
|
|
|
|
|
|
|
$year = $3; |
467
|
|
|
|
|
|
|
$hour = $4; |
468
|
|
|
|
|
|
|
$minute = $5; |
469
|
|
|
|
|
|
|
if (defined $6) { |
470
|
|
|
|
|
|
|
$second = $6; |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
} elsif ($datetime =~ $numeric_date_only) { |
473
|
|
|
|
|
|
|
$day = $3; |
474
|
|
|
|
|
|
|
$month = $2; |
475
|
|
|
|
|
|
|
$year = $1; |
476
|
|
|
|
|
|
|
} elsif ($datetime =~ $numeric_date_only_dd_mm_yyyy) { |
477
|
24206
|
|
|
24206
|
|
31754
|
$day = $1; |
478
|
|
|
|
|
|
|
$month = $2; |
479
|
|
|
|
|
|
|
$year = $3; |
480
|
24206
|
|
|
|
|
38156
|
} elsif ($datetime =~ $fully_specced) { |
481
|
24206
|
|
|
|
|
30162
|
$day = $3; |
482
|
|
|
|
|
|
|
$month = $2; |
483
|
|
|
|
|
|
|
$year = $1; |
484
|
|
|
|
|
|
|
$hour = $4; |
485
|
|
|
|
|
|
|
$minute = $5; |
486
|
24206
|
50
|
|
|
|
166683
|
$second = $6; |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
487
|
|
|
|
|
|
|
} elsif ($datetime =~ $datetime_yyyymmdd_hhmmss_TZ) { |
488
|
0
|
|
|
|
|
0
|
$year = $1; |
489
|
|
|
|
|
|
|
$month = $2; |
490
|
13
|
|
|
|
|
29
|
$day = $3; |
491
|
13
|
|
|
|
|
38
|
$hour = $4; |
492
|
13
|
|
|
|
|
30
|
$minute = $5; |
493
|
|
|
|
|
|
|
$second = $6; |
494
|
36
|
|
|
|
|
89
|
} |
495
|
36
|
|
|
|
|
87
|
# Type constraints mean we can't ever end up in here. |
496
|
36
|
|
|
|
|
63
|
else { |
497
|
36
|
|
|
|
|
73
|
confess "Invalid datetime format: $datetime"; |
498
|
36
|
|
|
|
|
50
|
} |
499
|
36
|
100
|
|
|
|
83
|
|
500
|
26
|
|
|
|
|
37
|
# Now that we've extracted out values, let's turn them into an epoch. |
501
|
|
|
|
|
|
|
# The all of following adjustments seem kind of gross: |
502
|
|
|
|
|
|
|
if (length $year == 2) { |
503
|
24074
|
|
|
|
|
46581
|
if ($year > 30 and $year < 70) { |
504
|
24074
|
|
|
|
|
29904
|
croak 'Date::Utility only supports two-digit years from 1970-2030. We got [' . $year . ']'; |
505
|
24074
|
|
|
|
|
31734
|
} |
506
|
|
|
|
|
|
|
|
507
|
2
|
|
|
|
|
5
|
$year += ($year <= 30) ? 2000 : 1900; |
508
|
2
|
|
|
|
|
4
|
} |
509
|
2
|
|
|
|
|
3
|
|
510
|
|
|
|
|
|
|
my $epoch = timegm($second, $minute, $hour, $day, $month - 1, $year); |
511
|
70
|
|
|
|
|
186
|
|
512
|
70
|
|
|
|
|
101
|
return { |
513
|
70
|
|
|
|
|
113
|
epoch => $epoch, |
514
|
70
|
|
|
|
|
107
|
second => sprintf("%02d", $second), |
515
|
70
|
|
|
|
|
100
|
minute => sprintf("%02d", $minute), |
516
|
70
|
|
|
|
|
99
|
hour => sprintf("%02d", $hour), |
517
|
|
|
|
|
|
|
day_of_month => $day + 0, |
518
|
1
|
|
|
|
|
3
|
month => $month + 0, |
519
|
1
|
|
|
|
|
1
|
year => $year + 0, |
520
|
1
|
|
|
|
|
3
|
}; |
521
|
1
|
|
|
|
|
1
|
} |
522
|
1
|
|
|
|
|
3
|
|
523
|
1
|
|
|
|
|
1
|
=head2 days_between |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
Returns number of days between two dates. |
526
|
|
|
|
|
|
|
|
527
|
10
|
|
|
|
|
97
|
=cut |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
my ($self, $date) = @_; |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
if (not $date) { |
532
|
24196
|
100
|
|
|
|
43832
|
Carp::croak('Date parameter not defined'); |
533
|
45
|
100
|
100
|
|
|
145
|
} |
534
|
1
|
|
|
|
|
15
|
return $self->days_since_epoch - $date->days_since_epoch; |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
|
537
|
44
|
100
|
|
|
|
92
|
=head2 is_before |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Returns a boolean which indicates whether this date object is earlier in time than the supplied date object. |
540
|
24195
|
|
|
|
|
68364
|
|
541
|
|
|
|
|
|
|
=cut |
542
|
|
|
|
|
|
|
|
543
|
24192
|
|
|
|
|
644471
|
my ($self, $date) = @_; |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
if (not $date) { |
546
|
|
|
|
|
|
|
Carp::croak('Date parameter not defined'); |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
return ($self->{epoch} < $date->{epoch}) ? 1 : undef; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=head2 is_after |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Returns a boolean which indicates whether this date object is later in time than the supplied date object. |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=cut |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
my ($self, $date) = @_; |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
if (not $date) { |
560
|
8
|
|
|
8
|
1
|
47
|
Carp::croak('Date parameter not defined'); |
561
|
|
|
|
|
|
|
} |
562
|
8
|
50
|
|
|
|
21
|
return ($self->{epoch} > $date->{epoch}) ? 1 : undef; |
563
|
0
|
|
|
|
|
0
|
} |
564
|
|
|
|
|
|
|
|
565
|
8
|
|
|
|
|
220
|
=head2 is_same_as |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
Returns a boolean which indicates whether this date object is the same time as the supplied date object. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=cut |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
my ($self, $date) = @_; |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
if (not $date) { |
574
|
|
|
|
|
|
|
Carp::croak('Date parameter not defined'); |
575
|
5
|
|
|
5
|
1
|
3522
|
} |
576
|
|
|
|
|
|
|
return ($self->{epoch} == $date->{epoch}) ? 1 : undef; |
577
|
5
|
50
|
|
|
|
11
|
} |
578
|
0
|
|
|
|
|
0
|
|
579
|
|
|
|
|
|
|
=head2 day_as_string |
580
|
5
|
100
|
|
|
|
23
|
|
581
|
|
|
|
|
|
|
Returns the name of the current day in short form. Example: Sun. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=cut |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
my $self = shift; |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
return substr($self->full_day_name, 0, 3); |
588
|
|
|
|
|
|
|
} |
589
|
|
|
|
|
|
|
|
590
|
5
|
|
|
5
|
1
|
2856
|
=head2 full_day_name |
591
|
|
|
|
|
|
|
|
592
|
5
|
50
|
|
|
|
12
|
Returns the name of the current day. Example: Sunday |
593
|
0
|
|
|
|
|
0
|
|
594
|
|
|
|
|
|
|
=cut |
595
|
5
|
100
|
|
|
|
22
|
|
596
|
|
|
|
|
|
|
# 0..6: Sunday first. |
597
|
|
|
|
|
|
|
my @day_names = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday); |
598
|
|
|
|
|
|
|
my %days_to_num = map { |
599
|
|
|
|
|
|
|
my $day = lc $day_names[$_]; |
600
|
|
|
|
|
|
|
( |
601
|
|
|
|
|
|
|
substr($day, 0, 3) => $_, # Three letter abbreviation |
602
|
|
|
|
|
|
|
$day => $_, # Full day name |
603
|
|
|
|
|
|
|
$_ => $_, # Number as number |
604
|
|
|
|
|
|
|
); |
605
|
13
|
|
|
13
|
1
|
2679
|
} 0 .. $#day_names; |
606
|
|
|
|
|
|
|
|
607
|
13
|
50
|
|
|
|
23
|
my $self = shift; |
608
|
0
|
|
|
|
|
0
|
|
609
|
|
|
|
|
|
|
return $day_names[$self->day_of_week]; |
610
|
13
|
100
|
|
|
|
65
|
} |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=head2 month_as_string |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Returns the name of current month in short form. Example: Jan |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=cut |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
my $self = shift; |
619
|
|
|
|
|
|
|
|
620
|
3
|
|
|
3
|
|
8
|
return month_number_to_abbrev($self->month); |
621
|
|
|
|
|
|
|
} |
622
|
3
|
|
|
|
|
67
|
|
623
|
|
|
|
|
|
|
=head2 full_month_name |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Returns the full name of current month. Example: January |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=cut |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
my $self = shift; |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
return month_number_to_fullname($self->month); |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head2 http_expires_format |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
Returns datetime in this format: Fri, 27 Nov 2009 02:12:02 GMT |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=cut |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
my $self = shift; |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
return |
643
|
403
|
|
|
403
|
|
640
|
$self->day_as_string . ', ' |
644
|
|
|
|
|
|
|
. sprintf('%02d', $self->day_of_month) . ' ' |
645
|
403
|
|
|
|
|
7774
|
. $self->month_as_string . ' ' |
646
|
|
|
|
|
|
|
. $self->year . ' ' |
647
|
|
|
|
|
|
|
. $self->time_hhmmss . ' ' |
648
|
|
|
|
|
|
|
. $self->timezone; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=head2 date_ddmmyy |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
Returns date in this format "dd-mm-yy" (28-02-10) |
654
|
|
|
|
|
|
|
|
655
|
403
|
|
|
403
|
|
633
|
=cut |
656
|
|
|
|
|
|
|
|
657
|
403
|
|
|
|
|
7853
|
my $self = shift; |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
return join('-', (sprintf('%02d', $self->day_of_month), sprintf('%02d', $self->month), sprintf('%02d', $self->year_in_two_digit))); |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=head2 date_ddmmyyyy |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Returns date in this format "dd-mm-yyyy" (28-02-2010) |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=cut |
667
|
3
|
|
|
3
|
|
5
|
|
668
|
|
|
|
|
|
|
my $self = shift; |
669
|
3
|
|
|
|
|
61
|
|
670
|
|
|
|
|
|
|
return join('-', (sprintf('%02d', $self->day_of_month), sprintf('%02d', $self->month), $self->year)); |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=head2 date_yyyymmdd |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
Returns date in this format "yyyy-mm-dd" (2010-03-02) |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=cut |
678
|
|
|
|
|
|
|
|
679
|
3
|
|
|
3
|
|
7
|
my $self = shift; |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
return join('-', ($self->year, sprintf('%02d', $self->month), sprintf('%02d', $self->day_of_month))); |
682
|
3
|
|
|
|
|
92
|
} |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=head2 datetime_yyyymmdd_hhmmss |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
Returns: "yyyy-mm-dd hh:mm:ss" (2010-03-02 05:09:40) |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
=cut |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
my $self = shift; |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
return join(' ', ($self->date_yyyymmdd, $self->time_hhmmss)); |
693
|
|
|
|
|
|
|
} |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
my $self = shift; |
696
|
|
|
|
|
|
|
|
697
|
3
|
|
|
3
|
|
8
|
return $self->datetime_yyyymmdd_hhmmss; |
698
|
|
|
|
|
|
|
} |
699
|
3
|
|
|
|
|
67
|
|
700
|
|
|
|
|
|
|
=head2 datetime_iso8601 iso8601 |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
Since all internal representations are in UTC |
703
|
|
|
|
|
|
|
Returns "yyyy-mm-ddThh:mm:ssZ" (2010-02-02T05:09:40Z) |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=cut |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
my $self = shift; |
708
|
|
|
|
|
|
|
|
709
|
3
|
|
|
3
|
|
6
|
return $self->date_yyyymmdd . 'T' . $self->time_hhmmss . 'Z'; |
710
|
|
|
|
|
|
|
} |
711
|
3
|
|
|
|
|
64
|
|
712
|
|
|
|
|
|
|
my $self = shift; |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
return $self->datetime_iso8601; |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 datetime_yyyymmdd_hhmmss_TZ |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
Returns datetime in this format "yyyy-mm-dd hh:mm:ssGMT" (2010-03-02 05:09:40GMT) |
720
|
|
|
|
|
|
|
|
721
|
848
|
|
|
848
|
|
1154
|
=cut |
722
|
|
|
|
|
|
|
|
723
|
848
|
|
|
|
|
16059
|
my $self = shift; |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
return $self->datetime_yyyymmdd_hhmmss . $self->timezone; |
726
|
|
|
|
|
|
|
} |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=head2 days_in_month |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=cut |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
my ($self) = @_; |
733
|
9
|
|
|
9
|
|
15
|
|
734
|
|
|
|
|
|
|
my $month = $self->month; |
735
|
9
|
|
|
|
|
203
|
# 30 days hath September, April, June and November. |
736
|
|
|
|
|
|
|
my %shorties = ( |
737
|
|
|
|
|
|
|
9 => 30, |
738
|
|
|
|
|
|
|
4 => 30, |
739
|
3
|
|
|
3
|
|
6
|
6 => 30, |
740
|
|
|
|
|
|
|
11 => 30 |
741
|
3
|
|
|
|
|
69
|
); |
742
|
|
|
|
|
|
|
# All the rest have 31 |
743
|
|
|
|
|
|
|
my $last_day = $shorties{$month} || 31; |
744
|
|
|
|
|
|
|
# Except February. |
745
|
|
|
|
|
|
|
if ($month == 2) { |
746
|
|
|
|
|
|
|
my $year = $self->year; |
747
|
|
|
|
|
|
|
$last_day = (($year % 4 or not $year % 100) and ($year % 400)) ? 28 : 29; |
748
|
|
|
|
|
|
|
} |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
return $last_day; |
751
|
|
|
|
|
|
|
} |
752
|
7
|
|
|
7
|
|
15
|
|
753
|
|
|
|
|
|
|
=head2 timezone_offset |
754
|
7
|
|
|
|
|
152
|
|
755
|
|
|
|
|
|
|
Returns a TimeInterval which represents the difference between UTC and the time in certain timezone |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=cut |
758
|
3
|
|
|
3
|
|
6
|
|
759
|
|
|
|
|
|
|
=head2 is_dst_in_zone |
760
|
3
|
|
|
|
|
67
|
|
761
|
|
|
|
|
|
|
Returns a boolean which indicates whether a certain zone is in DST at the given epoch |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=cut |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
{ |
766
|
|
|
|
|
|
|
use DateTime; |
767
|
|
|
|
|
|
|
use DateTime::TimeZone; |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
my $bignum = 20000000; |
770
|
6
|
|
|
6
|
|
11
|
|
771
|
|
|
|
|
|
|
my %cache; |
772
|
6
|
|
|
|
|
138
|
my $cache_for = sub { |
773
|
|
|
|
|
|
|
my $tm = shift; |
774
|
|
|
|
|
|
|
my $tzname = shift; |
775
|
|
|
|
|
|
|
my $k = int $tm / $bignum; |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
if (my $val = $cache{"$k $tzname"}) { |
778
|
|
|
|
|
|
|
return $val; |
779
|
|
|
|
|
|
|
} |
780
|
22
|
|
|
22
|
|
44
|
|
781
|
|
|
|
|
|
|
my $z = DateTime::TimeZone->new(name => $tzname); |
782
|
22
|
|
|
|
|
433
|
my $start_of_interval = $k * $bignum; |
783
|
|
|
|
|
|
|
my $dt = DateTime->from_epoch(epoch => $start_of_interval); |
784
|
22
|
|
|
|
|
85
|
my $rdoff = $dt->utc_rd_as_seconds - $start_of_interval; |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
my ($span_start, $span_end, undef, undef, $off, $is_dst, $name) = @{$z->_span_for_datetime(utc => $dt)}; |
787
|
|
|
|
|
|
|
$_ -= $rdoff for ($span_start, $span_end); |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
my @val = ([$span_start, $span_end, $off, $is_dst, $name]); |
790
|
|
|
|
|
|
|
|
791
|
22
|
|
100
|
|
|
72
|
while ($span_end < ($k + 1) * $bignum) { |
792
|
|
|
|
|
|
|
$dt = DateTime->from_epoch(epoch => $span_end); |
793
|
22
|
100
|
|
|
|
47
|
|
794
|
5
|
|
|
|
|
100
|
($span_start, $span_end, undef, undef, $off, $is_dst, $name) = @{$z->_span_for_datetime(utc => $dt)}; |
795
|
5
|
100
|
66
|
|
|
34
|
$_ -= $rdoff for ($span_start, $span_end); |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
push @val, [$span_start, $span_end, $off, $is_dst, $name]; |
798
|
22
|
|
|
|
|
526
|
} |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
return $cache{"$k $tzname"} = \@val; |
801
|
|
|
|
|
|
|
}; |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
my ($self, $tzname) = @_; |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
if ($tzname eq 'UTC' or $tzname eq 'Z') { |
806
|
|
|
|
|
|
|
return Time::Duration::Concise::Localize->new(interval => DateTime::TimeZone::UTC->offset_for_datetime); |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
my $tm = $self->{epoch}; |
809
|
|
|
|
|
|
|
my $spans = $cache_for->($tm, $tzname); |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
for my $sp (@$spans) { |
812
|
|
|
|
|
|
|
if ($tm < $sp->[1]) { |
813
|
|
|
|
|
|
|
return Time::Duration::Concise::Localize->new(interval => $sp->[2]); |
814
|
8
|
|
|
8
|
|
31211
|
} |
|
8
|
|
|
|
|
3625047
|
|
|
8
|
|
|
|
|
422
|
|
815
|
8
|
|
|
8
|
|
72
|
} |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
15962
|
|
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
die "time $tm not found in span"; |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
my ($self, $tzname) = @_; |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
if ($tzname eq 'UTC' or $tzname eq 'Z') { |
823
|
|
|
|
|
|
|
return DateTime::TimeZone::UTC->is_dst_for_datetime; |
824
|
|
|
|
|
|
|
} |
825
|
|
|
|
|
|
|
my $tm = $self->{epoch}; |
826
|
|
|
|
|
|
|
my $spans = $cache_for->($tm, $tzname); |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
for my $sp (@$spans) { |
829
|
|
|
|
|
|
|
if ($tm < $sp->[1]) { |
830
|
|
|
|
|
|
|
return $sp->[3]; |
831
|
|
|
|
|
|
|
} |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
die "time $tm not found in span"; |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=head2 plus_time_interval |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Returns a new Date::Utility plus the supplied Time::Duration::Concise::Localize. Negative TimeIntervals will move backward. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
Will also attempt to create a TimeInterval from a supplied code, if possible. |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=cut |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
my ($self, $ti) = @_; |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
return $self->_move_time_interval($ti, 1); |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=head2 minus_time_interval |
852
|
48
|
|
|
48
|
1
|
25834
|
|
853
|
|
|
|
|
|
|
Returns a new Date::Utility minus the supplied Time::Duration::Concise::Localize. Negative TimeIntervals will move forward. |
854
|
48
|
50
|
33
|
|
|
226
|
|
855
|
0
|
|
|
|
|
0
|
Will also attempt to create a TimeInterval from a supplied code, if possible. |
856
|
|
|
|
|
|
|
|
857
|
48
|
|
|
|
|
89
|
=cut |
858
|
48
|
|
|
|
|
104
|
|
859
|
|
|
|
|
|
|
my ($self, $ti) = @_; |
860
|
48
|
|
|
|
|
85
|
|
861
|
86
|
100
|
|
|
|
146
|
return $self->_move_time_interval($ti, -1); |
862
|
48
|
|
|
|
|
234
|
} |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
my ($self, $ti, $dir) = @_; |
865
|
|
|
|
|
|
|
|
866
|
0
|
|
|
|
|
0
|
unless (ref($ti)) { |
867
|
|
|
|
|
|
|
if ($ti =~ s/([\d.]+)y//) { |
868
|
|
|
|
|
|
|
my $new_date = $self->_plus_years($dir * $1); |
869
|
|
|
|
|
|
|
return $ti ? $new_date->_move_time_interval($ti, $dir) : $new_date; |
870
|
200048
|
|
|
200048
|
1
|
402557
|
} |
871
|
|
|
|
|
|
|
if ($ti =~ s/([\d.]+)mo//i) { |
872
|
200048
|
50
|
33
|
|
|
629400
|
my $new_date = $self->_plus_months($dir * $1); |
873
|
0
|
|
|
|
|
0
|
return $ti ? $new_date->_move_time_interval($ti, $dir) : $new_date; |
874
|
|
|
|
|
|
|
} |
875
|
200048
|
|
|
|
|
301662
|
try { $ti = Time::Duration::Concise::Localize->new(interval => $ti) } |
876
|
200048
|
|
|
|
|
367839
|
catch ($e) { |
877
|
|
|
|
|
|
|
$ti //= 'undef'; |
878
|
200048
|
|
|
|
|
446642
|
confess "Couldn't create a TimeInterval from the code '$ti': $e"; |
879
|
280761
|
100
|
|
|
|
680893
|
} |
880
|
200048
|
|
|
|
|
642763
|
} |
881
|
|
|
|
|
|
|
my $sec = $ti->seconds; |
882
|
|
|
|
|
|
|
return ($sec == 0) ? $self : Date::Utility->new($self->{epoch} + $dir * $sec); |
883
|
|
|
|
|
|
|
} |
884
|
0
|
|
|
|
|
0
|
|
885
|
|
|
|
|
|
|
=head2 months_ahead |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
Returns the month ahead or backward from the supplied month in the format of Mmm-yy. |
888
|
|
|
|
|
|
|
It could hanlde backward or forward move from the supplied month. |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
=cut |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
my ($self, $months_ahead) = @_; |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
# Use 0-11 range to make the math easier. |
895
|
|
|
|
|
|
|
my $current_month = $self->month - 1; |
896
|
|
|
|
|
|
|
my $current_year = $self->year; |
897
|
416
|
|
|
416
|
1
|
280426
|
|
898
|
|
|
|
|
|
|
# take the current month number, add the offset, and shift back to 1-12 |
899
|
416
|
|
|
|
|
1673
|
my $new_month = ($current_month + $months_ahead) % 12 + 1; |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
# we need to know how many years to go forward |
902
|
|
|
|
|
|
|
my $years_ahead = POSIX::floor(($current_month + $months_ahead) / 12); |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
# use sprintf to add leading zero, and then shift into the range 0-99 |
905
|
|
|
|
|
|
|
my $new_year = sprintf '%02d', (($current_year + $years_ahead) % 100); |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
return month_number_to_abbrev($new_month) . '-' . $new_year; |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
=head2 move_to_nth_dow |
911
|
13
|
|
|
13
|
1
|
2909
|
|
912
|
|
|
|
|
|
|
Takes an integer as an ordinal and a day of week representation |
913
|
13
|
|
|
|
|
30
|
|
914
|
|
|
|
|
|
|
The following are all equivalent: |
915
|
|
|
|
|
|
|
C<move_to_nth_dow(3, 'Monday')> |
916
|
|
|
|
|
|
|
C<move_to_nth_dow(3, 'Mon')> |
917
|
429
|
|
|
429
|
|
1102
|
C<move_to_nth_dow(3, 1)> |
918
|
|
|
|
|
|
|
|
919
|
429
|
50
|
|
|
|
1318
|
Returning the 3rd Monday of the month represented by the object or |
920
|
429
|
100
|
|
|
|
1395
|
C<undef> if it does not exist. |
921
|
4
|
|
|
|
|
19
|
|
922
|
3
|
50
|
|
|
|
80
|
An exception is thrown on improper day of week representations. |
923
|
|
|
|
|
|
|
|
924
|
425
|
100
|
|
|
|
1153
|
=cut |
925
|
18
|
|
|
|
|
68
|
|
926
|
17
|
50
|
|
|
|
439
|
my ($self, $nth, $dow_abb) = @_; |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
$dow_abb //= 'undef'; # For nicer error reporting below. |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
my $dow = $days_to_num{lc $dow_abb} // croak 'Invalid day of week. We got [' . $dow_abb . ']'; |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
my $dow_first = (7 - ($self->day_of_month - 1 - $self->day_of_week)) % 7; |
933
|
407
|
|
|
|
|
1098
|
my $dom = ($dow + 7 - $dow_first) % 7 + ($nth - 1) * 7 + 1; |
934
|
407
|
|
|
|
|
16522
|
|
935
|
406
|
100
|
|
|
|
3494
|
## no critic (RequireCheckingReturnValueOfEval) |
936
|
|
|
|
|
|
|
return eval { Date::Utility->new(join '-', $self->year, $self->month, $dom) }; |
937
|
|
|
|
|
|
|
} |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
=head1 STATIC METHODS |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
=head2 month_number_to_abbrev |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
Static method returns a standard mapping from month numbers to our 3 |
944
|
|
|
|
|
|
|
character abbreviated format. |
945
|
|
|
|
|
|
|
|
946
|
17
|
|
|
17
|
1
|
845
|
=cut |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
my %number_abbrev_map = ( |
949
|
17
|
|
|
|
|
450
|
1 => 'Jan', |
950
|
17
|
|
|
|
|
322
|
2 => 'Feb', |
951
|
|
|
|
|
|
|
3 => 'Mar', |
952
|
|
|
|
|
|
|
4 => 'Apr', |
953
|
17
|
|
|
|
|
29
|
5 => 'May', |
954
|
|
|
|
|
|
|
6 => 'Jun', |
955
|
|
|
|
|
|
|
7 => 'Jul', |
956
|
17
|
|
|
|
|
61
|
8 => 'Aug', |
957
|
|
|
|
|
|
|
9 => 'Sep', |
958
|
|
|
|
|
|
|
10 => 'Oct', |
959
|
17
|
|
|
|
|
60
|
11 => 'Nov', |
960
|
|
|
|
|
|
|
12 => 'Dec', |
961
|
17
|
|
|
|
|
36
|
); |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
my %abbrev_number_map = reverse %number_abbrev_map; |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
# Deal with leading zeroes. |
967
|
|
|
|
|
|
|
my $which = int shift; |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
return $number_abbrev_map{$which}; |
970
|
|
|
|
|
|
|
} |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
=head2 month_abbrev_to_number |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
Static method returns a standard mapping from 3 |
975
|
|
|
|
|
|
|
character abbreviated format to month numbers |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=cut |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
# Deal with case issues |
981
|
8009
|
|
|
8009
|
1
|
21439
|
my $which = ucfirst lc shift; |
982
|
|
|
|
|
|
|
|
983
|
8009
|
|
50
|
|
|
14145
|
return $abbrev_number_map{$which}; |
984
|
|
|
|
|
|
|
} |
985
|
8009
|
|
66
|
|
|
19912
|
|
986
|
|
|
|
|
|
|
=head1 STATIC METHODS |
987
|
8007
|
|
|
|
|
164241
|
|
988
|
8007
|
|
|
|
|
15553
|
=head2 month_number_to_fullname |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
Static method returns a standard mapping from month numbers to fullname. |
991
|
8007
|
|
|
|
|
10675
|
|
|
8007
|
|
|
|
|
149281
|
|
992
|
|
|
|
|
|
|
=cut |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
my %number_fullname_map = ( |
995
|
|
|
|
|
|
|
1 => 'January', |
996
|
|
|
|
|
|
|
2 => 'February', |
997
|
|
|
|
|
|
|
3 => 'March', |
998
|
|
|
|
|
|
|
4 => 'April', |
999
|
|
|
|
|
|
|
5 => 'May', |
1000
|
|
|
|
|
|
|
6 => 'June', |
1001
|
|
|
|
|
|
|
7 => 'July', |
1002
|
|
|
|
|
|
|
8 => 'August', |
1003
|
|
|
|
|
|
|
9 => 'September', |
1004
|
|
|
|
|
|
|
10 => 'October', |
1005
|
|
|
|
|
|
|
11 => 'November', |
1006
|
|
|
|
|
|
|
12 => 'December', |
1007
|
|
|
|
|
|
|
); |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
return $number_fullname_map{int shift}; |
1011
|
|
|
|
|
|
|
} |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
=head2 is_epoch_timestamp |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
Check if a given datetime is an epoch timestemp, i.e. an integer of under 14 digits. |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
=cut |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
return (shift // '') =~ $EPOCH_RE; |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
=head2 is_ddmmmyy |
1023
|
426
|
|
|
426
|
1
|
836
|
|
1024
|
|
|
|
|
|
|
Check if a given "date" is in dd-Mmm-yy format (e.g. 1-Oct-10) |
1025
|
426
|
|
|
|
|
8519
|
|
1026
|
|
|
|
|
|
|
=cut |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
my $date = shift; |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
return (defined $date and $date =~ /^\d{1,2}\-\w{3}-\d{2}$/) ? 1 : undef; |
1031
|
|
|
|
|
|
|
} |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
=head2 truncate_to_day |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
Returns a Date::Utility object with the time part truncated out of it. |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
For instance, '2011-12-13 23:24:25' will return a new Date::Utility |
1038
|
53
|
|
|
53
|
1
|
157
|
object representing '2011-12-13 00:00:00' |
1039
|
|
|
|
|
|
|
|
1040
|
53
|
|
|
|
|
205
|
=cut |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
my ($self) = @_; |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
my $epoch = $self->{epoch}; |
1045
|
|
|
|
|
|
|
my $rem = $epoch % 86400; |
1046
|
|
|
|
|
|
|
return $self if $rem == 0; |
1047
|
|
|
|
|
|
|
return Date::Utility->new($epoch - $rem); |
1048
|
|
|
|
|
|
|
} |
1049
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
=head2 truncate_to_month |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
Returns a Date::Utility object with the day and time part truncated out of it. |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
For instance, '2011-12-13 23:24:25' will return a new Date::Utility |
1055
|
|
|
|
|
|
|
object representing '2011-12-01 00:00:00' |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
=cut |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
my ($self) = @_; |
1060
|
|
|
|
|
|
|
return Date::Utility->new(sprintf("%04d-%02d-01", $self->year, $self->month)); |
1061
|
|
|
|
|
|
|
} |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=head2 truncate_to_hour |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
Returns a Date::Utility object with the minutes and seconds truncated out of it. |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
For instance, '2011-12-13 23:24:25' will return a new Date::Utility |
1068
|
9
|
|
|
9
|
1
|
96
|
object representing '2011-12-13 23:00:00' |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
=cut |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
my ($self) = @_; |
1073
|
|
|
|
|
|
|
return Date::Utility->new(sprintf("%04d-%02d-%02d %02d:00:00", $self->year, $self->month, $self->day_of_month, $self->hour)); |
1074
|
|
|
|
|
|
|
} |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
=head2 today |
1077
|
|
|
|
|
|
|
|
1078
|
6
|
|
100
|
6
|
1
|
159
|
Returns Date::Utility object for the start of the current day. Much faster than |
1079
|
|
|
|
|
|
|
Date::Utility->new, as it will return the same object till the end of the day. |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
=cut |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
my ($today_obj, $today_ends_at, $today_starts_at); |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
my $time = time; |
1086
|
|
|
|
|
|
|
if (not $today_obj or $time > $today_ends_at or $time < $today_starts_at) { |
1087
|
|
|
|
|
|
|
# UNIX time assume that day is always 86400 seconds, |
1088
|
6
|
|
|
6
|
1
|
12
|
# that makes life easier |
1089
|
|
|
|
|
|
|
$time = 86400 * int($time / 86400); |
1090
|
6
|
100
|
100
|
|
|
55
|
$today_obj = Date::Utility->new($time); |
1091
|
|
|
|
|
|
|
$today_starts_at = $time; |
1092
|
|
|
|
|
|
|
$today_ends_at = $time + 86399; |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
return $today_obj; |
1095
|
|
|
|
|
|
|
} |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
=head2 plus_years |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
Takes the following argument as named parameter: |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
=over 4 |
1102
|
|
|
|
|
|
|
|
1103
|
5
|
|
|
5
|
1
|
838
|
=item * C<years> - number of years to be added. (Integer) |
1104
|
|
|
|
|
|
|
|
1105
|
5
|
|
|
|
|
8
|
=back |
1106
|
5
|
|
|
|
|
8
|
|
1107
|
5
|
50
|
|
|
|
11
|
Returns a new L<Date::Utility> object plus the given years. If the day is greater than days in the new month, it will take the day of end month. |
1108
|
5
|
|
|
|
|
11
|
e.g. |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
print Date::Utility->new('2000-02-29')->plus_years(1)->date_yyyymmdd; |
1111
|
|
|
|
|
|
|
# will print 2001-02-28 |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
=cut |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
my ($self, $years) = @_; |
1116
|
|
|
|
|
|
|
die "Need an integer years number" |
1117
|
|
|
|
|
|
|
unless looks_like_number($years) |
1118
|
|
|
|
|
|
|
and $years == int($years); |
1119
|
|
|
|
|
|
|
return $self->_create_trimmed_date($self->year + $years, $self->month, $self->day_of_month); |
1120
|
|
|
|
|
|
|
} |
1121
|
1
|
|
|
1
|
1
|
7
|
|
1122
|
1
|
|
|
|
|
757
|
*_plus_years = \&plus_years; |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
=head2 minus_years |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
Takes the following argument as named parameter: |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
=over 4 |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=item * C<years> - number of years to be subracted. (Integer) |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
=back |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
Returns a new L<Date::Utility> object minus the given years. If the day is greater than days in the new month, it will take the day of end month. |
1135
|
5
|
|
|
5
|
1
|
824
|
e.g. |
1136
|
5
|
|
|
|
|
127
|
|
1137
|
|
|
|
|
|
|
print Date::Utility->new('2000-02-29')->minus_years(1)->date_yyyymmdd; |
1138
|
|
|
|
|
|
|
# will print 1999-02-28 |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
=cut |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
my ($self, $years) = @_; |
1143
|
|
|
|
|
|
|
return $self->_plus_years(-$years); |
1144
|
|
|
|
|
|
|
} |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
*_minus_years = \&minus_years; |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
=head2 plus_months |
1149
|
6
|
|
|
6
|
1
|
1080
|
|
1150
|
6
|
100
|
100
|
|
|
51
|
Takes the following argument as named parameter: |
|
|
|
100
|
|
|
|
|
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
=over 4 |
1153
|
4
|
|
|
|
|
13
|
|
1154
|
4
|
|
|
|
|
11
|
=item * C<years> - number of months to be added. (Integer) |
1155
|
4
|
|
|
|
|
19
|
|
1156
|
4
|
|
|
|
|
7
|
=back |
1157
|
|
|
|
|
|
|
|
1158
|
6
|
|
|
|
|
84
|
Returns a new L<Date::Utility> object plus the given months. If the day is greater than days in the new month, it will take the day of end month. |
1159
|
|
|
|
|
|
|
e.g. |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
print Date::Utility->new('2000-01-31')->plus_months(1)->date_yyyymmdd; |
1162
|
|
|
|
|
|
|
# will print 2000-02-28 |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
=cut |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
my ($self, $months) = @_; |
1167
|
|
|
|
|
|
|
(looks_like_number($months) && $months == int($months)) || die "Need an integer months number"; |
1168
|
|
|
|
|
|
|
my $new_year = $self->year; |
1169
|
|
|
|
|
|
|
my $new_month = $self->month + $months; |
1170
|
|
|
|
|
|
|
if ($new_month < 1 || $new_month > 12) { |
1171
|
|
|
|
|
|
|
$new_year += floor($new_month / 12); |
1172
|
|
|
|
|
|
|
$new_month = $new_month % 12; |
1173
|
|
|
|
|
|
|
if ($new_month < 1) { # when date is 2011-01-01, and $months is -13, then here $new_month will be 0, so hanndle this case here. |
1174
|
|
|
|
|
|
|
$new_year--; |
1175
|
|
|
|
|
|
|
$new_month += 12; |
1176
|
|
|
|
|
|
|
} |
1177
|
|
|
|
|
|
|
} |
1178
|
|
|
|
|
|
|
my $new_day = $self->day_of_month; |
1179
|
|
|
|
|
|
|
return $self->_create_trimmed_date($new_year, $new_month, $new_day); |
1180
|
8
|
|
|
8
|
1
|
25
|
} |
1181
|
8
|
100
|
66
|
|
|
61
|
|
1182
|
|
|
|
|
|
|
*_plus_months = \&plus_months; |
1183
|
|
|
|
|
|
|
|
1184
|
7
|
|
|
|
|
172
|
=head2 minus_months |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
Takes the following argument as named parameter: |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
=over 4 |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
=item * C<years> - number of months to be subracted. (Integer) |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
=back |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
Returns a new L<Date::Utility> object minus the given months. If the day is greater than days in the new month, it will take the day of end month. |
1195
|
|
|
|
|
|
|
e.g. |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
print Date::Utility->new('2000-03-31')->minus_months(1)->date_yyyymmdd; |
1198
|
|
|
|
|
|
|
# will print 2000-02-28 |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
=cut |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
my ($self, $months) = @_; |
1203
|
|
|
|
|
|
|
return $self->_plus_months(-$months); |
1204
|
|
|
|
|
|
|
} |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
*_minus_months = \&minus_months; |
1207
|
|
|
|
|
|
|
|
1208
|
2
|
|
|
2
|
1
|
21
|
=head2 create_trimmed_date |
1209
|
2
|
|
|
|
|
6
|
|
1210
|
|
|
|
|
|
|
Takes the following argument as named parameter: |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
=over 4 |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
=item * C<year> - calendar year of the date (Integer) |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
=item * C<month> - calendar month of the date. (Integer) |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
=item * C<day> - day of the month of the date. (Integer) |
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
=back |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
Returns a valid L<Date::Utility> object whose date part is same with the given year, month and day and time part is not changed. If the day is greater than the max day in that month , then use that max day as the day in the new object. |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
=cut |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
my ($self, $year, $month, $day) = @_; |
1227
|
|
|
|
|
|
|
my $max_day = __PACKAGE__->new(sprintf("%04d-%02d-01", $year, $month))->days_in_month; |
1228
|
|
|
|
|
|
|
$day = $day < $max_day ? $day : $max_day; |
1229
|
|
|
|
|
|
|
my $date_string = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $month, $day, $self->hour, $self->minute, $self->second); |
1230
|
|
|
|
|
|
|
return __PACKAGE__->new($date_string); |
1231
|
|
|
|
|
|
|
} |
1232
|
|
|
|
|
|
|
|
1233
|
22
|
|
|
22
|
1
|
51
|
*_create_trimmed_date = \&create_trimmed_date; |
1234
|
22
|
100
|
66
|
|
|
102
|
|
1235
|
21
|
|
|
|
|
499
|
no Moose; |
1236
|
21
|
|
|
|
|
421
|
|
1237
|
21
|
100
|
100
|
|
|
75
|
__PACKAGE__->meta->make_immutable( |
1238
|
5
|
|
|
|
|
18
|
constructor_name => '_new', |
1239
|
5
|
|
|
|
|
7
|
replace_constructor => 1 |
1240
|
5
|
100
|
|
|
|
12
|
); |
1241
|
1
|
|
|
|
|
2
|
1; |
1242
|
1
|
|
|
|
|
2
|
|
1243
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
1244
|
|
|
|
|
|
|
|
1245
|
21
|
|
|
|
|
413
|
=over 4 |
1246
|
21
|
|
|
|
|
55
|
|
1247
|
|
|
|
|
|
|
=item L<Moose> |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
=item L<DateTime> |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
=item L<POSIX> |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
=item L<Scalar::Util> |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
=item L<Tie::Hash::LRU> |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
=item L<Time::Local> |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
=item L<Syntax::Keyword::Try> |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
=back |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
=head1 AUTHOR |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
Binary.com, C<< <support at binary.com> >> |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
=head1 BUGS |
1269
|
|
|
|
|
|
|
|
1270
|
2
|
|
|
2
|
1
|
14
|
Please report any bugs or feature requests to C<bug-date-utility at rt.cpan.org>, or through |
1271
|
2
|
|
|
|
|
7
|
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Date-Utility>. I will be notified, and then you'll |
1272
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
=head1 SUPPORT |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
perldoc Date::Utility |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
You can also look for information at: |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
=over 4 |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Date-Utility> |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
L<http://annocpan.org/dist/Date-Utility> |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
=item * CPAN Ratings |
1294
|
|
|
|
|
|
|
|
1295
|
30
|
|
|
30
|
1
|
63
|
L<http://cpanratings.perl.org/d/Date-Utility> |
1296
|
30
|
|
|
|
|
109
|
|
1297
|
30
|
100
|
|
|
|
62
|
=item * Search CPAN |
1298
|
30
|
|
|
|
|
598
|
|
1299
|
30
|
|
|
|
|
74
|
L<http://search.cpan.org/dist/Date-Utility/> |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
=back |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
1304
|
8
|
|
|
8
|
|
71
|
|
|
8
|
|
|
|
|
22
|
|
|
8
|
|
|
|
|
125
|
|
1305
|
|
|
|
|
|
|
Copyright 2015 Binary.com. |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
1308
|
|
|
|
|
|
|
under the terms of the the Artistic License (2.0). You may obtain a |
1309
|
|
|
|
|
|
|
copy of the full license at: |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
L<http://www.perlfoundation.org/artistic_license_2_0> |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
Any use, modification, and distribution of the Standard or Modified |
1314
|
|
|
|
|
|
|
Versions is governed by this Artistic License. By using, modifying or |
1315
|
|
|
|
|
|
|
distributing the Package, you accept this license. Do not use, modify, |
1316
|
|
|
|
|
|
|
or distribute the Package, if you do not accept this license. |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
If your Modified Version has been derived from a Modified Version made |
1319
|
|
|
|
|
|
|
by someone other than you, you are nevertheless required to ensure that |
1320
|
|
|
|
|
|
|
your Modified Version complies with the requirements of this license. |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
This license does not grant you the right to use any trademark, service |
1323
|
|
|
|
|
|
|
mark, tradename, or logo of the Copyright Holder. |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
This license includes the non-exclusive, worldwide, free-of-charge |
1326
|
|
|
|
|
|
|
patent license to make, have made, use, offer to sell, sell, import and |
1327
|
|
|
|
|
|
|
otherwise transfer the Package with respect to any patent claims |
1328
|
|
|
|
|
|
|
licensable by the Copyright Holder that are necessarily infringed by the |
1329
|
|
|
|
|
|
|
Package. If you institute patent litigation (including a cross-claim or |
1330
|
|
|
|
|
|
|
counterclaim) against any party alleging that the Package constitutes |
1331
|
|
|
|
|
|
|
direct or contributory patent infringement, then this Artistic License |
1332
|
|
|
|
|
|
|
to you shall terminate on the date that such litigation is filed. |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER |
1335
|
|
|
|
|
|
|
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. |
1336
|
|
|
|
|
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
1337
|
|
|
|
|
|
|
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY |
1338
|
|
|
|
|
|
|
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR |
1339
|
|
|
|
|
|
|
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR |
1340
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, |
1341
|
|
|
|
|
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
=cut |