line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DateTime::Format::Natural; |
2
|
|
|
|
|
|
|
|
3
|
26
|
|
|
26
|
|
2365987
|
use strict; |
|
26
|
|
|
|
|
215
|
|
|
26
|
|
|
|
|
801
|
|
4
|
26
|
|
|
26
|
|
146
|
use warnings; |
|
26
|
|
|
|
|
62
|
|
|
26
|
|
|
|
|
878
|
|
5
|
26
|
|
|
|
|
12231
|
use base qw( |
6
|
|
|
|
|
|
|
DateTime::Format::Natural::Calc |
7
|
|
|
|
|
|
|
DateTime::Format::Natural::Duration |
8
|
|
|
|
|
|
|
DateTime::Format::Natural::Expand |
9
|
|
|
|
|
|
|
DateTime::Format::Natural::Extract |
10
|
|
|
|
|
|
|
DateTime::Format::Natural::Formatted |
11
|
|
|
|
|
|
|
DateTime::Format::Natural::Helpers |
12
|
|
|
|
|
|
|
DateTime::Format::Natural::Rewrite |
13
|
26
|
|
|
26
|
|
180
|
); |
|
26
|
|
|
|
|
53
|
|
14
|
26
|
|
|
26
|
|
196
|
use boolean qw(true false); |
|
26
|
|
|
|
|
57
|
|
|
26
|
|
|
|
|
149
|
|
15
|
|
|
|
|
|
|
|
16
|
26
|
|
|
26
|
|
1765
|
use Carp qw(croak); |
|
26
|
|
|
|
|
64
|
|
|
26
|
|
|
|
|
1246
|
|
17
|
26
|
|
|
26
|
|
168
|
use DateTime (); |
|
26
|
|
|
|
|
52
|
|
|
26
|
|
|
|
|
394
|
|
18
|
26
|
|
|
26
|
|
12055
|
use DateTime::HiRes (); |
|
26
|
|
|
|
|
42315
|
|
|
26
|
|
|
|
|
583
|
|
19
|
26
|
|
|
26
|
|
188
|
use DateTime::TimeZone (); |
|
26
|
|
|
|
|
78
|
|
|
26
|
|
|
|
|
513
|
|
20
|
26
|
|
|
26
|
|
151
|
use List::MoreUtils qw(all any none); |
|
26
|
|
|
|
|
81
|
|
|
26
|
|
|
|
|
247
|
|
21
|
26
|
|
|
26
|
|
35245
|
use Params::Validate ':all'; |
|
26
|
|
|
|
|
73371
|
|
|
26
|
|
|
|
|
4580
|
|
22
|
26
|
|
|
26
|
|
253
|
use Scalar::Util qw(blessed); |
|
26
|
|
|
|
|
85
|
|
|
26
|
|
|
|
|
1366
|
|
23
|
26
|
|
|
26
|
|
176
|
use Storable qw(dclone); |
|
26
|
|
|
|
|
56
|
|
|
26
|
|
|
|
|
1314
|
|
24
|
|
|
|
|
|
|
|
25
|
26
|
|
|
26
|
|
171
|
use DateTime::Format::Natural::Utils qw(trim); |
|
26
|
|
|
|
|
62
|
|
|
26
|
|
|
|
|
148518
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $VERSION = '1.18'; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
validation_options( |
30
|
|
|
|
|
|
|
on_fail => sub |
31
|
|
|
|
|
|
|
{ |
32
|
|
|
|
|
|
|
my ($error) = @_; |
33
|
|
|
|
|
|
|
chomp $error; |
34
|
|
|
|
|
|
|
croak $error; |
35
|
|
|
|
|
|
|
}, |
36
|
|
|
|
|
|
|
stack_skip => 2, |
37
|
|
|
|
|
|
|
); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub new |
40
|
|
|
|
|
|
|
{ |
41
|
10582
|
|
|
10582
|
1
|
142676
|
my $class = shift; |
42
|
|
|
|
|
|
|
|
43
|
10582
|
|
33
|
|
|
50324
|
my $self = bless {}, ref($class) || $class; |
44
|
|
|
|
|
|
|
|
45
|
10582
|
|
|
|
|
42363
|
$self->_init_check(@_); |
46
|
10581
|
|
|
|
|
858936
|
$self->_init(@_); |
47
|
|
|
|
|
|
|
|
48
|
10581
|
|
|
|
|
27448
|
return $self; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub _init |
52
|
|
|
|
|
|
|
{ |
53
|
10581
|
|
|
10581
|
|
21944
|
my $self = shift; |
54
|
10581
|
|
|
|
|
28573
|
my %opts = @_; |
55
|
|
|
|
|
|
|
|
56
|
10581
|
|
|
|
|
30462
|
my %presets = ( |
57
|
|
|
|
|
|
|
lang => 'en', |
58
|
|
|
|
|
|
|
format => 'd/m/y', |
59
|
|
|
|
|
|
|
demand_future => false, |
60
|
|
|
|
|
|
|
prefer_future => false, |
61
|
|
|
|
|
|
|
time_zone => 'floating', |
62
|
|
|
|
|
|
|
); |
63
|
10581
|
|
|
|
|
87363
|
foreach my $opt (keys %presets) { |
64
|
52905
|
|
|
|
|
124142
|
$self->{ucfirst $opt} = $presets{$opt}; |
65
|
|
|
|
|
|
|
} |
66
|
10581
|
|
|
|
|
27737
|
foreach my $opt (keys %opts) { |
67
|
7004
|
50
|
|
|
|
17028
|
if (defined $opts{$opt}) { |
68
|
7004
|
|
|
|
|
15565
|
$self->{ucfirst $opt} = $opts{$opt}; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
10581
|
|
100
|
|
|
50259
|
$self->{Daytime} = $opts{daytime} || {}; |
72
|
|
|
|
|
|
|
|
73
|
10581
|
|
|
|
|
38117
|
my $mod = join '::', (__PACKAGE__, 'Lang', uc $self->{Lang}); |
74
|
10581
|
50
|
|
|
|
794665
|
eval "require $mod" or die $@; |
75
|
|
|
|
|
|
|
|
76
|
10581
|
|
|
|
|
62531
|
$self->{data} = $mod->__new(); |
77
|
10581
|
|
|
|
|
29613
|
$self->{grammar_class} = $mod; |
78
|
|
|
|
|
|
|
|
79
|
10581
|
|
|
|
|
43111
|
$self->{mode} = ''; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub _init_check |
83
|
|
|
|
|
|
|
{ |
84
|
10582
|
|
|
10582
|
|
19470
|
my $self = shift; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
validate(@_, { |
87
|
|
|
|
|
|
|
demand_future => { |
88
|
|
|
|
|
|
|
# SCALARREF due to boolean.pm's implementation |
89
|
|
|
|
|
|
|
type => BOOLEAN | SCALARREF, |
90
|
|
|
|
|
|
|
optional => true, |
91
|
|
|
|
|
|
|
callbacks => { |
92
|
|
|
|
|
|
|
'mutually exclusive' => sub |
93
|
|
|
|
|
|
|
{ |
94
|
1073
|
100
|
|
1073
|
|
69574
|
return true unless exists $_[1]->{prefer_future}; |
95
|
1
|
|
|
|
|
9
|
die "prefer_future provided\n"; |
96
|
|
|
|
|
|
|
}, |
97
|
|
|
|
|
|
|
}, |
98
|
|
|
|
|
|
|
}, |
99
|
|
|
|
|
|
|
lang => { |
100
|
|
|
|
|
|
|
type => SCALAR, |
101
|
|
|
|
|
|
|
optional => true, |
102
|
|
|
|
|
|
|
regex => qr!^(?:en)$!i, |
103
|
|
|
|
|
|
|
}, |
104
|
|
|
|
|
|
|
format => { |
105
|
|
|
|
|
|
|
type => SCALAR, |
106
|
|
|
|
|
|
|
optional => true, |
107
|
|
|
|
|
|
|
regex => qr!^(?: |
108
|
|
|
|
|
|
|
(?: (?: [dmy]{1,4}[-./] ){2}[dmy]{1,4} ) |
109
|
|
|
|
|
|
|
| |
110
|
|
|
|
|
|
|
(?: [dm]{1,2}/[dm]{1,2} ) |
111
|
|
|
|
|
|
|
)$!ix, |
112
|
|
|
|
|
|
|
}, |
113
|
|
|
|
|
|
|
prefer_future => { |
114
|
|
|
|
|
|
|
# SCALARREF due to boolean.pm's implementation |
115
|
|
|
|
|
|
|
type => BOOLEAN | SCALARREF, |
116
|
|
|
|
|
|
|
optional => true, |
117
|
|
|
|
|
|
|
callbacks => { |
118
|
|
|
|
|
|
|
'mutually exclusive' => sub |
119
|
|
|
|
|
|
|
{ |
120
|
1090
|
50
|
|
1090
|
|
66537
|
return true unless exists $_[1]->{demand_future}; |
121
|
0
|
|
|
|
|
0
|
die "demand_future provided\n"; |
122
|
|
|
|
|
|
|
}, |
123
|
|
|
|
|
|
|
}, |
124
|
|
|
|
|
|
|
}, |
125
|
|
|
|
|
|
|
time_zone => { |
126
|
|
|
|
|
|
|
type => SCALAR | OBJECT, |
127
|
|
|
|
|
|
|
optional => true, |
128
|
|
|
|
|
|
|
callbacks => { |
129
|
|
|
|
|
|
|
'valid timezone' => sub |
130
|
|
|
|
|
|
|
{ |
131
|
1562
|
|
|
1562
|
|
84052
|
my $val = shift; |
132
|
1562
|
100
|
|
|
|
5484
|
if (blessed($val)) { |
133
|
1
|
|
|
|
|
17
|
return $val->isa('DateTime::TimeZone'); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
else { |
136
|
1561
|
|
|
|
|
2528
|
eval { DateTime::TimeZone->new(name => $val) }; |
|
1561
|
|
|
|
|
6062
|
|
137
|
1561
|
|
|
|
|
124601
|
return !$@; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
}, |
141
|
|
|
|
|
|
|
}, |
142
|
|
|
|
|
|
|
daytime => { |
143
|
|
|
|
|
|
|
type => HASHREF, |
144
|
|
|
|
|
|
|
optional => true, |
145
|
|
|
|
|
|
|
callbacks => { |
146
|
|
|
|
|
|
|
'valid daytime' => sub |
147
|
|
|
|
|
|
|
{ |
148
|
39
|
|
|
39
|
|
2037
|
my $href = shift; |
149
|
39
|
|
|
|
|
89
|
my %daytimes = map { $_ => true } qw(morning afternoon evening); |
|
117
|
|
|
|
|
363
|
|
150
|
39
|
50
|
|
|
|
502
|
if (any { !$daytimes{$_} } keys %$href) { |
|
58
|
50
|
|
|
|
383
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
151
|
0
|
|
|
|
|
0
|
die "spelling of daytime\n"; |
152
|
|
|
|
|
|
|
} |
153
|
58
|
|
|
|
|
342
|
elsif (any { !defined $href->{$_} } keys %$href) { |
154
|
0
|
|
|
|
|
0
|
die "undefined hour\n"; |
155
|
|
|
|
|
|
|
} |
156
|
58
|
|
|
|
|
285
|
elsif (any { $href->{$_} !~ /^\d{1,2}$/ } keys %$href) { |
157
|
0
|
|
|
|
|
0
|
die "not a valid number\n"; |
158
|
|
|
|
|
|
|
} |
159
|
58
|
50
|
|
|
|
203
|
elsif (any { $href->{$_} < 0 || $href->{$_} > 23 } keys %$href) { |
160
|
0
|
|
|
|
|
0
|
die "hour out of range\n"; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
else { |
163
|
39
|
|
|
|
|
105
|
return true; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
}, |
167
|
|
|
|
|
|
|
}, |
168
|
|
|
|
|
|
|
datetime => { |
169
|
|
|
|
|
|
|
type => OBJECT, |
170
|
|
|
|
|
|
|
optional => true, |
171
|
|
|
|
|
|
|
callbacks => { |
172
|
|
|
|
|
|
|
'valid object' => sub |
173
|
|
|
|
|
|
|
{ |
174
|
26
|
|
|
26
|
|
1110
|
my $obj = shift; |
175
|
26
|
50
|
|
|
|
412
|
blessed($obj) && $obj->isa('DateTime'); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
}, |
178
|
|
|
|
|
|
|
}, |
179
|
10582
|
|
|
|
|
38821
|
}); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub _init_vars |
183
|
|
|
|
|
|
|
{ |
184
|
11703
|
|
|
11703
|
|
22091
|
my $self = shift; |
185
|
|
|
|
|
|
|
|
186
|
11703
|
|
|
|
|
34986
|
delete @$self{qw(keyword modified postprocess)}; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub parse_datetime |
190
|
|
|
|
|
|
|
{ |
191
|
11703
|
|
|
11703
|
1
|
61477
|
my $self = shift; |
192
|
|
|
|
|
|
|
|
193
|
11703
|
|
|
|
|
35196
|
$self->_parse_init(@_); |
194
|
|
|
|
|
|
|
|
195
|
11703
|
|
|
|
|
88990
|
$self->{input_string} = $self->{date_string}; |
196
|
|
|
|
|
|
|
|
197
|
11703
|
|
|
|
|
22442
|
$self->{mode} = 'parse'; |
198
|
|
|
|
|
|
|
|
199
|
11703
|
|
|
|
|
20029
|
my $date_string = $self->{date_string}; |
200
|
|
|
|
|
|
|
|
201
|
11703
|
|
|
|
|
46464
|
$self->_rewrite(\$date_string); |
202
|
|
|
|
|
|
|
|
203
|
11703
|
|
|
|
|
72529
|
my ($formatted) = $date_string =~ $self->{data}->__regexes('format'); |
204
|
11703
|
|
|
|
|
49422
|
my %count = $self->_count_separators($formatted); |
205
|
|
|
|
|
|
|
|
206
|
11703
|
|
|
|
|
30511
|
$self->{tokens} = []; |
207
|
11703
|
|
|
|
|
26930
|
$self->{traces} = []; |
208
|
|
|
|
|
|
|
|
209
|
11703
|
100
|
|
|
|
36314
|
if ($self->_check_formatted('ymd', \%count)) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
210
|
271
|
|
|
|
|
1083
|
my $dt = $self->_parse_formatted_ymd($date_string, \%count); |
211
|
271
|
100
|
|
|
|
1066
|
return $dt if blessed($dt); |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
elsif ($self->_check_formatted('md', \%count)) { |
214
|
193
|
|
|
|
|
709
|
my $dt = $self->_parse_formatted_md($date_string); |
215
|
193
|
100
|
|
|
|
749
|
return $dt if blessed($dt); |
216
|
|
|
|
|
|
|
|
217
|
192
|
100
|
100
|
|
|
543
|
if ($self->{Prefer_future} || $self->{Demand_future}) { |
218
|
36
|
|
|
|
|
497
|
$self->_advance_future('md'); |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
elsif ($date_string =~ /^(\d{4}(?:-\d{2}){0,2})T(\d{2}(?::\d{2}){0,2})$/) { |
222
|
9
|
|
|
|
|
37
|
my ($date, $time) = ($1, $2); |
223
|
|
|
|
|
|
|
|
224
|
9
|
|
|
|
|
16
|
my %args; |
225
|
|
|
|
|
|
|
|
226
|
9
|
|
|
|
|
37
|
@args{qw(year month day)} = split /-/, $date; |
227
|
9
|
|
100
|
|
|
51
|
$args{$_} ||= 01 foreach qw(month day); |
228
|
|
|
|
|
|
|
|
229
|
9
|
|
|
|
|
31
|
@args{qw(hour minute second)} = split /:/, $time; |
230
|
9
|
|
100
|
|
|
42
|
$args{$_} ||= 00 foreach qw(minute second); |
231
|
|
|
|
|
|
|
|
232
|
9
|
|
|
|
|
57
|
my $valid_date = $self->_check_date(map $args{$_}, qw(year month day)); |
233
|
9
|
|
|
|
|
46
|
my $valid_time = $self->_check_time(map $args{$_}, qw(hour minute second)); |
234
|
|
|
|
|
|
|
|
235
|
9
|
50
|
33
|
|
|
41
|
if (not $valid_date && $valid_time) { |
236
|
0
|
0
|
|
|
|
0
|
my $type = !$valid_date ? 'date' : 'time'; |
237
|
0
|
|
|
|
|
0
|
$self->_set_failure; |
238
|
0
|
|
|
|
|
0
|
$self->_set_error("(invalid $type)"); |
239
|
0
|
|
|
|
|
0
|
return $self->_get_datetime_object; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
9
|
|
|
|
|
46
|
$self->_set(%args); |
243
|
|
|
|
|
|
|
|
244
|
9
|
|
|
|
|
36
|
$self->{datetime}->truncate(to => 'second'); |
245
|
9
|
|
|
|
|
2299
|
$self->_set_truncated; |
246
|
9
|
|
|
|
|
36
|
$self->_set_valid_exp; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
elsif ($date_string =~ /^([+-]) (\d+?) ([a-zA-Z]+)$/x) { |
249
|
14
|
|
|
|
|
85
|
my ($prefix, $value, $unit) = ($1, $2, lc $3); |
250
|
|
|
|
|
|
|
|
251
|
14
|
|
|
|
|
51
|
my %methods = ( |
252
|
|
|
|
|
|
|
'+' => '_add', |
253
|
|
|
|
|
|
|
'-' => '_subtract', |
254
|
|
|
|
|
|
|
); |
255
|
14
|
|
|
|
|
28
|
my $method = $methods{$prefix}; |
256
|
|
|
|
|
|
|
|
257
|
14
|
100
|
|
64
|
|
49
|
if (none { $unit =~ /^${_}s?$/ } @{$self->{data}->__units('ordered')}) { |
|
64
|
|
|
|
|
707
|
|
|
14
|
|
|
|
|
98
|
|
258
|
2
|
|
|
|
|
8
|
$self->_set_failure; |
259
|
2
|
|
|
|
|
14
|
$self->_set_error("(invalid unit)"); |
260
|
2
|
|
|
|
|
6
|
return $self->_get_datetime_object; |
261
|
|
|
|
|
|
|
} |
262
|
12
|
|
|
|
|
96
|
$self->$method($unit => $value); |
263
|
|
|
|
|
|
|
|
264
|
12
|
|
|
|
|
38
|
$self->_set_valid_exp; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
elsif ($date_string =~ /^\d{14}$/) { |
267
|
6
|
|
|
|
|
13
|
my %args; |
268
|
6
|
|
|
|
|
42
|
@args{qw(year month day hour minute second)} = $date_string =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/; |
269
|
|
|
|
|
|
|
|
270
|
6
|
|
|
|
|
49
|
my $valid_date = $self->_check_date(map $args{$_}, qw(year month day)); |
271
|
6
|
|
|
|
|
42
|
my $valid_time = $self->_check_time(map $args{$_}, qw(hour minute second)); |
272
|
|
|
|
|
|
|
|
273
|
6
|
50
|
33
|
|
|
45
|
if (not $valid_date && $valid_time) { |
274
|
0
|
0
|
|
|
|
0
|
my $type = !$valid_date ? 'date' : 'time'; |
275
|
0
|
|
|
|
|
0
|
$self->_set_failure; |
276
|
0
|
|
|
|
|
0
|
$self->_set_error("(invalid $type)"); |
277
|
0
|
|
|
|
|
0
|
return $self->_get_datetime_object; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
6
|
|
|
|
|
45
|
$self->_set(%args); |
281
|
|
|
|
|
|
|
|
282
|
6
|
|
|
|
|
43
|
$self->{datetime}->truncate(to => 'second'); |
283
|
6
|
|
|
|
|
1557
|
$self->_set_truncated; |
284
|
6
|
|
|
|
|
31
|
$self->_set_valid_exp; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
else { |
287
|
11210
|
|
|
|
|
21459
|
@{$self->{tokens}} = split /\s+/, $date_string; |
|
11210
|
|
|
|
|
58684
|
|
288
|
11210
|
|
|
|
|
64634
|
$self->{data}->__init('tokens')->($self); |
289
|
11210
|
|
|
|
|
19801
|
$self->{count}{tokens} = @{$self->{tokens}}; |
|
11210
|
|
|
|
|
34799
|
|
290
|
|
|
|
|
|
|
|
291
|
11210
|
|
|
|
|
30462
|
$self->_process; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
11699
|
|
|
|
|
227413
|
my $trace = $self->_trace_string; |
295
|
11699
|
100
|
|
|
|
38501
|
if (defined $trace) { |
296
|
11339
|
|
|
|
|
19714
|
@{$self->{traces}} = $trace; |
|
11339
|
|
|
|
|
33643
|
|
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
11699
|
|
|
|
|
33319
|
return $self->_get_datetime_object; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub _params_init |
303
|
|
|
|
|
|
|
{ |
304
|
13170
|
|
|
13170
|
|
21401
|
my $self = shift; |
305
|
13170
|
|
|
|
|
21415
|
my $params = pop; |
306
|
|
|
|
|
|
|
|
307
|
13170
|
50
|
|
|
|
34756
|
if (@_ > 1) { |
308
|
0
|
|
|
|
|
0
|
validate(@_, { string => { type => SCALAR }}); |
309
|
0
|
|
|
|
|
0
|
my %opts = @_; |
310
|
0
|
|
|
|
|
0
|
foreach my $opt (keys %opts) { |
311
|
0
|
|
|
|
|
0
|
${$params->{$opt}} = $opts{$opt}; |
|
0
|
|
|
|
|
0
|
|
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
else { |
315
|
13170
|
|
|
|
|
174200
|
validate_pos(@_, { type => SCALAR }); |
316
|
13170
|
|
|
|
|
44978
|
(${$params->{string}}) = @_; |
|
13170
|
|
|
|
|
35303
|
|
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
13170
|
|
|
|
|
47446
|
trim($params->{string}); |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub _parse_init |
323
|
|
|
|
|
|
|
{ |
324
|
11703
|
|
|
11703
|
|
18714
|
my $self = shift; |
325
|
|
|
|
|
|
|
|
326
|
11703
|
|
|
|
|
49855
|
$self->_params_init(@_, { string => \$self->{date_string} }); |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
my $set_datetime = sub |
329
|
|
|
|
|
|
|
{ |
330
|
2907
|
|
|
2907
|
|
7076
|
my ($method, $args) = @_; |
331
|
|
|
|
|
|
|
|
332
|
2907
|
100
|
66
|
|
|
9637
|
if (exists $self->{Datetime} && $method eq 'now') { |
333
|
24
|
|
|
|
|
284
|
$self->{datetime} = dclone($self->{Datetime}); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
else { |
336
|
|
|
|
|
|
|
$self->{datetime} = DateTime::HiRes->$method( |
337
|
|
|
|
|
|
|
time_zone => $self->{Time_zone}, |
338
|
2883
|
|
|
|
|
14812
|
%$args, |
339
|
|
|
|
|
|
|
); |
340
|
|
|
|
|
|
|
} |
341
|
11703
|
|
|
|
|
58613
|
}; |
342
|
|
|
|
|
|
|
|
343
|
11703
|
100
|
|
|
|
39795
|
if ($self->{running_tests}) { |
344
|
8796
|
|
|
|
|
80923
|
$self->{datetime} = $self->{datetime_test}->clone; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
else { |
347
|
2907
|
|
|
|
|
7098
|
$set_datetime->('now', {}); |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
11703
|
|
|
|
|
1652555
|
$self->_init_vars; |
351
|
|
|
|
|
|
|
|
352
|
11703
|
|
|
|
|
33979
|
$self->_unset_failure; |
353
|
11703
|
|
|
|
|
55324
|
$self->_unset_error; |
354
|
11703
|
|
|
|
|
30077
|
$self->_unset_valid_exp; |
355
|
11703
|
|
|
|
|
59171
|
$self->_unset_trace; |
356
|
11703
|
|
|
|
|
26326
|
$self->_unset_truncated; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
sub parse_datetime_duration |
360
|
|
|
|
|
|
|
{ |
361
|
1258
|
|
|
1258
|
1
|
251915
|
my $self = shift; |
362
|
|
|
|
|
|
|
|
363
|
1258
|
|
|
|
|
2319
|
my $duration_string; |
364
|
1258
|
|
|
|
|
5769
|
$self->_params_init(@_, { string => \$duration_string }); |
365
|
1258
|
|
|
|
|
8972
|
my $timespan_sep = $self->{data}->__timespan('literal'); |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
my @date_strings = $duration_string =~ /\s+ $timespan_sep \s+/ix |
368
|
1039
|
|
|
|
|
3304
|
? do { $self->{duration} = true; |
369
|
1039
|
|
|
|
|
8412
|
split /\s+ $timespan_sep \s+/ix, $duration_string } |
370
|
1258
|
100
|
|
|
|
9310
|
: do { $self->{duration} = false; |
|
219
|
|
|
|
|
785
|
|
371
|
219
|
|
|
|
|
1183
|
($duration_string) }; |
372
|
|
|
|
|
|
|
|
373
|
1258
|
|
|
|
|
2596
|
my $max = 2; |
374
|
|
|
|
|
|
|
|
375
|
1258
|
|
|
|
|
3151
|
my $shrinked = false; |
376
|
1258
|
100
|
|
|
|
5329
|
if (@date_strings > $max) { |
377
|
1
|
|
|
|
|
3
|
my $offset = $max; |
378
|
1
|
|
|
|
|
4
|
splice (@date_strings, $offset); |
379
|
1
|
|
|
|
|
3
|
$shrinked = true; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
1258
|
|
|
|
|
5844
|
$self->_rewrite_duration(\@date_strings); |
383
|
|
|
|
|
|
|
|
384
|
1258
|
|
|
|
|
7021
|
$self->_pre_duration(\@date_strings); |
385
|
1258
|
|
|
|
|
17841
|
@$self{qw(state truncated_duration)} = ({}, []); |
386
|
|
|
|
|
|
|
|
387
|
1258
|
|
|
|
|
3305
|
my (@queue, @traces, @truncated); |
388
|
1258
|
|
|
|
|
2818
|
foreach my $date_string (@date_strings) { |
389
|
2297
|
|
|
|
|
6284
|
push @queue, $self->parse_datetime($date_string); |
390
|
2297
|
|
|
|
|
5843
|
$self->_save_state( |
391
|
|
|
|
|
|
|
valid_expression => $self->_get_valid_exp, |
392
|
|
|
|
|
|
|
failure => $self->_get_failure, |
393
|
|
|
|
|
|
|
error => $self->_get_error, |
394
|
|
|
|
|
|
|
); |
395
|
2297
|
100
|
|
|
|
18955
|
if (@{$self->{traces}}) { |
|
2297
|
|
|
|
|
5956
|
|
396
|
2292
|
|
|
|
|
5385
|
push @traces, $self->{traces}[0]; |
397
|
|
|
|
|
|
|
} |
398
|
2297
|
100
|
|
|
|
6423
|
if ($self->{running_tests}) { |
399
|
1932
|
|
|
|
|
13549
|
push @truncated, $self->_get_truncated; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
1258
|
|
|
|
|
6483
|
$self->_post_duration(\@queue, \@traces, \@truncated); |
404
|
1258
|
|
|
|
|
7049
|
$self->_restore_state; |
405
|
|
|
|
|
|
|
|
406
|
1258
|
|
|
|
|
5184
|
delete @$self{qw(duration insert state)}; |
407
|
|
|
|
|
|
|
|
408
|
1258
|
|
|
|
|
2334
|
@{$self->{traces}} = @traces; |
|
1258
|
|
|
|
|
3292
|
|
409
|
1258
|
|
|
|
|
2283
|
@{$self->{truncated_duration}} = @truncated; |
|
1258
|
|
|
|
|
2730
|
|
410
|
1258
|
|
|
|
|
2709
|
$self->{input_string} = $duration_string; |
411
|
|
|
|
|
|
|
|
412
|
1258
|
100
|
|
|
|
3093
|
if ($shrinked) { |
413
|
1
|
|
|
|
|
17
|
$self->_set_failure; |
414
|
1
|
|
|
|
|
10
|
$self->_set_error("(limit of $max duration substrings exceeded)"); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
1258
|
|
|
|
|
14064
|
return @queue; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub extract_datetime |
421
|
|
|
|
|
|
|
{ |
422
|
209
|
|
|
209
|
1
|
8401
|
my $self = shift; |
423
|
|
|
|
|
|
|
|
424
|
209
|
|
|
|
|
467
|
my $extract_string; |
425
|
209
|
|
|
|
|
1007
|
$self->_params_init(@_, { string => \$extract_string }); |
426
|
|
|
|
|
|
|
|
427
|
209
|
|
|
|
|
1182
|
$self->_unset_failure; |
428
|
209
|
|
|
|
|
1076
|
$self->_unset_error; |
429
|
209
|
|
|
|
|
664
|
$self->_unset_valid_exp; |
430
|
|
|
|
|
|
|
|
431
|
209
|
|
|
|
|
1105
|
$self->{input_string} = $extract_string; |
432
|
|
|
|
|
|
|
|
433
|
209
|
|
|
|
|
536
|
$self->{mode} = 'extract'; |
434
|
|
|
|
|
|
|
|
435
|
209
|
|
|
|
|
1084
|
my @expressions = $self->_extract_expressions($extract_string); |
436
|
|
|
|
|
|
|
|
437
|
209
|
100
|
|
|
|
2436
|
$self->_set_valid_exp if @expressions; |
438
|
|
|
|
|
|
|
|
439
|
209
|
100
|
|
|
|
1988
|
return wantarray ? @expressions : $expressions[0]; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub success |
443
|
|
|
|
|
|
|
{ |
444
|
10602
|
|
|
10602
|
1
|
66362
|
my $self = shift; |
445
|
|
|
|
|
|
|
|
446
|
10602
|
100
|
100
|
|
|
24538
|
return ($self->_get_valid_exp && !$self->_get_failure) ? true : false; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub error |
450
|
|
|
|
|
|
|
{ |
451
|
6
|
|
|
6
|
1
|
54
|
my $self = shift; |
452
|
|
|
|
|
|
|
|
453
|
6
|
100
|
|
|
|
16
|
return '' if $self->success; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
my $error = sub |
456
|
|
|
|
|
|
|
{ |
457
|
5
|
100
|
66
|
5
|
|
33
|
return undef unless defined $self->{mode} && length $self->{mode}; |
458
|
4
|
|
|
|
|
29
|
my %errors = ( |
459
|
|
|
|
|
|
|
extract => "'$self->{input_string}' cannot be extracted from", |
460
|
|
|
|
|
|
|
parse => "'$self->{input_string}' does not parse", |
461
|
|
|
|
|
|
|
); |
462
|
4
|
|
|
|
|
13
|
return $errors{$self->{mode}}; |
463
|
5
|
|
|
|
|
122
|
}->(); |
464
|
|
|
|
|
|
|
|
465
|
5
|
100
|
|
|
|
34
|
if (defined $error) { |
466
|
4
|
|
100
|
|
|
24
|
$error .= ' ' . ($self->_get_error || '(perhaps you have some garbage?)'); |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
else { |
469
|
1
|
|
|
|
|
3
|
$error = 'neither extracting nor parsing method invoked'; |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
5
|
|
|
|
|
40
|
return $error; |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
sub trace |
476
|
|
|
|
|
|
|
{ |
477
|
7
|
|
|
7
|
1
|
51
|
my $self = shift; |
478
|
|
|
|
|
|
|
|
479
|
7
|
100
|
|
|
|
12
|
return @{$self->{traces} || []}; |
|
7
|
|
|
|
|
43
|
|
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub _process |
483
|
|
|
|
|
|
|
{ |
484
|
11531
|
|
|
11531
|
|
19983
|
my $self = shift; |
485
|
|
|
|
|
|
|
|
486
|
11531
|
|
|
|
|
17835
|
my %opts; |
487
|
|
|
|
|
|
|
|
488
|
11531
|
100
|
|
|
|
29283
|
if (!exists $self->{lookup}) { |
489
|
10414
|
|
|
|
|
15801
|
foreach my $keyword (keys %{$self->{data}->__grammar('')}) { |
|
10414
|
|
|
|
|
48644
|
|
490
|
697738
|
|
|
|
|
5412088
|
my $count = scalar @{$self->{data}->__grammar($keyword)->[0]}; |
|
697738
|
|
|
|
|
2577118
|
|
491
|
697738
|
|
|
|
|
1103281
|
push @{$self->{lookup}{$count}}, [ $keyword, false ]; |
|
697738
|
|
|
|
|
1720674
|
|
492
|
697738
|
100
|
|
|
|
3013971
|
if ($self->_expand_for($keyword)) { |
493
|
197866
|
|
|
|
|
1762125
|
push @{$self->{lookup}{$count + 1}}, [ $keyword, true ]; |
|
197866
|
|
|
|
|
504798
|
|
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
11531
|
50
|
|
|
|
129672
|
PARSE: foreach my $lookup (@{$self->{lookup}{$self->{count}{tokens}} || []}) { |
|
11531
|
|
|
|
|
49887
|
|
499
|
100179
|
|
|
|
|
1222963
|
my ($keyword, $expandable) = @$lookup; |
500
|
|
|
|
|
|
|
|
501
|
100179
|
|
|
|
|
144969
|
my @grammar = @{$self->{data}->__grammar($keyword)}; |
|
100179
|
|
|
|
|
476344
|
|
502
|
100179
|
|
|
|
|
199556
|
my $types_entry = shift @grammar; |
503
|
|
|
|
|
|
|
|
504
|
100179
|
100
|
|
|
|
233545
|
@grammar = $self->_expand($keyword, $types_entry, \@grammar) if $expandable; |
505
|
|
|
|
|
|
|
|
506
|
100179
|
|
|
|
|
657629
|
foreach my $entry (@grammar) { |
507
|
559353
|
100
|
|
|
|
3692967
|
my ($types, $expression) = $expandable ? @$entry : ($types_entry, $entry); |
508
|
559353
|
|
|
|
|
3924543
|
my $valid_expression = true; |
509
|
559353
|
|
|
|
|
1662323
|
my $definition = $expression->[0]; |
510
|
559353
|
|
|
|
|
1612944
|
my @positions = sort {$a <=> $b} keys %$definition; |
|
1690493
|
|
|
|
|
3125825
|
|
511
|
559353
|
|
|
|
|
945254
|
my (%first_stack, %rest_stack); |
512
|
559353
|
|
|
|
|
901152
|
foreach my $pos (@positions) { |
513
|
667702
|
100
|
|
|
|
1480241
|
if ($types->[$pos] eq 'SCALAR') { |
|
|
50
|
|
|
|
|
|
514
|
70348
|
50
|
|
|
|
151021
|
if (defined $definition->{$pos}) { |
515
|
70348
|
100
|
|
|
|
95118
|
if (${$self->_token($pos)} =~ /^$definition->{$pos}$/i) { |
|
70348
|
|
|
|
|
132145
|
|
516
|
6387
|
|
|
|
|
16622
|
next; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
else { |
519
|
63961
|
|
|
|
|
152726
|
$valid_expression = false; |
520
|
63961
|
|
|
|
|
212970
|
last; |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
elsif ($types->[$pos] eq 'REGEXP') { |
525
|
597354
|
100
|
|
|
|
814184
|
if (my @captured = ${$self->_token($pos)} =~ $definition->{$pos}) { |
|
597354
|
|
|
|
|
1076874
|
|
526
|
113499
|
|
|
|
|
281988
|
$first_stack{$pos} = shift @captured; |
527
|
113499
|
|
|
|
|
209425
|
$rest_stack{$pos} = [ @captured ]; |
528
|
113499
|
|
|
|
|
250870
|
next; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
else { |
531
|
483855
|
|
|
|
|
1067084
|
$valid_expression = false; |
532
|
483855
|
|
|
|
|
1475077
|
last; |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
else { |
536
|
0
|
|
|
|
|
0
|
die "grammar error at keyword \"$keyword\" within $self->{grammar_class}: ", |
537
|
|
|
|
|
|
|
"unknown type $types->[$pos]\n"; |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
} |
540
|
559353
|
100
|
100
|
|
|
1260741
|
if ($valid_expression && @{$expression->[2]}) { |
|
11537
|
|
|
|
|
110176
|
|
541
|
8573
|
|
|
|
|
16046
|
my $i = 0; |
542
|
8573
|
|
|
|
|
14620
|
foreach my $check (@{$expression->[2]}) { |
|
8573
|
|
|
|
|
20426
|
|
543
|
10839
|
|
|
|
|
16880
|
my @pos = @{$expression->[1][$i++]}; |
|
10839
|
|
|
|
|
30410
|
|
544
|
10839
|
|
|
|
|
18911
|
my $error; |
545
|
10839
|
|
|
|
|
38365
|
$valid_expression &= $check->(\%first_stack, \%rest_stack, \@pos, \$error); |
546
|
10839
|
100
|
|
|
|
65009
|
unless ($valid_expression) { |
547
|
366
|
|
|
|
|
1792
|
$self->_set_error("($error)"); |
548
|
366
|
|
|
|
|
849
|
last; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
} |
552
|
559353
|
100
|
|
|
|
4013217
|
if ($valid_expression) { |
553
|
11171
|
|
|
|
|
48496
|
$self->_set_valid_exp; |
554
|
11171
|
100
|
|
|
|
39642
|
my @truncate_to = @{$expression->[6]->{truncate_to} || []}; |
|
11171
|
|
|
|
|
42540
|
|
555
|
11171
|
|
|
|
|
21371
|
my $i = 0; |
556
|
11171
|
|
|
|
|
17438
|
foreach my $positions (@{$expression->[3]}) { |
|
11171
|
|
|
|
|
24674
|
|
557
|
19578
|
|
|
|
|
33436
|
my ($c, @values); |
558
|
19578
|
|
|
|
|
38484
|
foreach my $pos (@$positions) { |
559
|
24633
|
100
|
|
|
|
81656
|
my $index = ref $pos eq 'HASH' ? (keys %$pos)[0] : $pos; |
560
|
|
|
|
|
|
|
$values[$c++] = ref $pos |
561
|
|
|
|
|
|
|
? $index eq 'VALUE' |
562
|
|
|
|
|
|
|
? $pos->{$index} |
563
|
|
|
|
|
|
|
: $self->SUPER::_helper($pos->{$index}, $first_stack{$index}) |
564
|
|
|
|
|
|
|
: exists $first_stack{$index} |
565
|
|
|
|
|
|
|
? $first_stack{$index} |
566
|
24633
|
100
|
|
|
|
118367
|
: ${$self->_token($index)}; |
|
0
|
50
|
|
|
|
0
|
|
|
|
100
|
|
|
|
|
|
567
|
|
|
|
|
|
|
} |
568
|
19578
|
|
|
|
|
51179
|
my $worker = "SUPER::$expression->[5]->[$i]"; |
569
|
19578
|
|
|
|
|
94659
|
$self->$worker(@values, $expression->[4]->[$i++]); |
570
|
19578
|
|
|
|
|
86205
|
$self->_truncate(shift @truncate_to); |
571
|
|
|
|
|
|
|
} |
572
|
11171
|
|
|
|
|
22072
|
%opts = %{$expression->[6]}; |
|
11171
|
|
|
|
|
37019
|
|
573
|
11171
|
|
|
|
|
27759
|
$self->{keyword} = $keyword; |
574
|
11171
|
|
|
|
|
99521
|
last PARSE; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
11531
|
|
|
|
|
41469
|
$self->_post_process(%opts); |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
sub _truncate |
583
|
|
|
|
|
|
|
{ |
584
|
19578
|
|
|
19578
|
|
34534
|
my $self = shift; |
585
|
19578
|
|
|
|
|
39721
|
my ($truncate_to) = @_; |
586
|
|
|
|
|
|
|
|
587
|
19578
|
100
|
|
|
|
61720
|
return unless defined $truncate_to; |
588
|
|
|
|
|
|
|
|
589
|
11813
|
100
|
|
|
|
24119
|
my @truncate_to = map { $_ =~ /_/ ? split /_/, $_ : $_ } $truncate_to; |
|
11813
|
|
|
|
|
70727
|
|
590
|
11813
|
|
|
|
|
24269
|
my $i = 0; |
591
|
11813
|
|
|
|
|
19123
|
my @units = @{$self->{data}->__units('ordered')}; |
|
11813
|
|
|
|
|
75937
|
|
592
|
11813
|
|
|
|
|
28686
|
my %indexes = map { $_ => $i++ } @units; |
|
94504
|
|
|
|
|
179770
|
|
593
|
11813
|
|
|
|
|
30878
|
foreach my $unit (@truncate_to) { |
594
|
20487
|
|
|
|
|
39018
|
my $index = $indexes{$unit} - 1; |
595
|
20487
|
100
|
66
|
|
|
87726
|
if (defined $units[$index] && !exists $self->{modified}{$units[$index]}) { |
596
|
11759
|
|
|
|
|
46170
|
$self->{datetime}->truncate(to => $unit); |
597
|
11759
|
|
|
|
|
3421144
|
$self->_set_truncated; |
598
|
11759
|
|
|
|
|
87029
|
last; |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
sub _post_process |
604
|
|
|
|
|
|
|
{ |
605
|
11531
|
|
|
11531
|
|
20079
|
my $self = shift; |
606
|
11531
|
|
|
|
|
25806
|
my %opts = @_; |
607
|
|
|
|
|
|
|
|
608
|
11531
|
|
|
|
|
23246
|
delete $opts{truncate_to}; |
609
|
|
|
|
|
|
|
|
610
|
11531
|
50
|
100
|
|
|
39954
|
if (($self->{Prefer_future} || $self->{Demand_future}) |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
611
|
|
|
|
|
|
|
&& (exists $opts{advance_future} && $opts{advance_future}) |
612
|
|
|
|
|
|
|
) { |
613
|
2070
|
|
|
|
|
41369
|
$self->_advance_future; |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub _advance_future |
618
|
|
|
|
|
|
|
{ |
619
|
2106
|
|
|
2106
|
|
3852
|
my $self = shift; |
620
|
2106
|
|
|
|
|
4580
|
my %advance = map { $_ => true } @_; |
|
36
|
|
|
|
|
80
|
|
621
|
|
|
|
|
|
|
|
622
|
2106
|
|
|
|
|
3462
|
my %modified = map { $_ => true } keys %{$self->{modified}}; |
|
6156
|
|
|
|
|
19240
|
|
|
2106
|
|
|
|
|
6969
|
|
623
|
|
|
|
|
|
|
my $token_contains = sub |
624
|
|
|
|
|
|
|
{ |
625
|
2520
|
|
|
2520
|
|
40010
|
my ($identifier) = @_; |
626
|
|
|
|
|
|
|
return any { |
627
|
27066
|
|
|
|
|
66076
|
my $data = $_; |
628
|
|
|
|
|
|
|
any { |
629
|
43158
|
|
|
|
|
69852
|
my $token = $_; |
630
|
43158
|
|
|
|
|
233575
|
$token =~ /^$data$/i; |
631
|
27066
|
|
|
|
|
65879
|
} @{$self->{tokens}} |
|
27066
|
|
|
|
|
60178
|
|
632
|
2520
|
|
|
|
|
9770
|
} @{$self->{data}->{$identifier}}; |
|
2520
|
|
|
|
|
10713
|
|
633
|
2106
|
|
|
|
|
16534
|
}; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
my $now = exists $self->{Datetime} |
636
|
|
|
|
|
|
|
? dclone($self->{Datetime}) |
637
|
2106
|
100
|
|
|
|
10396
|
: DateTime::HiRes->now(time_zone => $self->{Time_zone}); |
638
|
|
|
|
|
|
|
|
639
|
2106
|
|
|
1530
|
|
1043385
|
my $day_of_week = sub { $_[0]->_Day_of_Week(map $_[0]->{datetime}->$_, qw(year month day)) }; |
|
1530
|
|
|
|
|
51451
|
|
640
|
|
|
|
|
|
|
|
641
|
2106
|
|
|
|
|
6908
|
my $skip_weekdays = false; |
642
|
|
|
|
|
|
|
|
643
|
2106
|
50
|
33
|
4366
|
|
16072
|
if ((all { /^(?:(?:nano)?second|minute|hour)$/ } keys %modified) |
|
4366
|
100
|
66
|
|
|
25581
|
|
|
|
100
|
100
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
644
|
|
|
|
|
|
|
&& (exists $self->{modified}{hour} && $self->{modified}{hour} == 1) |
645
|
|
|
|
|
|
|
&& (($self->{Prefer_future} && $self->{datetime} < $now) |
646
|
|
|
|
|
|
|
|| ($self->{Demand_future} && $self->{datetime} <= $now)) |
647
|
|
|
|
|
|
|
) { |
648
|
234
|
|
|
|
|
26185
|
$self->{postprocess}{day} = 1; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
elsif (sub { |
651
|
1872
|
100
|
|
1872
|
|
26292
|
return false unless @{$self->{tokens}} == 2; |
|
1872
|
|
|
|
|
6788
|
|
652
|
1398
|
|
|
|
|
10325
|
my ($day, $weekday) = map $self->{data}->__RE($_), qw(day weekday); |
653
|
1398
|
100
|
100
|
|
|
10081
|
if ($self->{tokens}->[0] =~ $day |
654
|
|
|
|
|
|
|
&& $self->{tokens}->[1] =~ $weekday) { |
655
|
36
|
|
|
|
|
156
|
$skip_weekdays = true; |
656
|
36
|
|
|
|
|
141
|
return true; |
657
|
|
|
|
|
|
|
} |
658
|
1362
|
|
|
|
|
3711
|
return false; |
659
|
|
|
|
|
|
|
}->() |
660
|
108
|
|
|
108
|
|
946
|
&& (all { /^(?:day|month|year)$/ } keys %modified) |
661
|
|
|
|
|
|
|
&& (($self->{Prefer_future} && $self->{datetime}->day < $now->day) |
662
|
|
|
|
|
|
|
|| ($self->{Demand_future} && $self->{datetime}->day <= $now->day)) |
663
|
|
|
|
|
|
|
) { |
664
|
18
|
|
|
|
|
648
|
$self->{postprocess}{week} = 4; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
elsif (($token_contains->('weekdays_all') && !$skip_weekdays) |
667
|
|
|
|
|
|
|
&& (exists $self->{modified}{day} && $self->{modified}{day} == 1) |
668
|
|
|
|
|
|
|
&& (($self->{Prefer_future} && $day_of_week->($self) < $now->wday) |
669
|
|
|
|
|
|
|
|| ($self->{Demand_future} && $day_of_week->($self) <= $now->wday)) |
670
|
|
|
|
|
|
|
) { |
671
|
1188
|
|
|
|
|
18598
|
$self->{postprocess}{day} = 7; |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
elsif (($token_contains->('months_all') || $advance{md}) |
674
|
156
|
|
|
156
|
|
1943
|
&& (all { /^(?:day|month)$/ } keys %modified) |
675
|
|
|
|
|
|
|
&& (exists $self->{modified}{month} && $self->{modified}{month} == 1) |
676
|
|
|
|
|
|
|
&& (exists $self->{modified}{day} |
677
|
|
|
|
|
|
|
? $self->{modified}{day} == 1 |
678
|
|
|
|
|
|
|
? true : false |
679
|
|
|
|
|
|
|
: true) |
680
|
|
|
|
|
|
|
&& (($self->{Prefer_future} && $self->{datetime}->day_of_year < $now->day_of_year) |
681
|
|
|
|
|
|
|
|| ($self->{Demand_future} && $self->{datetime}->day_of_year <= $now->day_of_year)) |
682
|
|
|
|
|
|
|
) { |
683
|
72
|
|
|
|
|
3154
|
$self->{postprocess}{year} = 1; |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
sub _token |
688
|
|
|
|
|
|
|
{ |
689
|
667702
|
|
|
667702
|
|
962023
|
my $self = shift; |
690
|
667702
|
|
|
|
|
1080627
|
my ($pos) = @_; |
691
|
|
|
|
|
|
|
|
692
|
667702
|
|
|
|
|
941405
|
my $str = ''; |
693
|
667702
|
|
|
|
|
1310994
|
my $token = $self->{tokens}->[0 + $pos]; |
694
|
|
|
|
|
|
|
|
695
|
667702
|
50
|
|
|
|
3545706
|
return defined $token |
696
|
|
|
|
|
|
|
? \$token |
697
|
|
|
|
|
|
|
: \$str; |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
19578
|
|
|
19578
|
|
30451
|
sub _register_trace { push @{$_[0]->{trace}}, (caller(1))[3] } |
|
19578
|
|
|
|
|
159329
|
|
701
|
11703
|
|
|
11703
|
|
18694
|
sub _unset_trace { @{$_[0]->{trace}} = () } |
|
11703
|
|
|
|
|
31756
|
|
702
|
|
|
|
|
|
|
|
703
|
2652
|
|
|
2652
|
|
17909
|
sub _get_error { $_[0]->{error} } |
704
|
372
|
|
|
372
|
|
1018
|
sub _set_error { $_[0]->{error} = $_[1] } |
705
|
11914
|
|
|
11914
|
|
24299
|
sub _unset_error { $_[0]->{error} = undef } |
706
|
|
|
|
|
|
|
|
707
|
12538
|
|
|
12538
|
|
108447
|
sub _get_failure { $_[0]->{failure} } |
708
|
6
|
|
|
6
|
|
32
|
sub _set_failure { $_[0]->{failure} = true } |
709
|
11914
|
|
|
11914
|
|
29365
|
sub _unset_failure { $_[0]->{failure} = false } |
710
|
|
|
|
|
|
|
|
711
|
12899
|
|
|
12899
|
|
45741
|
sub _get_valid_exp { $_[0]->{valid_expression} } |
712
|
11868
|
|
|
11868
|
|
27345
|
sub _set_valid_exp { $_[0]->{valid_expression} = true } |
713
|
12236
|
|
|
12236
|
|
24377
|
sub _unset_valid_exp { $_[0]->{valid_expression} = false } |
714
|
|
|
|
|
|
|
|
715
|
10779
|
|
|
10779
|
|
186792
|
sub _get_truncated { $_[0]->{truncated} } |
716
|
12236
|
|
|
12236
|
|
36810
|
sub _set_truncated { $_[0]->{truncated} = true } |
717
|
12024
|
|
|
12024
|
|
25120
|
sub _unset_truncated { $_[0]->{truncated} = false } |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
sub _get_datetime_object |
720
|
|
|
|
|
|
|
{ |
721
|
11703
|
|
|
11703
|
|
19774
|
my $self = shift; |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
my $dt = DateTime->new( |
724
|
|
|
|
|
|
|
time_zone => $self->{datetime}->time_zone, |
725
|
|
|
|
|
|
|
year => $self->{datetime}->year, |
726
|
|
|
|
|
|
|
month => $self->{datetime}->month, |
727
|
|
|
|
|
|
|
day => $self->{datetime}->day_of_month, |
728
|
|
|
|
|
|
|
hour => $self->{datetime}->hour, |
729
|
|
|
|
|
|
|
minute => $self->{datetime}->minute, |
730
|
|
|
|
|
|
|
second => $self->{datetime}->second, |
731
|
|
|
|
|
|
|
nanosecond => $self->{datetime}->nanosecond, |
732
|
11703
|
|
|
|
|
38045
|
); |
733
|
|
|
|
|
|
|
|
734
|
11703
|
|
|
|
|
3694180
|
foreach my $unit (keys %{$self->{postprocess}}) { |
|
11703
|
|
|
|
|
45633
|
|
735
|
1512
|
|
|
|
|
6983
|
$dt->add("${unit}s" => $self->{postprocess}{$unit}); |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
11703
|
|
|
|
|
1556361
|
return $dt; |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
# solely for testing purpose |
742
|
|
|
|
|
|
|
sub _set_datetime |
743
|
|
|
|
|
|
|
{ |
744
|
7806
|
|
|
7806
|
|
34156
|
my $self = shift; |
745
|
7806
|
|
|
|
|
18234
|
my ($time, $tz) = @_; |
746
|
|
|
|
|
|
|
|
747
|
7806
|
|
100
|
|
|
55200
|
$self->{datetime_test} = DateTime->new( |
748
|
|
|
|
|
|
|
time_zone => $tz || 'floating', |
749
|
|
|
|
|
|
|
%$time, |
750
|
|
|
|
|
|
|
); |
751
|
7806
|
|
|
|
|
2934778
|
$self->{running_tests} = true; |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
1; |
755
|
|
|
|
|
|
|
__END__ |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=encoding ISO8859-1 |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
=head1 NAME |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
DateTime::Format::Natural - Parse informal natural language date/time strings |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=head1 SYNOPSIS |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
use DateTime::Format::Natural; |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
$parser = DateTime::Format::Natural->new; |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
$dt = $parser->parse_datetime($date_string); |
770
|
|
|
|
|
|
|
@dt = $parser->parse_datetime_duration($date_string); |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
$date_string = $parser->extract_datetime($extract_string); |
773
|
|
|
|
|
|
|
@date_strings = $parser->extract_datetime($extract_string); |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
if ($parser->success) { |
776
|
|
|
|
|
|
|
# operate on $dt/@dt, for example: |
777
|
|
|
|
|
|
|
print $dt->strftime('%d.%m.%Y %H:%M:%S'), "\n"; |
778
|
|
|
|
|
|
|
} else { |
779
|
|
|
|
|
|
|
warn $parser->error; |
780
|
|
|
|
|
|
|
} |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
@traces = $parser->trace; |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# examples |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
12:14 PM |
787
|
|
|
|
|
|
|
next tuesday at 2am |
788
|
|
|
|
|
|
|
tomorrow morning |
789
|
|
|
|
|
|
|
4pm yesterday |
790
|
|
|
|
|
|
|
10 weeks ago |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
1st tuesday last november |
793
|
|
|
|
|
|
|
2nd friday in august |
794
|
|
|
|
|
|
|
final thursday in april |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
for 3 hours |
797
|
|
|
|
|
|
|
monday to friday |
798
|
|
|
|
|
|
|
1 April 10 am to 1 May 8am |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
jan 24, 2011 12:00 |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
=head1 DESCRIPTION |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
C<DateTime::Format::Natural> parses informal natural language date/time strings. |
805
|
|
|
|
|
|
|
In addition, parsable date/time substrings may be extracted from ordinary strings. |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
=head2 new |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
Creates a new C<DateTime::Format::Natural> object. Arguments to C<new()> are options and |
812
|
|
|
|
|
|
|
not necessarily required. |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
$parser = DateTime::Format::Natural->new( |
815
|
|
|
|
|
|
|
datetime => DateTime->new(...), |
816
|
|
|
|
|
|
|
lang => 'en', |
817
|
|
|
|
|
|
|
format => 'mm/dd/yy', |
818
|
|
|
|
|
|
|
prefer_future => [0|1], |
819
|
|
|
|
|
|
|
demand_future => [0|1], |
820
|
|
|
|
|
|
|
time_zone => 'floating', |
821
|
|
|
|
|
|
|
daytime => { morning => 06, |
822
|
|
|
|
|
|
|
afternoon => 13, |
823
|
|
|
|
|
|
|
evening => 20, |
824
|
|
|
|
|
|
|
}, |
825
|
|
|
|
|
|
|
); |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=over 4 |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
=item * C<datetime> |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Overrides the present now with a L<DateTime> object provided. |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=item * C<lang> |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
Contains the language selected, currently limited to C<en> (english). |
836
|
|
|
|
|
|
|
Defaults to 'C<en>'. |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=item * C<format> |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Specifies the format of numeric dates. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
The format is used to influence how numeric dates are parsed. Given two |
843
|
|
|
|
|
|
|
numbers separated by a slash, the month/day order expected comes from |
844
|
|
|
|
|
|
|
this option. If there is a third number, this option describes where |
845
|
|
|
|
|
|
|
to expect the year. When this format can't be used to interpret the |
846
|
|
|
|
|
|
|
date, some unambiguous dates may be parsed, but there is no form |
847
|
|
|
|
|
|
|
guarantee. |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
Current supported "month/day" formats: C<dd/mm>, C<mm/dd>. |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
Current supported "year/month/day" formats (with slashes): C<dd/mm/yy>, |
852
|
|
|
|
|
|
|
C<dd/mm/yyyy>, C<mm/dd/yyyy>, C<yyyy/mm/dd>. |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
Note that all of the above formats with three units do also parse |
855
|
|
|
|
|
|
|
with dots or dashes as format separators. |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
Furthermore, formats can be abbreviated as long as they remain |
858
|
|
|
|
|
|
|
unambiguous. |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
Defaults to 'C<d/m/y>'. |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=item * C<prefer_future> |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
Prefers future time and dates. Accepts a boolean, defaults to false. |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=item * C<demand_future> |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
Demands future time and dates. Similar to C<prefer_future>, but stronger. |
869
|
|
|
|
|
|
|
Accepts a boolean, defaults to false. |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=item * C<time_zone> |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
The time zone to use when parsing and for output. Accepts any time zone |
874
|
|
|
|
|
|
|
recognized by L<DateTime>. Defaults to 'floating'. |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
=item * C<daytime> |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
A hash reference consisting of customized daytime hours, |
879
|
|
|
|
|
|
|
which may be selectively changed. |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
=back |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
=head1 METHODS |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
=head2 parse_datetime |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
Returns a L<DateTime> object constructed from a natural language date/time string. |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
$dt = $parser->parse_datetime($date_string); |
890
|
|
|
|
|
|
|
$dt = $parser->parse_datetime(string => $date_string); |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
=over 4 |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
=item * C<string> |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
The date string. |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
=back |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=head2 parse_datetime_duration |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
Returns one or two L<DateTime> objects constructed from a natural language |
903
|
|
|
|
|
|
|
date/time string which may contain timespans/durations. I<Same> interface |
904
|
|
|
|
|
|
|
and options as C<parse_datetime()>, but should be explicitly called in |
905
|
|
|
|
|
|
|
list context. |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
@dt = $parser->parse_datetime_duration($date_string); |
908
|
|
|
|
|
|
|
@dt = $parser->parse_datetime_duration(string => $date_string); |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
=head2 extract_datetime |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
Returns parsable date/time substrings (also known as expressions) extracted |
913
|
|
|
|
|
|
|
from the string provided; in scalar context only the first parsable substring |
914
|
|
|
|
|
|
|
is returned, whereas in list context all parsable substrings are returned. |
915
|
|
|
|
|
|
|
Each extracted substring can then be passed to the C<parse_datetime()>/ |
916
|
|
|
|
|
|
|
C<parse_datetime_duration()> methods. |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
$date_string = $parser->extract_datetime($extract_string); |
919
|
|
|
|
|
|
|
@date_strings = $parser->extract_datetime($extract_string); |
920
|
|
|
|
|
|
|
# or |
921
|
|
|
|
|
|
|
$date_string = $parser->extract_datetime(string => $extract_string); |
922
|
|
|
|
|
|
|
@date_strings = $parser->extract_datetime(string => $extract_string); |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
=head2 success |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
Returns a boolean indicating success or failure for parsing the date/time |
927
|
|
|
|
|
|
|
string given. |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
=head2 error |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
Returns the error message if the parsing did not succeed. |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
=head2 trace |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
Returns one or two strings with the grammar keyword for the valid |
936
|
|
|
|
|
|
|
expression parsed, traces of methods which were called within the Calc |
937
|
|
|
|
|
|
|
class and a summary how often certain units have been modified. More than |
938
|
|
|
|
|
|
|
one string is commonly returned for durations. Useful as a debugging aid. |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
=head1 GRAMMAR |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
The grammar handling has been rewritten to be easily extendable and hence |
943
|
|
|
|
|
|
|
everybody is encouraged to propose sensible new additions and/or changes. |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
See the class L<DateTime::Format::Natural::Lang::EN> if you're intending |
946
|
|
|
|
|
|
|
to hack a bit on the grammar guts. |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
=head1 EXAMPLES |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
See the class L<DateTime::Format::Natural::Lang::EN> for an overview of |
951
|
|
|
|
|
|
|
currently valid input. |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
=head1 BUGS & CAVEATS |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
C<parse_datetime()>/C<parse_datetime_duration()> always return one or two |
956
|
|
|
|
|
|
|
DateTime objects regardless whether the parse was successful or not. In |
957
|
|
|
|
|
|
|
case no valid expression was found or a failure occurred, an unaltered |
958
|
|
|
|
|
|
|
DateTime object with its initial values (most often the "current" now) is |
959
|
|
|
|
|
|
|
likely to be returned. It is therefore recommended to use C<success()> to |
960
|
|
|
|
|
|
|
assert that the parse did succeed (at least, for common uses), otherwise |
961
|
|
|
|
|
|
|
the absence of a parse failure cannot be guaranteed. |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
C<parse_datetime()> is not capable of handling durations. |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=head1 CREDITS |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
Thanks to Tatsuhiko Miyagawa for the initial inspiration. See Miyagawa's journal |
968
|
|
|
|
|
|
|
entry L<http://use.perl.org/~miyagawa/journal/31378> for more information. |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
Furthermore, thanks to (in order of appearance) who have contributed |
971
|
|
|
|
|
|
|
valuable suggestions and patches: |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
Clayton L. Scott |
974
|
|
|
|
|
|
|
Dave Rolsky |
975
|
|
|
|
|
|
|
CPAN Author 'SEKIMURA' |
976
|
|
|
|
|
|
|
mike (pulsation) |
977
|
|
|
|
|
|
|
Mark Stosberg |
978
|
|
|
|
|
|
|
Tuomas Jormola |
979
|
|
|
|
|
|
|
Cory Watson |
980
|
|
|
|
|
|
|
Urs Stotz |
981
|
|
|
|
|
|
|
Shawn M. Moore |
982
|
|
|
|
|
|
|
Andreas J. König |
983
|
|
|
|
|
|
|
Chia-liang Kao |
984
|
|
|
|
|
|
|
Jonny Schulz |
985
|
|
|
|
|
|
|
Jesse Vincent |
986
|
|
|
|
|
|
|
Jason May |
987
|
|
|
|
|
|
|
Pat Kale |
988
|
|
|
|
|
|
|
Ankur Gupta |
989
|
|
|
|
|
|
|
Alex Bowley |
990
|
|
|
|
|
|
|
Elliot Shank |
991
|
|
|
|
|
|
|
Anirvan Chatterjee |
992
|
|
|
|
|
|
|
Michael Reddick |
993
|
|
|
|
|
|
|
Christian Brink |
994
|
|
|
|
|
|
|
Giovanni Pensa |
995
|
|
|
|
|
|
|
Andrew Sterling Hanenkamp |
996
|
|
|
|
|
|
|
Eric Wilhelm |
997
|
|
|
|
|
|
|
Kevin Field |
998
|
|
|
|
|
|
|
Wes Morgan |
999
|
|
|
|
|
|
|
Vladimir Marek |
1000
|
|
|
|
|
|
|
Rod Taylor |
1001
|
|
|
|
|
|
|
Tim Esselens |
1002
|
|
|
|
|
|
|
Colm Dougan |
1003
|
|
|
|
|
|
|
Chifung Fan |
1004
|
|
|
|
|
|
|
Xiao Yafeng |
1005
|
|
|
|
|
|
|
Roman Filippov |
1006
|
|
|
|
|
|
|
David Steinbrunner |
1007
|
|
|
|
|
|
|
Debian Perl Group |
1008
|
|
|
|
|
|
|
Tim Bunce |
1009
|
|
|
|
|
|
|
Ricardo Signes |
1010
|
|
|
|
|
|
|
Felix Ostmann |
1011
|
|
|
|
|
|
|
Jörn Clausen |
1012
|
|
|
|
|
|
|
Jim Avera |
1013
|
|
|
|
|
|
|
Olaf Alders |
1014
|
|
|
|
|
|
|
Karen Etheridge |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
=head1 SEE ALSO |
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
L<dateparse>, L<DateTime>, L<Date::Calc>, L<http://datetime.perl.org> |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
=head1 AUTHOR |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
Steven Schubiger <schubiger@cpan.org> |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
=head1 LICENSE |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
This program is free software; you may redistribute it and/or |
1027
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
See L<http://dev.perl.org/licenses/> |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
=cut |