line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DateTime::Format::Natural; |
2
|
|
|
|
|
|
|
|
3
|
26
|
|
|
26
|
|
2451663
|
use strict; |
|
26
|
|
|
|
|
224
|
|
|
26
|
|
|
|
|
763
|
|
4
|
26
|
|
|
26
|
|
138
|
use warnings; |
|
26
|
|
|
|
|
56
|
|
|
26
|
|
|
|
|
898
|
|
5
|
26
|
|
|
|
|
13438
|
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
|
|
177
|
); |
|
26
|
|
|
|
|
65
|
|
14
|
26
|
|
|
26
|
|
201
|
use boolean qw(true false); |
|
26
|
|
|
|
|
72
|
|
|
26
|
|
|
|
|
155
|
|
15
|
|
|
|
|
|
|
|
16
|
26
|
|
|
26
|
|
1752
|
use Carp qw(croak); |
|
26
|
|
|
|
|
68
|
|
|
26
|
|
|
|
|
1217
|
|
17
|
26
|
|
|
26
|
|
182
|
use DateTime (); |
|
26
|
|
|
|
|
61
|
|
|
26
|
|
|
|
|
394
|
|
18
|
26
|
|
|
26
|
|
12406
|
use DateTime::HiRes (); |
|
26
|
|
|
|
|
45733
|
|
|
26
|
|
|
|
|
577
|
|
19
|
26
|
|
|
26
|
|
198
|
use DateTime::TimeZone (); |
|
26
|
|
|
|
|
69
|
|
|
26
|
|
|
|
|
548
|
|
20
|
26
|
|
|
26
|
|
146
|
use List::MoreUtils qw(all any none); |
|
26
|
|
|
|
|
90
|
|
|
26
|
|
|
|
|
280
|
|
21
|
26
|
|
|
26
|
|
36512
|
use Params::Validate ':all'; |
|
26
|
|
|
|
|
77034
|
|
|
26
|
|
|
|
|
4936
|
|
22
|
26
|
|
|
26
|
|
257
|
use Scalar::Util qw(blessed); |
|
26
|
|
|
|
|
86
|
|
|
26
|
|
|
|
|
1477
|
|
23
|
26
|
|
|
26
|
|
183
|
use Storable qw(dclone); |
|
26
|
|
|
|
|
55
|
|
|
26
|
|
|
|
|
1440
|
|
24
|
|
|
|
|
|
|
|
25
|
26
|
|
|
26
|
|
178
|
use DateTime::Format::Natural::Utils qw(trim); |
|
26
|
|
|
|
|
63
|
|
|
26
|
|
|
|
|
157392
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $VERSION = '1.17_02'; |
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
|
154456
|
my $class = shift; |
42
|
|
|
|
|
|
|
|
43
|
10582
|
|
33
|
|
|
50517
|
my $self = bless {}, ref($class) || $class; |
44
|
|
|
|
|
|
|
|
45
|
10582
|
|
|
|
|
41789
|
$self->_init_check(@_); |
46
|
10581
|
|
|
|
|
897881
|
$self->_init(@_); |
47
|
|
|
|
|
|
|
|
48
|
10581
|
|
|
|
|
29144
|
return $self; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub _init |
52
|
|
|
|
|
|
|
{ |
53
|
10581
|
|
|
10581
|
|
23265
|
my $self = shift; |
54
|
10581
|
|
|
|
|
33037
|
my %opts = @_; |
55
|
|
|
|
|
|
|
|
56
|
10581
|
|
|
|
|
31514
|
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
|
|
|
|
|
89046
|
foreach my $opt (keys %presets) { |
64
|
52905
|
|
|
|
|
127256
|
$self->{ucfirst $opt} = $presets{$opt}; |
65
|
|
|
|
|
|
|
} |
66
|
10581
|
|
|
|
|
30444
|
foreach my $opt (keys %opts) { |
67
|
7004
|
50
|
|
|
|
18341
|
if (defined $opts{$opt}) { |
68
|
7004
|
|
|
|
|
16116
|
$self->{ucfirst $opt} = $opts{$opt}; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
10581
|
|
100
|
|
|
51178
|
$self->{Daytime} = $opts{daytime} || {}; |
72
|
|
|
|
|
|
|
|
73
|
10581
|
|
|
|
|
38673
|
my $mod = join '::', (__PACKAGE__, 'Lang', uc $self->{Lang}); |
74
|
10581
|
50
|
|
|
|
830233
|
eval "require $mod" or die $@; |
75
|
|
|
|
|
|
|
|
76
|
10581
|
|
|
|
|
67153
|
$self->{data} = $mod->__new(); |
77
|
10581
|
|
|
|
|
31291
|
$self->{grammar_class} = $mod; |
78
|
|
|
|
|
|
|
|
79
|
10581
|
|
|
|
|
43066
|
$self->{mode} = ''; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub _init_check |
83
|
|
|
|
|
|
|
{ |
84
|
10582
|
|
|
10582
|
|
18873
|
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
|
|
68752
|
return true unless exists $_[1]->{prefer_future}; |
95
|
1
|
|
|
|
|
10
|
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
|
|
70472
|
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
|
|
86536
|
my $val = shift; |
132
|
1562
|
100
|
|
|
|
5578
|
if (blessed($val)) { |
133
|
1
|
|
|
|
|
18
|
return $val->isa('DateTime::TimeZone'); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
else { |
136
|
1561
|
|
|
|
|
2967
|
eval { DateTime::TimeZone->new(name => $val) }; |
|
1561
|
|
|
|
|
6087
|
|
137
|
1561
|
|
|
|
|
127587
|
return !$@; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
}, |
141
|
|
|
|
|
|
|
}, |
142
|
|
|
|
|
|
|
daytime => { |
143
|
|
|
|
|
|
|
type => HASHREF, |
144
|
|
|
|
|
|
|
optional => true, |
145
|
|
|
|
|
|
|
callbacks => { |
146
|
|
|
|
|
|
|
'valid daytime' => sub |
147
|
|
|
|
|
|
|
{ |
148
|
39
|
|
|
39
|
|
2108
|
my $href = shift; |
149
|
39
|
|
|
|
|
91
|
my %daytimes = map { $_ => true } qw(morning afternoon evening); |
|
117
|
|
|
|
|
368
|
|
150
|
39
|
50
|
|
|
|
521
|
if (any { !$daytimes{$_} } keys %$href) { |
|
58
|
50
|
|
|
|
375
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
151
|
0
|
|
|
|
|
0
|
die "spelling of daytime\n"; |
152
|
|
|
|
|
|
|
} |
153
|
58
|
|
|
|
|
324
|
elsif (any { !defined $href->{$_} } keys %$href) { |
154
|
0
|
|
|
|
|
0
|
die "undefined hour\n"; |
155
|
|
|
|
|
|
|
} |
156
|
58
|
|
|
|
|
300
|
elsif (any { $href->{$_} !~ /^\d{1,2}$/ } keys %$href) { |
157
|
0
|
|
|
|
|
0
|
die "not a valid number\n"; |
158
|
|
|
|
|
|
|
} |
159
|
58
|
50
|
|
|
|
216
|
elsif (any { $href->{$_} < 0 || $href->{$_} > 23 } keys %$href) { |
160
|
0
|
|
|
|
|
0
|
die "hour out of range\n"; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
else { |
163
|
39
|
|
|
|
|
96
|
return true; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
}, |
167
|
|
|
|
|
|
|
}, |
168
|
|
|
|
|
|
|
datetime => { |
169
|
|
|
|
|
|
|
type => OBJECT, |
170
|
|
|
|
|
|
|
optional => true, |
171
|
|
|
|
|
|
|
callbacks => { |
172
|
|
|
|
|
|
|
'valid object' => sub |
173
|
|
|
|
|
|
|
{ |
174
|
26
|
|
|
26
|
|
911
|
my $obj = shift; |
175
|
26
|
50
|
|
|
|
424
|
blessed($obj) && $obj->isa('DateTime'); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
}, |
178
|
|
|
|
|
|
|
}, |
179
|
10582
|
|
|
|
|
41210
|
}); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub _init_vars |
183
|
|
|
|
|
|
|
{ |
184
|
11703
|
|
|
11703
|
|
21402
|
my $self = shift; |
185
|
|
|
|
|
|
|
|
186
|
11703
|
|
|
|
|
36632
|
delete @$self{qw(keyword modified postprocess)}; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub parse_datetime |
190
|
|
|
|
|
|
|
{ |
191
|
11703
|
|
|
11703
|
1
|
64126
|
my $self = shift; |
192
|
|
|
|
|
|
|
|
193
|
11703
|
|
|
|
|
37754
|
$self->_parse_init(@_); |
194
|
|
|
|
|
|
|
|
195
|
11703
|
|
|
|
|
91801
|
$self->{input_string} = $self->{date_string}; |
196
|
|
|
|
|
|
|
|
197
|
11703
|
|
|
|
|
21731
|
$self->{mode} = 'parse'; |
198
|
|
|
|
|
|
|
|
199
|
11703
|
|
|
|
|
21234
|
my $date_string = $self->{date_string}; |
200
|
|
|
|
|
|
|
|
201
|
11703
|
|
|
|
|
48734
|
$self->_rewrite(\$date_string); |
202
|
|
|
|
|
|
|
|
203
|
11703
|
|
|
|
|
76257
|
my ($formatted) = $date_string =~ $self->{data}->__regexes('format'); |
204
|
11703
|
|
|
|
|
48469
|
my %count = $self->_count_separators($formatted); |
205
|
|
|
|
|
|
|
|
206
|
11703
|
|
|
|
|
31806
|
$self->{tokens} = []; |
207
|
11703
|
|
|
|
|
25224
|
$self->{traces} = []; |
208
|
|
|
|
|
|
|
|
209
|
11703
|
100
|
|
|
|
35994
|
if ($self->_check_formatted('ymd', \%count)) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
210
|
271
|
|
|
|
|
1074
|
my $dt = $self->_parse_formatted_ymd($date_string, \%count); |
211
|
271
|
100
|
|
|
|
1049
|
return $dt if blessed($dt); |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
elsif ($self->_check_formatted('md', \%count)) { |
214
|
193
|
|
|
|
|
750
|
my $dt = $self->_parse_formatted_md($date_string); |
215
|
193
|
100
|
|
|
|
714
|
return $dt if blessed($dt); |
216
|
|
|
|
|
|
|
|
217
|
192
|
100
|
100
|
|
|
534
|
if ($self->{Prefer_future} || $self->{Demand_future}) { |
218
|
36
|
|
|
|
|
509
|
$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
|
|
|
|
|
39
|
my ($date, $time) = ($1, $2); |
223
|
|
|
|
|
|
|
|
224
|
9
|
|
|
|
|
12
|
my %args; |
225
|
|
|
|
|
|
|
|
226
|
9
|
|
|
|
|
50
|
@args{qw(year month day)} = split /-/, $date; |
227
|
9
|
|
100
|
|
|
57
|
$args{$_} ||= 01 foreach qw(month day); |
228
|
|
|
|
|
|
|
|
229
|
9
|
|
|
|
|
32
|
@args{qw(hour minute second)} = split /:/, $time; |
230
|
9
|
|
100
|
|
|
42
|
$args{$_} ||= 00 foreach qw(minute second); |
231
|
|
|
|
|
|
|
|
232
|
9
|
|
|
|
|
65
|
my $valid_date = $self->_check_date(map $args{$_}, qw(year month day)); |
233
|
9
|
|
|
|
|
53
|
my $valid_time = $self->_check_time(map $args{$_}, qw(hour minute second)); |
234
|
|
|
|
|
|
|
|
235
|
9
|
50
|
33
|
|
|
40
|
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
|
|
|
|
|
49
|
$self->_set(%args); |
243
|
|
|
|
|
|
|
|
244
|
9
|
|
|
|
|
39
|
$self->{datetime}->truncate(to => 'second'); |
245
|
9
|
|
|
|
|
2456
|
$self->_set_truncated; |
246
|
9
|
|
|
|
|
43
|
$self->_set_valid_exp; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
elsif ($date_string =~ /^([+-]) (\d+?) ([a-zA-Z]+)$/x) { |
249
|
14
|
|
|
|
|
81
|
my ($prefix, $value, $unit) = ($1, $2, lc $3); |
250
|
|
|
|
|
|
|
|
251
|
14
|
|
|
|
|
55
|
my %methods = ( |
252
|
|
|
|
|
|
|
'+' => '_add', |
253
|
|
|
|
|
|
|
'-' => '_subtract', |
254
|
|
|
|
|
|
|
); |
255
|
14
|
|
|
|
|
32
|
my $method = $methods{$prefix}; |
256
|
|
|
|
|
|
|
|
257
|
14
|
100
|
|
64
|
|
54
|
if (none { $unit =~ /^${_}s?$/ } @{$self->{data}->__units('ordered')}) { |
|
64
|
|
|
|
|
679
|
|
|
14
|
|
|
|
|
93
|
|
258
|
2
|
|
|
|
|
8
|
$self->_set_failure; |
259
|
2
|
|
|
|
|
12
|
$self->_set_error("(invalid unit)"); |
260
|
2
|
|
|
|
|
6
|
return $self->_get_datetime_object; |
261
|
|
|
|
|
|
|
} |
262
|
12
|
|
|
|
|
107
|
$self->$method($unit => $value); |
263
|
|
|
|
|
|
|
|
264
|
12
|
|
|
|
|
39
|
$self->_set_valid_exp; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
elsif ($date_string =~ /^\d{14}$/) { |
267
|
6
|
|
|
|
|
11
|
my %args; |
268
|
6
|
|
|
|
|
46
|
@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
|
|
|
|
|
43
|
my $valid_date = $self->_check_date(map $args{$_}, qw(year month day)); |
271
|
6
|
|
|
|
|
38
|
my $valid_time = $self->_check_time(map $args{$_}, qw(hour minute second)); |
272
|
|
|
|
|
|
|
|
273
|
6
|
50
|
33
|
|
|
37
|
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
|
|
|
|
|
31
|
$self->_set(%args); |
281
|
|
|
|
|
|
|
|
282
|
6
|
|
|
|
|
53
|
$self->{datetime}->truncate(to => 'second'); |
283
|
6
|
|
|
|
|
1548
|
$self->_set_truncated; |
284
|
6
|
|
|
|
|
28
|
$self->_set_valid_exp; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
else { |
287
|
11210
|
|
|
|
|
22140
|
@{$self->{tokens}} = split /\s+/, $date_string; |
|
11210
|
|
|
|
|
58935
|
|
288
|
11210
|
|
|
|
|
65367
|
$self->{data}->__init('tokens')->($self); |
289
|
11210
|
|
|
|
|
19622
|
$self->{count}{tokens} = @{$self->{tokens}}; |
|
11210
|
|
|
|
|
34498
|
|
290
|
|
|
|
|
|
|
|
291
|
11210
|
|
|
|
|
31956
|
$self->_process; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
11699
|
|
|
|
|
232834
|
my $trace = $self->_trace_string; |
295
|
11699
|
100
|
|
|
|
38267
|
if (defined $trace) { |
296
|
11339
|
|
|
|
|
19344
|
@{$self->{traces}} = $trace; |
|
11339
|
|
|
|
|
33434
|
|
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
11699
|
|
|
|
|
35155
|
return $self->_get_datetime_object; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub _params_init |
303
|
|
|
|
|
|
|
{ |
304
|
13170
|
|
|
13170
|
|
21946
|
my $self = shift; |
305
|
13170
|
|
|
|
|
21765
|
my $params = pop; |
306
|
|
|
|
|
|
|
|
307
|
13170
|
50
|
|
|
|
34524
|
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
|
|
|
|
|
176022
|
validate_pos(@_, { type => SCALAR }); |
316
|
13170
|
|
|
|
|
45777
|
(${$params->{string}}) = @_; |
|
13170
|
|
|
|
|
35415
|
|
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
13170
|
|
|
|
|
50199
|
trim($params->{string}); |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub _parse_init |
323
|
|
|
|
|
|
|
{ |
324
|
11703
|
|
|
11703
|
|
21221
|
my $self = shift; |
325
|
|
|
|
|
|
|
|
326
|
11703
|
|
|
|
|
51834
|
$self->_params_init(@_, { string => \$self->{date_string} }); |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
my $set_datetime = sub |
329
|
|
|
|
|
|
|
{ |
330
|
2907
|
|
|
2907
|
|
7001
|
my ($method, $args) = @_; |
331
|
|
|
|
|
|
|
|
332
|
2907
|
100
|
66
|
|
|
9125
|
if (exists $self->{Datetime} && $method eq 'now') { |
333
|
24
|
|
|
|
|
267
|
$self->{datetime} = dclone($self->{Datetime}); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
else { |
336
|
|
|
|
|
|
|
$self->{datetime} = DateTime::HiRes->$method( |
337
|
|
|
|
|
|
|
time_zone => $self->{Time_zone}, |
338
|
2883
|
|
|
|
|
15222
|
%$args, |
339
|
|
|
|
|
|
|
); |
340
|
|
|
|
|
|
|
} |
341
|
11703
|
|
|
|
|
61073
|
}; |
342
|
|
|
|
|
|
|
|
343
|
11703
|
100
|
|
|
|
42799
|
if ($self->{running_tests}) { |
344
|
8796
|
|
|
|
|
80978
|
$self->{datetime} = $self->{datetime_test}->clone; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
else { |
347
|
2907
|
|
|
|
|
6741
|
$set_datetime->('now', {}); |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
11703
|
|
|
|
|
1663856
|
$self->_init_vars; |
351
|
|
|
|
|
|
|
|
352
|
11703
|
|
|
|
|
35019
|
$self->_unset_failure; |
353
|
11703
|
|
|
|
|
56525
|
$self->_unset_error; |
354
|
11703
|
|
|
|
|
32570
|
$self->_unset_valid_exp; |
355
|
11703
|
|
|
|
|
59401
|
$self->_unset_trace; |
356
|
11703
|
|
|
|
|
27347
|
$self->_unset_truncated; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
sub parse_datetime_duration |
360
|
|
|
|
|
|
|
{ |
361
|
1258
|
|
|
1258
|
1
|
204266
|
my $self = shift; |
362
|
|
|
|
|
|
|
|
363
|
1258
|
|
|
|
|
2445
|
my $duration_string; |
364
|
1258
|
|
|
|
|
6435
|
$self->_params_init(@_, { string => \$duration_string }); |
365
|
1258
|
|
|
|
|
9320
|
my $timespan_sep = $self->{data}->__timespan('literal'); |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
my @date_strings = $duration_string =~ /\s+ $timespan_sep \s+/ix |
368
|
1039
|
|
|
|
|
3230
|
? do { $self->{duration} = true; |
369
|
1039
|
|
|
|
|
9671
|
split /\s+ $timespan_sep \s+/ix, $duration_string } |
370
|
1258
|
100
|
|
|
|
8797
|
: do { $self->{duration} = false; |
|
219
|
|
|
|
|
836
|
|
371
|
219
|
|
|
|
|
1106
|
($duration_string) }; |
372
|
|
|
|
|
|
|
|
373
|
1258
|
|
|
|
|
2775
|
my $max = 2; |
374
|
|
|
|
|
|
|
|
375
|
1258
|
|
|
|
|
3042
|
my $shrinked = false; |
376
|
1258
|
100
|
|
|
|
5620
|
if (@date_strings > $max) { |
377
|
1
|
|
|
|
|
3
|
my $offset = $max; |
378
|
1
|
|
|
|
|
2
|
splice (@date_strings, $offset); |
379
|
1
|
|
|
|
|
3
|
$shrinked = true; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
1258
|
|
|
|
|
6037
|
$self->_rewrite_duration(\@date_strings); |
383
|
|
|
|
|
|
|
|
384
|
1258
|
|
|
|
|
7615
|
$self->_pre_duration(\@date_strings); |
385
|
1258
|
|
|
|
|
17864
|
@$self{qw(state truncated_duration)} = ({}, []); |
386
|
|
|
|
|
|
|
|
387
|
1258
|
|
|
|
|
2976
|
my (@queue, @traces, @truncated); |
388
|
1258
|
|
|
|
|
3022
|
foreach my $date_string (@date_strings) { |
389
|
2297
|
|
|
|
|
6680
|
push @queue, $self->parse_datetime($date_string); |
390
|
2297
|
|
|
|
|
6466
|
$self->_save_state( |
391
|
|
|
|
|
|
|
valid_expression => $self->_get_valid_exp, |
392
|
|
|
|
|
|
|
failure => $self->_get_failure, |
393
|
|
|
|
|
|
|
error => $self->_get_error, |
394
|
|
|
|
|
|
|
); |
395
|
2297
|
100
|
|
|
|
19646
|
if (@{$self->{traces}}) { |
|
2297
|
|
|
|
|
6280
|
|
396
|
2292
|
|
|
|
|
5367
|
push @traces, $self->{traces}[0]; |
397
|
|
|
|
|
|
|
} |
398
|
2297
|
100
|
|
|
|
5955
|
if ($self->{running_tests}) { |
399
|
1932
|
|
|
|
|
14325
|
push @truncated, $self->_get_truncated; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
1258
|
|
|
|
|
6452
|
$self->_post_duration(\@queue, \@traces, \@truncated); |
404
|
1258
|
|
|
|
|
7674
|
$self->_restore_state; |
405
|
|
|
|
|
|
|
|
406
|
1258
|
|
|
|
|
5347
|
delete @$self{qw(duration insert state)}; |
407
|
|
|
|
|
|
|
|
408
|
1258
|
|
|
|
|
2443
|
@{$self->{traces}} = @traces; |
|
1258
|
|
|
|
|
3741
|
|
409
|
1258
|
|
|
|
|
2288
|
@{$self->{truncated_duration}} = @truncated; |
|
1258
|
|
|
|
|
2848
|
|
410
|
1258
|
|
|
|
|
2711
|
$self->{input_string} = $duration_string; |
411
|
|
|
|
|
|
|
|
412
|
1258
|
100
|
|
|
|
3386
|
if ($shrinked) { |
413
|
1
|
|
|
|
|
11
|
$self->_set_failure; |
414
|
1
|
|
|
|
|
13
|
$self->_set_error("(limit of $max duration substrings exceeded)"); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
1258
|
|
|
|
|
15241
|
return @queue; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub extract_datetime |
421
|
|
|
|
|
|
|
{ |
422
|
209
|
|
|
209
|
1
|
4964
|
my $self = shift; |
423
|
|
|
|
|
|
|
|
424
|
209
|
|
|
|
|
399
|
my $extract_string; |
425
|
209
|
|
|
|
|
1061
|
$self->_params_init(@_, { string => \$extract_string }); |
426
|
|
|
|
|
|
|
|
427
|
209
|
|
|
|
|
1010
|
$self->_unset_failure; |
428
|
209
|
|
|
|
|
1126
|
$self->_unset_error; |
429
|
209
|
|
|
|
|
582
|
$self->_unset_valid_exp; |
430
|
|
|
|
|
|
|
|
431
|
209
|
|
|
|
|
966
|
$self->{input_string} = $extract_string; |
432
|
|
|
|
|
|
|
|
433
|
209
|
|
|
|
|
569
|
$self->{mode} = 'extract'; |
434
|
|
|
|
|
|
|
|
435
|
209
|
|
|
|
|
1093
|
my @expressions = $self->_extract_expressions($extract_string); |
436
|
|
|
|
|
|
|
|
437
|
209
|
100
|
|
|
|
2428
|
$self->_set_valid_exp if @expressions; |
438
|
|
|
|
|
|
|
|
439
|
209
|
100
|
|
|
|
2122
|
return wantarray ? @expressions : $expressions[0]; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub success |
443
|
|
|
|
|
|
|
{ |
444
|
10602
|
|
|
10602
|
1
|
69653
|
my $self = shift; |
445
|
|
|
|
|
|
|
|
446
|
10602
|
100
|
100
|
|
|
25734
|
return ($self->_get_valid_exp && !$self->_get_failure) ? true : false; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub error |
450
|
|
|
|
|
|
|
{ |
451
|
6
|
|
|
6
|
1
|
64
|
my $self = shift; |
452
|
|
|
|
|
|
|
|
453
|
6
|
100
|
|
|
|
16
|
return '' if $self->success; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
my $error = sub |
456
|
|
|
|
|
|
|
{ |
457
|
5
|
100
|
66
|
5
|
|
35
|
return undef unless defined $self->{mode} && length $self->{mode}; |
458
|
4
|
|
|
|
|
36
|
my %errors = ( |
459
|
|
|
|
|
|
|
extract => "'$self->{input_string}' cannot be extracted from", |
460
|
|
|
|
|
|
|
parse => "'$self->{input_string}' does not parse", |
461
|
|
|
|
|
|
|
); |
462
|
4
|
|
|
|
|
15
|
return $errors{$self->{mode}}; |
463
|
5
|
|
|
|
|
142
|
}->(); |
464
|
|
|
|
|
|
|
|
465
|
5
|
100
|
|
|
|
32
|
if (defined $error) { |
466
|
4
|
|
100
|
|
|
21
|
$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
|
|
|
|
|
44
|
return $error; |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
sub trace |
476
|
|
|
|
|
|
|
{ |
477
|
7
|
|
|
7
|
1
|
56
|
my $self = shift; |
478
|
|
|
|
|
|
|
|
479
|
7
|
100
|
|
|
|
11
|
return @{$self->{traces} || []}; |
|
7
|
|
|
|
|
45
|
|
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub _process |
483
|
|
|
|
|
|
|
{ |
484
|
11531
|
|
|
11531
|
|
21100
|
my $self = shift; |
485
|
|
|
|
|
|
|
|
486
|
11531
|
|
|
|
|
18595
|
my %opts; |
487
|
|
|
|
|
|
|
|
488
|
11531
|
100
|
|
|
|
30861
|
if (!exists $self->{lookup}) { |
489
|
10414
|
|
|
|
|
16112
|
foreach my $keyword (keys %{$self->{data}->__grammar('')}) { |
|
10414
|
|
|
|
|
48481
|
|
490
|
697738
|
|
|
|
|
5412121
|
my $count = scalar @{$self->{data}->__grammar($keyword)->[0]}; |
|
697738
|
|
|
|
|
2569562
|
|
491
|
697738
|
|
|
|
|
1128650
|
push @{$self->{lookup}{$count}}, [ $keyword, false ]; |
|
697738
|
|
|
|
|
1770375
|
|
492
|
697738
|
100
|
|
|
|
2981901
|
if ($self->_expand_for($keyword)) { |
493
|
197866
|
|
|
|
|
1761486
|
push @{$self->{lookup}{$count + 1}}, [ $keyword, true ]; |
|
197866
|
|
|
|
|
509019
|
|
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
11531
|
50
|
|
|
|
129625
|
PARSE: foreach my $lookup (@{$self->{lookup}{$self->{count}{tokens}} || []}) { |
|
11531
|
|
|
|
|
52460
|
|
499
|
111614
|
|
|
|
|
1537686
|
my ($keyword, $expandable) = @$lookup; |
500
|
|
|
|
|
|
|
|
501
|
111614
|
|
|
|
|
165728
|
my @grammar = @{$self->{data}->__grammar($keyword)}; |
|
111614
|
|
|
|
|
545725
|
|
502
|
111614
|
|
|
|
|
213165
|
my $types_entry = shift @grammar; |
503
|
|
|
|
|
|
|
|
504
|
111614
|
100
|
|
|
|
269197
|
@grammar = $self->_expand($keyword, $types_entry, \@grammar) if $expandable; |
505
|
|
|
|
|
|
|
|
506
|
111614
|
|
|
|
|
744537
|
foreach my $entry (@grammar) { |
507
|
669041
|
100
|
|
|
|
4486307
|
my ($types, $expression) = $expandable ? @$entry : ($types_entry, $entry); |
508
|
669041
|
|
|
|
|
4705131
|
my $valid_expression = true; |
509
|
669041
|
|
|
|
|
2021954
|
my $definition = $expression->[0]; |
510
|
669041
|
|
|
|
|
1922781
|
my @positions = sort {$a <=> $b} keys %$definition; |
|
1844039
|
|
|
|
|
3539442
|
|
511
|
669041
|
|
|
|
|
1154313
|
my (%first_stack, %rest_stack); |
512
|
669041
|
|
|
|
|
1066095
|
foreach my $pos (@positions) { |
513
|
775069
|
100
|
|
|
|
1734612
|
if ($types->[$pos] eq 'SCALAR') { |
|
|
50
|
|
|
|
|
|
514
|
65024
|
50
|
|
|
|
139585
|
if (defined $definition->{$pos}) { |
515
|
65024
|
100
|
|
|
|
88076
|
if (${$self->_token($pos)} =~ /^$definition->{$pos}$/i) { |
|
65024
|
|
|
|
|
120527
|
|
516
|
6354
|
|
|
|
|
16660
|
next; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
else { |
519
|
58670
|
|
|
|
|
148519
|
$valid_expression = false; |
520
|
58670
|
|
|
|
|
196796
|
last; |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
elsif ($types->[$pos] eq 'REGEXP') { |
525
|
710045
|
100
|
|
|
|
973930
|
if (my @captured = ${$self->_token($pos)} =~ $definition->{$pos}) { |
|
710045
|
|
|
|
|
1292169
|
|
526
|
111211
|
|
|
|
|
269581
|
$first_stack{$pos} = shift @captured; |
527
|
111211
|
|
|
|
|
205246
|
$rest_stack{$pos} = [ @captured ]; |
528
|
111211
|
|
|
|
|
254490
|
next; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
else { |
531
|
598834
|
|
|
|
|
1321743
|
$valid_expression = false; |
532
|
598834
|
|
|
|
|
1844212
|
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
|
669041
|
100
|
100
|
|
|
1563220
|
if ($valid_expression && @{$expression->[2]}) { |
|
11537
|
|
|
|
|
115155
|
|
541
|
8573
|
|
|
|
|
16372
|
my $i = 0; |
542
|
8573
|
|
|
|
|
14806
|
foreach my $check (@{$expression->[2]}) { |
|
8573
|
|
|
|
|
20706
|
|
543
|
10839
|
|
|
|
|
17909
|
my @pos = @{$expression->[1][$i++]}; |
|
10839
|
|
|
|
|
28125
|
|
544
|
10839
|
|
|
|
|
18235
|
my $error; |
545
|
10839
|
|
|
|
|
39955
|
$valid_expression &= $check->(\%first_stack, \%rest_stack, \@pos, \$error); |
546
|
10839
|
100
|
|
|
|
66683
|
unless ($valid_expression) { |
547
|
366
|
|
|
|
|
1735
|
$self->_set_error("($error)"); |
548
|
366
|
|
|
|
|
832
|
last; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
} |
552
|
669041
|
100
|
|
|
|
4847932
|
if ($valid_expression) { |
553
|
11171
|
|
|
|
|
51624
|
$self->_set_valid_exp; |
554
|
11171
|
100
|
|
|
|
40320
|
my @truncate_to = @{$expression->[6]->{truncate_to} || []}; |
|
11171
|
|
|
|
|
44274
|
|
555
|
11171
|
|
|
|
|
20746
|
my $i = 0; |
556
|
11171
|
|
|
|
|
17436
|
foreach my $positions (@{$expression->[3]}) { |
|
11171
|
|
|
|
|
25445
|
|
557
|
19578
|
|
|
|
|
33532
|
my ($c, @values); |
558
|
19578
|
|
|
|
|
36753
|
foreach my $pos (@$positions) { |
559
|
24633
|
100
|
|
|
|
86184
|
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
|
|
|
|
123651
|
: ${$self->_token($index)}; |
|
0
|
50
|
|
|
|
0
|
|
|
|
100
|
|
|
|
|
|
567
|
|
|
|
|
|
|
} |
568
|
19578
|
|
|
|
|
52143
|
my $worker = "SUPER::$expression->[5]->[$i]"; |
569
|
19578
|
|
|
|
|
96954
|
$self->$worker(@values, $expression->[4]->[$i++]); |
570
|
19578
|
|
|
|
|
85911
|
$self->_truncate(shift @truncate_to); |
571
|
|
|
|
|
|
|
} |
572
|
11171
|
|
|
|
|
22245
|
%opts = %{$expression->[6]}; |
|
11171
|
|
|
|
|
36146
|
|
573
|
11171
|
|
|
|
|
28016
|
$self->{keyword} = $keyword; |
574
|
11171
|
|
|
|
|
93098
|
last PARSE; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
11531
|
|
|
|
|
38376
|
$self->_post_process(%opts); |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
sub _truncate |
583
|
|
|
|
|
|
|
{ |
584
|
19578
|
|
|
19578
|
|
34802
|
my $self = shift; |
585
|
19578
|
|
|
|
|
40237
|
my ($truncate_to) = @_; |
586
|
|
|
|
|
|
|
|
587
|
19578
|
100
|
|
|
|
62846
|
return unless defined $truncate_to; |
588
|
|
|
|
|
|
|
|
589
|
11327
|
100
|
|
|
|
22936
|
my @truncate_to = map { $_ =~ /_/ ? split /_/, $_ : $_ } $truncate_to; |
|
11327
|
|
|
|
|
72313
|
|
590
|
11327
|
|
|
|
|
23049
|
my $i = 0; |
591
|
11327
|
|
|
|
|
18224
|
my @units = @{$self->{data}->__units('ordered')}; |
|
11327
|
|
|
|
|
74992
|
|
592
|
11327
|
|
|
|
|
28653
|
my %indexes = map { $_ => $i++ } @units; |
|
90616
|
|
|
|
|
178335
|
|
593
|
11327
|
|
|
|
|
31604
|
foreach my $unit (@truncate_to) { |
594
|
20001
|
|
|
|
|
39352
|
my $index = $indexes{$unit} - 1; |
595
|
20001
|
100
|
66
|
|
|
89242
|
if (defined $units[$index] && !exists $self->{modified}{$units[$index]}) { |
596
|
11273
|
|
|
|
|
47070
|
$self->{datetime}->truncate(to => $unit); |
597
|
11273
|
|
|
|
|
3342510
|
$self->_set_truncated; |
598
|
11273
|
|
|
|
|
86682
|
last; |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
sub _post_process |
604
|
|
|
|
|
|
|
{ |
605
|
11531
|
|
|
11531
|
|
20450
|
my $self = shift; |
606
|
11531
|
|
|
|
|
27148
|
my %opts = @_; |
607
|
|
|
|
|
|
|
|
608
|
11531
|
|
|
|
|
24763
|
delete $opts{truncate_to}; |
609
|
|
|
|
|
|
|
|
610
|
11531
|
50
|
100
|
|
|
44079
|
if (($self->{Prefer_future} || $self->{Demand_future}) |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
611
|
|
|
|
|
|
|
&& (exists $opts{advance_future} && $opts{advance_future}) |
612
|
|
|
|
|
|
|
) { |
613
|
2070
|
|
|
|
|
41399
|
$self->_advance_future; |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub _advance_future |
618
|
|
|
|
|
|
|
{ |
619
|
2106
|
|
|
2106
|
|
3759
|
my $self = shift; |
620
|
2106
|
|
|
|
|
4279
|
my %advance = map { $_ => true } @_; |
|
36
|
|
|
|
|
79
|
|
621
|
|
|
|
|
|
|
|
622
|
2106
|
|
|
|
|
3658
|
my %modified = map { $_ => true } keys %{$self->{modified}}; |
|
6156
|
|
|
|
|
18968
|
|
|
2106
|
|
|
|
|
6998
|
|
623
|
|
|
|
|
|
|
my $token_contains = sub |
624
|
|
|
|
|
|
|
{ |
625
|
2520
|
|
|
2520
|
|
41017
|
my ($identifier) = @_; |
626
|
|
|
|
|
|
|
return any { |
627
|
27066
|
|
|
|
|
67864
|
my $data = $_; |
628
|
|
|
|
|
|
|
any { |
629
|
43158
|
|
|
|
|
72848
|
my $token = $_; |
630
|
43158
|
|
|
|
|
238447
|
$token =~ /^$data$/i; |
631
|
27066
|
|
|
|
|
67532
|
} @{$self->{tokens}} |
|
27066
|
|
|
|
|
62343
|
|
632
|
2520
|
|
|
|
|
9810
|
} @{$self->{data}->{$identifier}}; |
|
2520
|
|
|
|
|
10964
|
|
633
|
2106
|
|
|
|
|
16897
|
}; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
my $now = exists $self->{Datetime} |
636
|
|
|
|
|
|
|
? dclone($self->{Datetime}) |
637
|
2106
|
100
|
|
|
|
10079
|
: DateTime::HiRes->now(time_zone => $self->{Time_zone}); |
638
|
|
|
|
|
|
|
|
639
|
2106
|
|
|
1530
|
|
1045281
|
my $day_of_week = sub { $_[0]->_Day_of_Week(map $_[0]->{datetime}->$_, qw(year month day)) }; |
|
1530
|
|
|
|
|
52420
|
|
640
|
|
|
|
|
|
|
|
641
|
2106
|
|
|
|
|
6791
|
my $skip_weekdays = false; |
642
|
|
|
|
|
|
|
|
643
|
2106
|
50
|
33
|
4388
|
|
14244
|
if ((all { /^(?:(?:nano)?second|minute|hour)$/ } keys %modified) |
|
4388
|
100
|
66
|
|
|
26106
|
|
|
|
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
|
|
|
|
|
25226
|
$self->{postprocess}{day} = 1; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
elsif (sub { |
651
|
1872
|
100
|
|
1872
|
|
26313
|
return false unless @{$self->{tokens}} == 2; |
|
1872
|
|
|
|
|
6697
|
|
652
|
1398
|
|
|
|
|
10586
|
my ($day, $weekday) = map $self->{data}->__RE($_), qw(day weekday); |
653
|
1398
|
100
|
100
|
|
|
10615
|
if ($self->{tokens}->[0] =~ $day |
654
|
|
|
|
|
|
|
&& $self->{tokens}->[1] =~ $weekday) { |
655
|
36
|
|
|
|
|
156
|
$skip_weekdays = true; |
656
|
36
|
|
|
|
|
140
|
return true; |
657
|
|
|
|
|
|
|
} |
658
|
1362
|
|
|
|
|
4155
|
return false; |
659
|
|
|
|
|
|
|
}->() |
660
|
108
|
|
|
108
|
|
972
|
&& (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
|
|
|
|
|
766
|
$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
|
|
|
|
|
18304
|
$self->{postprocess}{day} = 7; |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
elsif (($token_contains->('months_all') || $advance{md}) |
674
|
156
|
|
|
156
|
|
1926
|
&& (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
|
|
|
|
|
3291
|
$self->{postprocess}{year} = 1; |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
sub _token |
688
|
|
|
|
|
|
|
{ |
689
|
775069
|
|
|
775069
|
|
1109855
|
my $self = shift; |
690
|
775069
|
|
|
|
|
1240561
|
my ($pos) = @_; |
691
|
|
|
|
|
|
|
|
692
|
775069
|
|
|
|
|
1095727
|
my $str = ''; |
693
|
775069
|
|
|
|
|
1547344
|
my $token = $self->{tokens}->[0 + $pos]; |
694
|
|
|
|
|
|
|
|
695
|
775069
|
50
|
|
|
|
4168393
|
return defined $token |
696
|
|
|
|
|
|
|
? \$token |
697
|
|
|
|
|
|
|
: \$str; |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
19578
|
|
|
19578
|
|
30664
|
sub _register_trace { push @{$_[0]->{trace}}, (caller(1))[3] } |
|
19578
|
|
|
|
|
169726
|
|
701
|
11703
|
|
|
11703
|
|
20205
|
sub _unset_trace { @{$_[0]->{trace}} = () } |
|
11703
|
|
|
|
|
33007
|
|
702
|
|
|
|
|
|
|
|
703
|
2652
|
|
|
2652
|
|
17899
|
sub _get_error { $_[0]->{error} } |
704
|
372
|
|
|
372
|
|
952
|
sub _set_error { $_[0]->{error} = $_[1] } |
705
|
11914
|
|
|
11914
|
|
24734
|
sub _unset_error { $_[0]->{error} = undef } |
706
|
|
|
|
|
|
|
|
707
|
12538
|
|
|
12538
|
|
110128
|
sub _get_failure { $_[0]->{failure} } |
708
|
6
|
|
|
6
|
|
35
|
sub _set_failure { $_[0]->{failure} = true } |
709
|
11914
|
|
|
11914
|
|
30601
|
sub _unset_failure { $_[0]->{failure} = false } |
710
|
|
|
|
|
|
|
|
711
|
12899
|
|
|
12899
|
|
47590
|
sub _get_valid_exp { $_[0]->{valid_expression} } |
712
|
11868
|
|
|
11868
|
|
26923
|
sub _set_valid_exp { $_[0]->{valid_expression} = true } |
713
|
12236
|
|
|
12236
|
|
25696
|
sub _unset_valid_exp { $_[0]->{valid_expression} = false } |
714
|
|
|
|
|
|
|
|
715
|
10779
|
|
|
10779
|
|
188786
|
sub _get_truncated { $_[0]->{truncated} } |
716
|
11750
|
|
|
11750
|
|
35925
|
sub _set_truncated { $_[0]->{truncated} = true } |
717
|
12024
|
|
|
12024
|
|
25964
|
sub _unset_truncated { $_[0]->{truncated} = false } |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
sub _get_datetime_object |
720
|
|
|
|
|
|
|
{ |
721
|
11703
|
|
|
11703
|
|
20062
|
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
|
|
|
|
|
42199
|
); |
733
|
|
|
|
|
|
|
|
734
|
11703
|
|
|
|
|
3733263
|
foreach my $unit (keys %{$self->{postprocess}}) { |
|
11703
|
|
|
|
|
53783
|
|
735
|
1512
|
|
|
|
|
7044
|
$dt->add("${unit}s" => $self->{postprocess}{$unit}); |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
11703
|
|
|
|
|
1563652
|
return $dt; |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
# solely for testing purpose |
742
|
|
|
|
|
|
|
sub _set_datetime |
743
|
|
|
|
|
|
|
{ |
744
|
7806
|
|
|
7806
|
|
34654
|
my $self = shift; |
745
|
7806
|
|
|
|
|
18858
|
my ($time, $tz) = @_; |
746
|
|
|
|
|
|
|
|
747
|
7806
|
|
100
|
|
|
59060
|
$self->{datetime_test} = DateTime->new( |
748
|
|
|
|
|
|
|
time_zone => $tz || 'floating', |
749
|
|
|
|
|
|
|
%$time, |
750
|
|
|
|
|
|
|
); |
751
|
7806
|
|
|
|
|
2985026
|
$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 |