← Index
NYTProf Performance Profile   « line view »
For -e
  Run on Thu Jun 30 16:34:56 2016
Reported on Thu Jun 30 16:35:08 2016

Filename/zpool_host_mnt/mnt/home/s1/repos/perl-DateTime-Format-Alami/lib/DateTime/Format/Alami.pm
StatementsExecuted 63784 statements in 80.2ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
424610267.9ms2.38sDateTime::Format::Alami::::CORE:matchDateTime::Format::Alami::CORE:match (opcode)
10001142.0ms2.64sDateTime::Format::Alami::::parse_datetimeDateTime::Format::Alami::parse_datetime
30001126.2ms2.32sDateTime::Format::Alami::::a_dateymdDateTime::Format::Alami::a_dateymd
30001111.4ms901msDateTime::Format::Alami::::a_todayDateTime::Format::Alami::a_today
4424.82ms13.7msDateTime::Format::Alami::::newDateTime::Format::Alami::new
3322.26ms2.27msDateTime::Format::Alami::::CORE:regcompDateTime::Format::Alami::CORE:regcomp (opcode)
1111.77ms2.14msDateTime::Format::Alami::::BEGIN@114DateTime::Format::Alami::BEGIN@114
1000111.44ms1.44msDateTime::Format::Alami::::_resetDateTime::Format::Alami::_reset
1111.25ms10.9msDateTime::Format::Alami::::BEGIN@394DateTime::Format::Alami::BEGIN@394
111746µs780µsDateTime::Format::Alami::::BEGIN@9DateTime::Format::Alami::BEGIN@9
11154µs70µsDateTime::Format::Alami::::o_monthnameDateTime::Format::Alami::o_monthname
32153µs76µsDateTime::Format::Alami::::o_durwordsDateTime::Format::Alami::o_durwords
11140µs50µsDateTime::Format::Alami::::o_dowDateTime::Format::Alami::o_dow
331136µs36µsDateTime::Format::Alami::::CORE:substcontDateTime::Format::Alami::CORE:substcont (opcode)
562135µs35µsDateTime::Format::Alami::::CORE:substDateTime::Format::Alami::CORE:subst (opcode)
11133µs33µsDateTime::Format::Alami::::BEGIN@6DateTime::Format::Alami::BEGIN@6
11125µs25µsDateTime::Format::Alami::::CORE:sortDateTime::Format::Alami::CORE:sort (opcode)
22119µs26µsDateTime::Format::Alami::::o_timedurwordsDateTime::Format::Alami::o_timedurwords
44316µs16µsDateTime::Format::Alami::::CORE:qrDateTime::Format::Alami::CORE:qr (opcode)
22115µs73µsDateTime::Format::Alami::::o_durDateTime::Format::Alami::o_dur
11112µs33µsDateTime::Format::Alami::::BEGIN@497DateTime::Format::Alami::BEGIN@497
11110µs15µsDateTime::Format::Alami::::BEGIN@7DateTime::Format::Alami::BEGIN@7
1119µs22µsDateTime::Format::Alami::::BEGIN@247DateTime::Format::Alami::BEGIN@247
1119µs116µsDateTime::Format::Alami::::BEGIN@11DateTime::Format::Alami::BEGIN@11
1119µs21µsDateTime::Format::Alami::::BEGIN@50DateTime::Format::Alami::BEGIN@50
1119µs21µsDateTime::Format::Alami::::BEGIN@160DateTime::Format::Alami::BEGIN@160
1119µs17µsDateTime::Format::Alami::::BEGIN@8DateTime::Format::Alami::BEGIN@8
1119µs24µsDateTime::Format::Alami::::o_timedurDateTime::Format::Alami::o_timedur
1118µs18µsDateTime::Format::Alami::::BEGIN@504DateTime::Format::Alami::BEGIN@504
1115µs31µsDateTime::Format::Alami::::odur_durDateTime::Format::Alami::odur_dur
1112µs2µsDateTime::Format::Alami::::pdur_durDateTime::Format::Alami::pdur_dur
1112µs2µsDateTime::Format::Alami::::o_dayintDateTime::Format::Alami::o_dayint
1112µs2µsDateTime::Format::Alami::::o_hourDateTime::Format::Alami::o_hour
1111µs1µsDateTime::Format::Alami::::o_monthintDateTime::Format::Alami::o_monthint
1111µs1µsDateTime::Format::Alami::::o_yearintDateTime::Format::Alami::o_yearint
1111µs1µsDateTime::Format::Alami::::o_minuteDateTime::Format::Alami::o_minute
1111µs1µsDateTime::Format::Alami::::o_secondDateTime::Format::Alami::o_second
0000s0sDateTime::Format::Alami::::_now_if_unsetDateTime::Format::Alami::_now_if_unset
0000s0sDateTime::Format::Alami::::_parse_durDateTime::Format::Alami::_parse_dur
0000s0sDateTime::Format::Alami::::_reset_durDateTime::Format::Alami::_reset_dur
0000s0sDateTime::Format::Alami::::_today_if_unsetDateTime::Format::Alami::_today_if_unset
0000s0sDateTime::Format::Alami::::a_date_timeDateTime::Format::Alami::a_date_time
0000s0sDateTime::Format::Alami::::a_dur_agoDateTime::Format::Alami::a_dur_ago
0000s0sDateTime::Format::Alami::::a_dur_laterDateTime::Format::Alami::a_dur_later
0000s0sDateTime::Format::Alami::::a_nowDateTime::Format::Alami::a_now
0000s0sDateTime::Format::Alami::::a_timeDateTime::Format::Alami::a_time
0000s0sDateTime::Format::Alami::::a_tomorrowDateTime::Format::Alami::a_tomorrow
0000s0sDateTime::Format::Alami::::a_which_dowDateTime::Format::Alami::a_which_dow
0000s0sDateTime::Format::Alami::::a_yesterdayDateTime::Format::Alami::a_yesterday
0000s0sDateTime::Format::Alami::::adur_durDateTime::Format::Alami::adur_dur
0000s0sDateTime::Format::Alami::::parse_datetime_durationDateTime::Format::Alami::parse_datetime_duration
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package DateTime::Format::Alami;
2
3# DATE
4# VERSION
5
6259µs133µs
# spent 33µs within DateTime::Format::Alami::BEGIN@6 which was called: # once (33µs+0s) by Role::Tiny::_load_module at line 6
use 5.014000;
# spent 33µs making 1 call to DateTime::Format::Alami::BEGIN@6
7228µs219µs
# spent 15µs (10+4) within DateTime::Format::Alami::BEGIN@7 which was called: # once (10µs+4µs) by Role::Tiny::_load_module at line 7
use strict;
# spent 15µs making 1 call to DateTime::Format::Alami::BEGIN@7 # spent 4µs making 1 call to strict::import
8229µs226µs
# spent 17µs (9+9) within DateTime::Format::Alami::BEGIN@8 which was called: # once (9µs+9µs) by Role::Tiny::_load_module at line 8
use warnings;
# spent 17µs making 1 call to DateTime::Format::Alami::BEGIN@8 # spent 9µs making 1 call to warnings::import
92768µs2815µs
# spent 780µs (746+34) within DateTime::Format::Alami::BEGIN@9 which was called: # once (746µs+34µs) by Role::Tiny::_load_module at line 9
use Log::Any::IfLOG '$log';
# spent 780µs making 1 call to DateTime::Format::Alami::BEGIN@9 # spent 34µs making 1 call to Log::Any::IfLOG::import
10
112208µs2223µs
# spent 116µs (9+107) within DateTime::Format::Alami::BEGIN@11 which was called: # once (9µs+107µs) by Role::Tiny::_load_module at line 11
use Role::Tiny;
# spent 116µs making 1 call to DateTime::Format::Alami::BEGIN@11 # spent 107µs making 1 call to Role::Tiny::import
12
1313µsmy @short_mons = qw(jan feb mar apr may jun jul aug sep oct nov dec);
1411µsmy @dow = qw(monday tuesday wednesday thursday friday saturday sunday);
15
1613µs16µsrequires 'o_num';
# spent 6µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
171900ns12µsrequires '_parse_num';
# spent 2µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
18
191900ns11µsrequires 'w_year';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
201800ns11µsrequires 'w_month';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
211800ns11µsrequires 'w_week';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
221900ns11µsrequires 'w_day';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
231800ns11µsrequires 'w_hour';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
241800ns11µsrequires 'w_minute';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
251800ns11µsrequires 'w_second';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
26
27117µs1216µsrequires "w_$_" for @short_mons;
# spent 16µs making 12 calls to Role::Tiny::__ANON__[Role/Tiny.pm:86], avg 1µs/call
28110µs78µsrequires "w_$_" for @dow;
# spent 8µs making 7 calls to Role::Tiny::__ANON__[Role/Tiny.pm:86], avg 1µs/call
29
301800ns11µsrequires 'p_now';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
311800ns11µsrequires 'p_today';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
321700ns11µsrequires 'p_yesterday';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
331800ns11µsrequires 'p_tomorrow';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
341800ns11µsrequires 'p_dateymd';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
351800ns12µsrequires 'o_date';
# spent 2µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
361800ns11µsrequires 'p_dur_ago';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
371800ns11µsrequires 'p_dur_later';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
381700ns11µsrequires 'p_which_dow';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
391700ns11µsrequires 'p_time';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
401700ns11µsrequires 'p_date_time';
# spent 1µs making 1 call to Role::Tiny::__ANON__[Role/Tiny.pm:86]
41
42our ($m, $o);
43
# spent 13.7ms (4.82+8.86) within DateTime::Format::Alami::new which was called 4 times, avg 3.42ms/call: # once (4.80ms+8.86ms) by main::BEGIN@0 at line 94 of lib/DateTime/Format/Alami/EN.pm # once (8µs+0s) by main::BEGIN@0 at line 95 of lib/DateTime/Format/Alami/EN.pm # once (8µs+0s) by main::RUNTIME at line 1 of -e # once (4µs+0s) by main::BEGIN@0 at line 96 of lib/DateTime/Format/Alami/EN.pm
sub new {
4442µs my $class = shift;
4542µs if ($class eq __PACKAGE__) {
46 die "Use one of the DateTime::Format::Alami::* instead, ".
47 "e.g. DateTime::Format::Alami::EN";
48 }
4944µs my $self = bless {}, $class;
502577µs233µs
# spent 21µs (9+12) within DateTime::Format::Alami::BEGIN@50 which was called: # once (9µs+12µs) by Role::Tiny::_load_module at line 50
no strict 'refs';
# spent 21µs making 1 call to DateTime::Format::Alami::BEGIN@50 # spent 12µs making 1 call to strict::unimport
51410µs unless (${"$class\::RE_DT"} && ${"$class\::RE_DUR"}) {
521590µs require Class::Inspector;
531565µs require Data::Graph::Util;
54
5515µs1476µs my $meths = Class::Inspector->methods($class);
# spent 476µs making 1 call to Class::Inspector::methods
561400ns my %pats; # key = "p_..."
57 my %pat_lengths; # key = "p_..."
58 my %graph;
5917µs for my $meth (@$meths) {
6078155µs7848µs next unless $meth =~ /^(odur|o|pdur|p)_/;
# spent 48µs making 78 calls to DateTime::Format::Alami::CORE:match, avg 617ns/call
612855µs28283µs my $pat = $self->$meth;
# spent 70µs making 1 call to DateTime::Format::Alami::o_monthname # spent 50µs making 1 call to DateTime::Format::Alami::o_dow # spent 47µs making 1 call to DateTime::Format::Alami::o_dur # spent 31µs making 1 call to DateTime::Format::Alami::odur_dur # spent 24µs making 1 call to DateTime::Format::Alami::o_timedur # spent 21µs making 1 call to DateTime::Format::Alami::o_durwords # spent 11µs making 1 call to DateTime::Format::Alami::o_timedurwords # spent 2µs making 1 call to DateTime::Format::Alami::EN::p_dateymd # spent 2µs making 1 call to DateTime::Format::Alami::pdur_dur # spent 2µs making 1 call to DateTime::Format::Alami::EN::o_ampm # spent 2µs making 1 call to DateTime::Format::Alami::EN::p_which_dow # spent 2µs making 1 call to DateTime::Format::Alami::o_dayint # spent 2µs making 1 call to DateTime::Format::Alami::o_hour # spent 2µs making 1 call to DateTime::Format::Alami::EN::p_date_time # spent 2µs making 1 call to DateTime::Format::Alami::EN::p_dur_ago # spent 1µs making 1 call to DateTime::Format::Alami::EN::o_cardinal_suffix # spent 1µs making 1 call to DateTime::Format::Alami::EN::p_dur_later # spent 1µs making 1 call to DateTime::Format::Alami::EN::p_now # spent 1µs making 1 call to DateTime::Format::Alami::EN::p_today # spent 1µs making 1 call to DateTime::Format::Alami::EN::p_yesterday # spent 1µs making 1 call to DateTime::Format::Alami::EN::o_date # spent 1µs making 1 call to DateTime::Format::Alami::EN::p_time # spent 1µs making 1 call to DateTime::Format::Alami::o_monthint # spent 1µs making 1 call to DateTime::Format::Alami::o_yearint # spent 1µs making 1 call to DateTime::Format::Alami::EN::p_tomorrow # spent 1µs making 1 call to DateTime::Format::Alami::o_minute # spent 1µs making 1 call to DateTime::Format::Alami::EN::o_num # spent 1µs making 1 call to DateTime::Format::Alami::o_second
622855µs2812µs my $is_p = $meth =~ /^p_/;
# spent 12µs making 28 calls to DateTime::Format::Alami::CORE:match, avg 429ns/call
632840µs285µs my $is_pdur = $meth =~ /^pdur_/;
# spent 5µs making 28 calls to DateTime::Format::Alami::CORE:match, avg 189ns/call
6478196µs6156µs $pat =~ s/<(\w+)>/push @{$graph{$meth}}, $1; "(?\&$1)"/eg;
# spent 36µs making 33 calls to DateTime::Format::Alami::CORE:substcont, avg 1µs/call # spent 20µs making 28 calls to DateTime::Format::Alami::CORE:subst, avg 721ns/call
65286µs my $action_meth = $meth;
665664µs2814µs if ($is_pdur) { $action_meth =~ s/^pdur_/adur_/ } else { $action_meth =~ s/^p_/a_/ }
# spent 14µs making 28 calls to DateTime::Format::Alami::CORE:subst, avg 518ns/call
67 #my $before_meth = $meth; $before_meth =~ s/^p_/before_p_/;
68 #$before_meth = undef unless $is_p && $self->can($before_meth);
69 $pat = join(
70 "",
71 "(",
72 #($before_meth ? "(?{ ".($ENV{DEBUG} ? "say \"invoking $before_meth()\";" : "")."\$DateTime::Format::Alami::o->$before_meth(\$DateTime::Format::Alami::m) })" : ""),
73 ($is_p || $is_pdur ? "\\b $pat \\b" : $pat), ")",
74
75 # we capture ourselves instead of relying on named capture
76 # because subpattern capture are discarded
77 "(?{ \$DateTime::Format::Alami::m->{$meth} = \$^N })",
78
792860µs ($is_p || $is_pdur ? "(?{ ".($ENV{DEBUG} ? "say \"invoking $action_meth(\$^N)\";" : "")."\$DateTime::Format::Alami::o->{_pat} = \"$meth\"; \$DateTime::Format::Alami::o->$action_meth(\$DateTime::Format::Alami::m) })" : ""),
80 );
812814µs $pats{$meth} = $pat;
822825µs $pat_lengths{$meth} = length($pat);
83 }
8415µs187µs my @pat_names_by_deps = Data::Graph::Util::toposort(\%graph);
# spent 87µs making 1 call to Data::Graph::Util::toposort
85114µs my %pat_name_dep_orders = map { $pat_names_by_deps[$_] => $_ }
86 0..$#pat_names_by_deps;
87137µs125µs my @pat_names = sort {(
# spent 25µs making 1 call to DateTime::Format::Alami::CORE:sort
88 ($pat_name_dep_orders{$a} // 9999) <=>
89 ($pat_name_dep_orders{$b} // 9999)
90 ||
91 $pat_lengths{$b} <=> $pat_lengths{$a}) } keys %pats;
9211µs my $nl = $ENV{DEBUG} ? "\n" : "";
93 my $re_dt = join(
94 "",
95 "(?&top)", $nl,
96 #"(?&p_dateymd)", $nl, # testing
97 "(?(DEFINE)", $nl,
98 "(?<top>", join("|",
992849µs288µs map {"(?&$_)"} grep {/^p_/} @pat_names), ")$nl",
# spent 8µs making 28 calls to DateTime::Format::Alami::CORE:match, avg 268ns/call
1002999µs2813µs (map { "(?<$_> $pats{$_})$nl" } grep {/^(o|p)_/} @pat_names),
# spent 13µs making 28 calls to DateTime::Format::Alami::CORE:match, avg 468ns/call
101 ")", # end of define
102 );
103 my $re_dur = join(
104 "",
105 "(?&top)", $nl,
106 #"(?&pdur_dur)", $nl, # testing
107 "(?(DEFINE)", $nl,
108 "(?<top>", join("|",
1092845µs284µs map {"(?&$_)"} grep {/^pdur_/} @pat_names), ")$nl",
# spent 4µs making 28 calls to DateTime::Format::Alami::CORE:match, avg 154ns/call
1102962µs285µs (map { "(?<$_> $pats{$_})$nl" } grep {/^(odur|pdur)_/} @pat_names),
# spent 5µs making 28 calls to DateTime::Format::Alami::CORE:match, avg 171ns/call
111 ")", # end of define
112 );
113 {
1143604µs22.16ms
# spent 2.14ms (1.77+367µs) within DateTime::Format::Alami::BEGIN@114 which was called: # once (1.77ms+367µs) by Role::Tiny::_load_module at line 114
use re 'eval';
# spent 2.14ms making 1 call to DateTime::Format::Alami::BEGIN@114 # spent 20µs making 1 call to re::import
11511.56ms21.55ms ${"$class\::RE_DT"} = qr/$re_dt/ix;
# spent 1.55ms making 1 call to DateTime::Format::Alami::CORE:regcomp # spent 1µs making 1 call to DateTime::Format::Alami::CORE:qr
# spent 63.9ms executing statements in string eval
1161248µs2247µs ${"$class\::RE_DUR"} = qr/$re_dur/ix;
# spent 246µs making 1 call to DateTime::Format::Alami::CORE:regcomp # spent 1µs making 1 call to DateTime::Format::Alami::CORE:qr
# spent 9µs executing statements in string eval
117 }
118 }
11944µs unless (${"$class\::MAPS"}) {
1201900ns my $maps = {};
121 # month names -> num
122 {
1231400ns my $i = 0;
12411µs for my $m (@short_mons) {
125121µs ++$i;
126124µs my $meth = "w_$m";
1271222µs1212µs for (@{ $self->$meth }) {
# spent 2µs making 1 call to DateTime::Format::Alami::EN::w_jan # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_feb # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_apr # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_dec # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_oct # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_aug # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_jul # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_jun # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_mar # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_nov # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_sep # spent 800ns making 1 call to DateTime::Format::Alami::EN::w_may
1282418µs $maps->{months}{$_} = $i;
129 }
130 }
131 }
132 # day-of-week names -> num (monday=1, sunday=7)
133 {
13431µs my $i = 0;
1351900ns for my $m (@dow) {
13671µs ++$i;
13773µs my $meth = "w_$m";
138713µs77µs for (@{ $self->$meth }) {
# spent 1µs making 1 call to DateTime::Format::Alami::EN::w_friday # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_saturday # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_thursday # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_tuesday # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_monday # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_sunday # spent 900ns making 1 call to DateTime::Format::Alami::EN::w_wednesday
1391410µs $maps->{dow}{$_} = $i;
140 }
141 }
142 }
14318µs ${"$class\::MAPS"} = $maps;
144 }
145
146 # _time_zone is old name (<= 0.11) will be removed later
14746µs $self->{time_zone} //= $self->{_time_zone};
148
149418µs $self;
150}
151
152
# spent 1.44ms within DateTime::Format::Alami::_reset which was called 1000 times, avg 1µs/call: # 1000 times (1.44ms+0s) by DateTime::Format::Alami::parse_datetime at line 187, avg 1µs/call
sub _reset {
1531000176µs my $self = shift;
1541000397µs undef $self->{_pat};
1551000163µs undef $self->{_dt};
15610001.63ms undef $self->{_uses_time};
157}
158
159
# spent 2.64s (42.0ms+2.60) within DateTime::Format::Alami::parse_datetime which was called 1000 times, avg 2.64ms/call: # 1000 times (42.0ms+2.60s) by main::RUNTIME at line 1 of -e, avg 2.64ms/call
sub parse_datetime {
1602603µs232µs
# spent 21µs (9+12) within DateTime::Format::Alami::BEGIN@160 which was called: # once (9µs+12µs) by Role::Tiny::_load_module at line 160
no strict 'refs';
# spent 21µs making 1 call to DateTime::Format::Alami::BEGIN@160 # spent 12µs making 1 call to strict::unimport
161
162 # we require DateTime here, for all the a_* methods
1631000822µs require DateTime;
164
1651000453µs my ($self, $str, $opts) = @_;
166
167 # allow calling as static method
1681000285µs unless (ref $self) { $self = $self->new }
169
1701000379µs $opts //= {};
1711000533µs $opts->{format} //= 'DateTime';
172 #$opts->{prefers} //= 'nearest';
1731000224µs $opts->{returns} //= 'first';
174
1751000177µs local $self->{time_zone} = $opts->{time_zone} if $opts->{time_zone};
176
177 # we need /o to avoid repeated regcomp, but we need to make it work with all
178 # subclasses, so we use eval() here.
17910001.53ms unless (defined *{ref($self).'::_code_match_dt'}) {
180153µs *{ref($self).'::_code_match_dt'} = eval "sub { \$_[0] =~ /(\$".ref($self)."::RE_DT)/go; \$1 }";
# spent 7.22ms executing statements in string eval
# includes 8.78ms spent executing 1000 calls to 1 sub defined therein.
1811500ns die if $@;
182 }
183
1841000178µs $o = $self;
1851000113µs my @res;
1861000126µs while (1) {
1871000831µs10001.44ms $o->_reset;
# spent 1.44ms making 1000 calls to DateTime::Format::Alami::_reset, avg 1µs/call
1881000949µs $m = {};
18910002.50ms10002.39s my $match = &{ref($self).'::_code_match_dt'}($str) or last;
19010001.43ms1000154ms $o->{_dt}->truncate(to=>'day') unless $o->{_uses_time};
# spent 154ms making 1000 calls to DateTime::truncate, avg 154µs/call
191 my $res = {
192 verbatim => $match,
193 pattern => $o->{_pat},
19410003.54ms pos => pos($str) - length($match),
195 m => {%$m},
196 };
1971000475µs $res->{uses_time} = $o->{_uses_time} ? 1:0;
1981000344µs $res->{DateTime} = $o->{_dt};
199 $res->{epoch} = $o->{_dt}->epoch if
2001000567µs $opts->{format} eq 'combined' || $opts->{format} eq 'epoch';
2011000394µs push @res, $res;
2021000731µs last if $opts->{returns} eq 'first';
203 }
204
2051000235µs die "Can't parse date '$str'" unless @res;
206
2071000180µs @res = ($res[-1]) if $opts->{returns} eq 'last';
208
20910004.47ms1000237µs if ($opts->{returns} =~ /\A(?:all_cron|earliest|latest)\z/) {
# spent 237µs making 1000 calls to DateTime::Format::Alami::CORE:match, avg 237ns/call
210 # sort chronologically, note that by this time the DateTime module
211 # should already have been loaded
212 @res = sort {
213 DateTime->compare($a->{DateTime}, $b->{DateTime})
214 } @res;
215 }
216
21710002.63ms if ($opts->{format} eq 'DateTime') {
218 @res = map { $_->{DateTime} } @res;
219 } elsif ($opts->{format} eq 'epoch') {
220 @res = map { $_->{epoch} } @res;
221 } elsif ($opts->{format} eq 'verbatim') {
222 @res = map { $_->{verbatim} } @res;
223 }
224
22510006.17ms20001.35ms if ($opts->{returns} =~ /\A(?:all|all_cron)\z/) {
# spent 1.35ms making 2000 calls to DateTime::Format::Alami::CORE:match, avg 675ns/call
226 return \@res;
227 } elsif ($opts->{returns} =~ /\A(?:first|earliest)\z/) {
228 return $res[0];
229 } elsif ($opts->{returns} =~ /\A(?:last|latest)\z/) {
230 return $res[-1];
231 } else {
232 die "Unknown returns option '$opts->{returns}'";
233 }
234}
235
236sub _reset_dur {
237 my $self = shift;
238 undef $self->{_pat};
239 undef $self->{_dtdur};
240}
241
242sub parse_datetime_duration {
243 # we require DateTime here, for all the adur_* methods
244 require DateTime;
245 require DateTime::Duration;
246
2472966µs234µs
# spent 22µs (9+12) within DateTime::Format::Alami::BEGIN@247 which was called: # once (9µs+12µs) by Role::Tiny::_load_module at line 247
no strict 'refs';
# spent 22µs making 1 call to DateTime::Format::Alami::BEGIN@247 # spent 12µs making 1 call to strict::unimport
248
249 my ($self, $str, $opts) = @_;
250
251 # allow calling as static method
252 unless (ref $self) { $self = $self->new }
253
254 $opts //= {};
255 $opts->{format} //= 'Duration';
256 $opts->{returns} //= 'first';
257
258 # we need /o to avoid repeated regcomp, but we need to make it work with all
259 # subclasses, so we use eval() here.
260 unless (defined *{ref($self).'::_code_match_dur'}) {
261 *{ref($self).'::_code_match_dur'} = eval "sub { \$_[0] =~ /(\$".ref($self)."::RE_DUR)/go; \$1 }";
262 die if $@;
263 }
264
265 $o = $self;
266 my @res;
267 while (1) {
268 $o->_reset_dur;
269 $m = {};
270 my $match = &{ref($self).'::_code_match_dur'}($str) or last;
271 my $res = {
272 verbatim => $match,
273 pattern => $o->{_pat},
274 pos => pos($str) - length($match),
275 m => {%$m},
276 };
277 $res->{Duration} = $o->{_dtdur};
278 if ($opts->{format} eq 'combined' || $opts->{format} eq 'seconds') {
279 my $d = $o->{_dtdur};
280 $res->{seconds} =
281 $d->years * 365.25*86400 +
282 $d->months * 30.4375*86400 +
283 $d->weeks * 7*86400 +
284 $d->days * 86400 +
285 $d->hours * 3600 +
286 $d->minutes * 60 +
287 $d->seconds +
288 $d->nanoseconds * 1e-9;
289 }
290 push @res, $res;
291 last if $opts->{returns} eq 'first';
292 }
293
294 die "Can't parse duration" unless @res;
295
296 @res = ($res[-1]) if $opts->{returns} eq 'last';
297
298 # XXX support returns largest, smallest, all_sorted
299 if ($opts->{returns} =~ /\A(?:all_sorted|largest|smallest)\z/) {
300 my $base_dt = DateTime->now;
301 # sort from smallest to largest
302 @res = sort {
303 DateTime::Duration->compare($a->{Duration}, $b->{Duration}, $base_dt)
304 } @res;
305 }
306
307 if ($opts->{format} eq 'Duration') {
308 @res = map { $_->{Duration} } @res;
309 } elsif ($opts->{format} eq 'seconds') {
310 @res = map { $_->{seconds} } @res;
311 } elsif ($opts->{format} eq 'verbatim') {
312 @res = map { $_->{verbatim} } @res;
313 }
314
315 if ($opts->{returns} =~ /\A(?:all|all_sorted)\z/) {
316 return \@res;
317 } elsif ($opts->{returns} =~ /\A(?:first|smallest)\z/) {
318 return $res[0];
319 } elsif ($opts->{returns} =~ /\A(?:last|largest)\z/) {
320 return $res[-1];
321 } else {
322 die "Unknown returns option '$opts->{returns}'";
323 }
324}
325
32613µs
# spent 2µs within DateTime::Format::Alami::o_dayint which was called: # once (2µs+0s) by DateTime::Format::Alami::new at line 61
sub o_dayint { "(?:[12][0-9]|3[01]|0?[1-9])" }
327
32813µs
# spent 1µs within DateTime::Format::Alami::o_monthint which was called: # once (1µs+0s) by DateTime::Format::Alami::new at line 61
sub o_monthint { "(?:0?[1-9]|1[012])" }
329
33013µs
# spent 1µs within DateTime::Format::Alami::o_yearint which was called: # once (1µs+0s) by DateTime::Format::Alami::new at line 61
sub o_yearint { "(?:[0-9]{4}|[0-9]{2})" }
331
33213µs
# spent 2µs within DateTime::Format::Alami::o_hour which was called: # once (2µs+0s) by DateTime::Format::Alami::new at line 61
sub o_hour { "(?:[0-9][0-9]?)" }
333
33413µs
# spent 1µs within DateTime::Format::Alami::o_minute which was called: # once (1µs+0s) by DateTime::Format::Alami::new at line 61
sub o_minute { "(?:[0-9][0-9]?)" }
335
33613µs
# spent 1µs within DateTime::Format::Alami::o_second which was called: # once (1µs+0s) by DateTime::Format::Alami::new at line 61
sub o_second { "(?:[0-9][0-9]?)" }
337
338
# spent 70µs (54+16) within DateTime::Format::Alami::o_monthname which was called: # once (54µs+16µs) by DateTime::Format::Alami::new at line 61
sub o_monthname {
3391500ns my $self = shift;
340 "(?:" . join(
341 "|",
3422534µs1216µs (map {my $meth="w_$_"; @{ $self->$meth }} @short_mons)
# spent 2µs making 1 call to DateTime::Format::Alami::EN::w_jan # spent 2µs making 1 call to DateTime::Format::Alami::EN::w_aug # spent 2µs making 1 call to DateTime::Format::Alami::EN::w_mar # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_feb # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_jun # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_dec # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_apr # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_may # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_oct # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_sep # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_jul # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_nov
343 ) . ")";
344}
345
346
# spent 50µs (40+10) within DateTime::Format::Alami::o_dow which was called: # once (40µs+10µs) by DateTime::Format::Alami::new at line 61
sub o_dow {
3471500ns my $self = shift;
348 "(?:" . join(
349 "|",
3501526µs710µs (map {my $meth="w_$_"; @{ $self->$meth }} @dow)
# spent 2µs making 1 call to DateTime::Format::Alami::EN::w_monday # spent 2µs making 1 call to DateTime::Format::Alami::EN::w_tuesday # spent 2µs making 1 call to DateTime::Format::Alami::EN::w_thursday # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_wednesday # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_friday # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_saturday # spent 1µs making 1 call to DateTime::Format::Alami::EN::w_sunday
351 ) . ")";
352}
353
354
# spent 76µs (53+23) within DateTime::Format::Alami::o_durwords which was called 3 times, avg 26µs/call: # 2 times (38µs+17µs) by DateTime::Format::Alami::o_dur at line 366, avg 28µs/call # once (15µs+6µs) by DateTime::Format::Alami::new at line 61
sub o_durwords {
3553700ns my $self = shift;
356 "(?:" . join(
357 "|",
358 @{ $self->w_year }, @{ $self->w_month }, @{ $self->w_week },
359 @{ $self->w_day },
360332µs2123µs @{ $self->w_hour }, @{ $self->w_minute }, @{ $self->w_second },
# spent 4µs making 3 calls to DateTime::Format::Alami::EN::w_month, avg 1µs/call # spent 4µs making 3 calls to DateTime::Format::Alami::EN::w_week, avg 1µs/call # spent 4µs making 3 calls to DateTime::Format::Alami::EN::w_year, avg 1µs/call # spent 3µs making 3 calls to DateTime::Format::Alami::EN::w_second, avg 1µs/call # spent 3µs making 3 calls to DateTime::Format::Alami::EN::w_minute, avg 1µs/call # spent 3µs making 3 calls to DateTime::Format::Alami::EN::w_hour, avg 1µs/call # spent 3µs making 3 calls to DateTime::Format::Alami::EN::w_day, avg 967ns/call
361 ) . ")";
362}
363
364
# spent 73µs (15+58) within DateTime::Format::Alami::o_dur which was called 2 times, avg 37µs/call: # once (10µs+37µs) by DateTime::Format::Alami::new at line 61 # once (5µs+21µs) by DateTime::Format::Alami::odur_dur at line 371
sub o_dur {
3652500ns my $self = shift;
366213µs458µs "(?:(" . $self->o_num . "\\s*" . $self->o_durwords . "\\s*(?:,\\s*)?)+)";
# spent 56µs making 2 calls to DateTime::Format::Alami::o_durwords, avg 28µs/call # spent 2µs making 2 calls to DateTime::Format::Alami::EN::o_num, avg 1µs/call
367}
368
369
# spent 31µs (5+26) within DateTime::Format::Alami::odur_dur which was called: # once (5µs+26µs) by DateTime::Format::Alami::new at line 61
sub odur_dur {
3701500ns my $self = shift;
37113µs126µs $self->o_dur;
# spent 26µs making 1 call to DateTime::Format::Alami::o_dur
372}
373
374
# spent 2µs within DateTime::Format::Alami::pdur_dur which was called: # once (2µs+0s) by DateTime::Format::Alami::new at line 61
sub pdur_dur {
3751500ns my $self = shift;
37613µs "(?:<odur_dur>)";
377}
378
379# durations less than a day
380
# spent 26µs (19+6) within DateTime::Format::Alami::o_timedurwords which was called 2 times, avg 13µs/call: # once (12µs+3µs) by DateTime::Format::Alami::o_timedur at line 390 # once (8µs+3µs) by DateTime::Format::Alami::new at line 61
sub o_timedurwords {
3812800ns my $self = shift;
382 "(?:" . join(
383 "|",
384213µs66µs @{ $self->w_hour }, @{ $self->w_minute }, @{ $self->w_second },
# spent 2µs making 2 calls to DateTime::Format::Alami::EN::w_second, avg 1µs/call # spent 2µs making 2 calls to DateTime::Format::Alami::EN::w_hour, avg 1µs/call # spent 2µs making 2 calls to DateTime::Format::Alami::EN::w_minute, avg 950ns/call
385 ) . ")";
386}
387
388
# spent 24µs (9+15) within DateTime::Format::Alami::o_timedur which was called: # once (9µs+15µs) by DateTime::Format::Alami::new at line 61
sub o_timedur {
3891400ns my $self = shift;
39016µs216µs "(?:(" . $self->o_num . "\\s*" . $self->o_timedurwords . "\\s*(?:,\\s*)?)+)";
# spent 15µs making 1 call to DateTime::Format::Alami::o_timedurwords # spent 700ns making 1 call to DateTime::Format::Alami::EN::o_num
391}
392
393sub _parse_dur {
39421.09ms211.0ms
# spent 10.9ms (1.25+9.69) within DateTime::Format::Alami::BEGIN@394 which was called: # once (1.25ms+9.69ms) by Role::Tiny::_load_module at line 394
use experimental 'smartmatch';
# spent 10.9ms making 1 call to DateTime::Format::Alami::BEGIN@394 # spent 82µs making 1 call to experimental::import
395
396 my ($self, $str) = @_;
397
398 #say "D:dur=$str";
399 my %args;
400 unless ($self->{_cache_re_parse_dur}) {
401 my $o_num = $self->o_num;
402 my $o_dw = $self->o_durwords;
403 $self->{_cache_re_parse_dur} = qr/($o_num)\s*($o_dw)/ix;
404 }
405 unless ($self->{_cache_w_second}) {
406 $self->{_cache_w_second} = $self->w_second;
407 $self->{_cache_w_minute} = $self->w_minute;
408 $self->{_cache_w_hour} = $self->w_hour;
409 $self->{_cache_w_day} = $self->w_day;
410 $self->{_cache_w_week} = $self->w_week;
411 $self->{_cache_w_month} = $self->w_month;
412 $self->{_cache_w_year} = $self->w_year;
413 }
414 while ($str =~ /$self->{_cache_re_parse_dur}/g) {
415 my ($n, $unit) = ($1, $2);
416 $n = $self->_parse_num($n);
417 if ($unit ~~ $self->{_cache_w_second}) {
418 $args{seconds} = $n;
419 $self->{_uses_time} = 1;
420 } elsif ($unit ~~ $self->{_cache_w_minute}) {
421 $args{minutes} = $n;
422 $self->{_uses_time} = 1;
423 } elsif ($unit ~~ $self->{_cache_w_hour}) {
424 $args{hours} = $n;
425 $self->{_uses_time} = 1;
426 } elsif ($unit ~~ $self->{_cache_w_day}) {
427 $args{days} = $n;
428 } elsif ($unit ~~ $self->{_cache_w_week}) {
429 $args{weeks} = $n;
430 } elsif ($unit ~~ $self->{_cache_w_month}) {
431 $args{months} = $n;
432 } elsif ($unit ~~ $self->{_cache_w_year}) {
433 $args{years} = $n;
434 }
435 }
436 DateTime::Duration->new(%args);
437}
438
439sub _now_if_unset {
440 my $self = shift;
441 $self->a_now unless $self->{_dt};
442}
443
444sub _today_if_unset {
445 my $self = shift;
446 $self->a_today unless $self->{_dt};
447}
448
449sub a_now {
450 my $self = shift;
451 $self->{_dt} = DateTime->now(
452 (time_zone => $self->{time_zone}) x !!defined($self->{time_zone}),
453 );
454 $self->{_uses_time} = 1;
455}
456
457
# spent 901ms (11.4+890) within DateTime::Format::Alami::a_today which was called 3000 times, avg 300µs/call: # 3000 times (11.4ms+890ms) by DateTime::Format::Alami::a_dateymd at line 479, avg 300µs/call
sub a_today {
4583000533µs my $self = shift;
459 $self->{_dt} = DateTime->today(
46030006.95ms3000890ms (time_zone => $self->{time_zone}) x !!defined($self->{time_zone}),
# spent 890ms making 3000 calls to DateTime::today, avg 297µs/call
461 );
46230003.95ms $self->{_uses_time} = 0;
463}
464
465sub a_yesterday {
466 my $self = shift;
467 $self->a_today;
468 $self->{_dt}->subtract(days => 1);
469}
470
471sub a_tomorrow {
472 my $self = shift;
473 $self->a_today;
474 $self->{_dt}->add(days => 1);
475}
476
477
# spent 2.32s (26.2ms+2.29) within DateTime::Format::Alami::a_dateymd which was called 3000 times, avg 772µs/call: # 3000 times (26.2ms+2.29s) by DateTime::Format::Alami::CORE:match at line 1 of (eval 6)[lib/DateTime/Format/Alami.pm:115], avg 772µs/call
sub a_dateymd {
4783000998µs my ($self, $m) = @_;
47930002.18ms3000901ms $self->a_today;
# spent 901ms making 3000 calls to DateTime::Format::Alami::a_today, avg 300µs/call
4803000811µs if (defined $m->{o_yearint}) {
481 my $year;
482 if (length($m->{o_yearint}) == 2) {
483 my $start_of_century_year = int($self->{_dt}->year / 100) * 100;
484 $year = $start_of_century_year + $m->{o_yearint};
485 } else {
486 $year = $m->{o_yearint};
487 }
488 $self->{_dt}->set_year($year);
489 }
49030003.67ms3000698ms if (defined $m->{o_dayint}) {
# spent 698ms making 3000 calls to DateTime::set_day, avg 233µs/call
491 $self->{_dt}->set_day($m->{o_dayint});
492 }
49330001.00ms if (defined $m->{o_monthint}) {
494 $self->{_dt}->set_month($m->{o_monthint});
495 }
49630007.19ms if (defined $m->{o_monthname}) {
497289µs254µs
# spent 33µs (12+21) within DateTime::Format::Alami::BEGIN@497 which was called: # once (12µs+21µs) by Role::Tiny::_load_module at line 497
no strict 'refs';
# spent 33µs making 1 call to DateTime::Format::Alami::BEGIN@497 # spent 21µs making 1 call to strict::unimport
49830004.74ms my $maps = ${ ref($self) . '::MAPS' };
49930005.64ms3000691ms $self->{_dt}->set_month($maps->{months}{lc $m->{o_monthname}});
# spent 691ms making 3000 calls to DateTime::set_month, avg 230µs/call
500 }
501}
502
503sub a_which_dow {
5042502µs227µs
# spent 18µs (8+10) within DateTime::Format::Alami::BEGIN@504 which was called: # once (8µs+10µs) by Role::Tiny::_load_module at line 504
no strict 'refs';
# spent 18µs making 1 call to DateTime::Format::Alami::BEGIN@504 # spent 10µs making 1 call to strict::unimport
505
506 my ($self, $m) = @_;
507 $self->a_today;
508 my $dow_num = $self->{_dt}->day_of_week;
509
510 my $maps = ${ ref($self) . '::MAPS' };
511 my $wanted_dow_num = $maps->{dow}{lc $m->{o_dow} };
512
513 $self->{_dt}->add(days => ($wanted_dow_num-$dow_num));
514
515 if ($m->{offset}) {
516 $self->{_dt}->add(days => (7*$m->{offset}));
517 }
518}
519
520sub adur_dur {
521 my ($self, $m) = @_;
522 $self->{_dtdur} = $self->_parse_dur($m->{odur_dur});
523}
524
525sub a_dur_ago {
526 my ($self, $m) = @_;
527 $self->a_now;
528 my $dur = $self->_parse_dur($m->{o_dur});
529 $self->{_dt}->subtract_duration($dur);
530}
531
532sub a_dur_later {
533 my ($self, $m) = @_;
534 $self->a_now;
535 my $dur = $self->_parse_dur($m->{o_dur});
536 $self->{_dt}->add_duration($dur);
537}
538
539sub a_time {
540 my ($self, $m) = @_;
541 $self->_now_if_unset;
542 $self->{_uses_time} = 1;
543 my $hour = $m->{o_hour};
544 if ($m->{o_ampm}) {
545 $hour += 12 if lc($m->{o_ampm}) eq 'pm' && $hour < 12;
546 $hour = 0 if lc($m->{o_ampm}) eq 'am' && $hour == 12;
547 }
548 $self->{_dt}->set_hour($hour);
549 $self->{_dt}->set_minute($m->{o_minute});
550 $self->{_dt}->set_second($m->{o_second} // 0);
551}
552
553sub a_date_time {
554 my ($self, $m) = @_;
555}
556
557112µs1;
558# ABSTRACT: Parse human date/time expression (base class)
559
560=for Pod::Coverage ^((adur|a|pdur|p|odur|o|w)_.+)$
561
562=head1 SYNOPSIS
563
564For English:
565
566 use DateTime::Format::Alami::EN;
567 my $parser = DateTime::Format::Alami::EN->new();
568 my $dt = $parser->parse_datetime("2 hours 13 minutes from now");
569
570Or you can also call as class method:
571
572 my $dt = DateTime::Format::Alami::EN->parse_datetime("yesterday");
573
574To parse duration:
575
576 my $dtdur = DateTime::Format::Alami::EN->parse_datetime_duration("2h"); # 2 hours
577
578For Indonesian:
579
580 use DateTime::Format::Alami::ID;
581 my $parser = DateTime::Format::Alami::ID->new();
582 my $dt = $parser->parse_datetime("5 jam lagi");
583
584Or you can also call as class method:
585
586 my $dt = DateTime::Format::Alami::ID->parse_datetime("hari ini");
587
588To parse duration:
589
590 my $dtdur = DateTime::Format::Alami::ID->parse_datetime_duration("2h"); # 2 days
591
592
593=head1 DESCRIPTION
594
595This class parses human/natural date/time/duration string and returns
596L<DateTime> (or L<DateTime::Duration>) object. Currently it supports English and
597Indonesian. The goal of this module is to make it easier to add support for
598other human languages.
599
600To actually use this class, you must use one of its subclasses for each
601human language that you want to parse.
602
603There are already some other DateTime human language parsers on CPAN and
604elsewhere, see L</"SEE ALSO">.
605
606
607=head1 HOW IT WORKS
608
609L<DateTime::Format::Alami> is base class. Each human language is implemented in
610a separate C<< DateTime::Format::Alami::<ISO_CODE> >> module (e.g.
611L<DateTime::Format::Alami::EN> and L<DateTime::Format::Alami::EN>) which is a
612subclass.
613
614Parsing is done using a single recursive regex (i.e. containing C<(?&NAME)> and
615C<(?(DEFINE))> patterns, see L<perlre>). This regex is composed from pieces of
616pattern strings in the C<p_*> and C<o_*> methods, to make it easier to override
617in an OO-fashion.
618
619A pattern string that is returned by the C<p_*> method is a normal regex pattern
620string that will be compiled using the /x and /i regex modifier. The pattern
621string can also refer to pattern in other C<o_*> or C<p_*> method using syntax
622C<< <o_foo> >> or C<< <p_foo> >>. Example, C<o_today> for English might be
623something like:
624
625 sub p_today { "(?: today | this \s+ day )" }
626
627Other examples:
628
629 sub p_yesterday { "(?: yesterday )" }
630
631 sub p_dateymd { join(
632 "",
633 '(?: <o_dayint> \\s* ?<o_monthname> | <o_monthname> \\s* <o_dayint>\\b|<o_monthint>[ /-]<o_dayint>\\b )',
634 '(?: \\s*[,/-]?\\s* <o_yearint>)?'
635 )}
636
637 sub o_date { "(?: <p_today>|<p_yesterday>|<p_dateymd>)" }
638
639 sub p_time { "(?: <o_hour>:<o_minute>(?:<o_second>)? \s* <o_ampm> )" }
640
641 sub p_date_time { "(?: <o_date> (?:\s+ at)? <o_time> )" }
642
643When a pattern from C<p_*> matches, a corresponding action method C<a_*> will be
644invoked. Usually the method will set or modify a DateTime object in C<<
645$self->{_dt} >>. For example, this is code for C<a_today>:
646
647 sub a_today {
648 my $self = shift;
649 $self->{_dt} = DateTime->today;
650 }
651
652The patterns from all C<p_*> methods will be combined in an alternation to form
653the final pattern.
654
655An C<o_*> pattern is just like C<p_*>, but they will not be
656combined into the final pattern and matching it won't execute a corresponding
657C<a_*> method.
658
659And there are also C<w_*> methods which return array of strings.
660
661Parsing duration is similar, except the method names are C<pdur_*>, C<odur_*>
662and C<adur_*>.
663
664
665=head1 ADDING A NEW HUMAN LANGUAGE
666
667See an example in existing C<DateTime::Format::Alami::*> module. Basically you
668just need to supply the necessary patterns in the C<p_*> methods. If you want to
669introduce new C<p_*> method, don't forget to supply the action too in the C<a_*>
670method.
671
672
673=head1 METHODS
674
675=head2 new => obj
676
677Constructor. You actually must instantiate subclass instead.
678
679=head2 parse_datetime($str[ , \%opts ]) => obj
680
681Parse/extract date/time expression in C<$str>. Die if expression cannot be
682parsed. Otherwise return L<DateTime> object (or string/number if C<format>
683option is C<verbatim>/C<epoch>, or hash if C<format> option is C<combined>) or
684array of objects/strings/numbers (if C<returns> option is C<all>/C<all_cron>).
685
686Known options:
687
688=over
689
690=item * time_zone => str
691
692Will be passed to DateTime constructor.
693
694=item * format => str (DateTime|verbatim|epoch|combined)
695
696The default is C<DateTime>, which will return DateTime object. Other choices
697include C<verbatim> (returns the original text), C<epoch> (returns Unix
698timestamp), C<combined> (returns a hash containing keys like C<DateTime>,
699C<verbatim>, C<epoch>, and other extra information: C<pos> [position of pattern
700in the string], C<pattern> [pattern name], C<m> [raw named capture groups],
701C<uses_time> [whether the date involves time of day]).
702
703You might think that choosing C<epoch> or C<verbatim> could avoid the overhead
704of DateTime, but actually you can't since DateTime is used as the primary format
705during parsing. The epoch is retrieved from the DateTime object using the
706C<epoch> method.
707
708=item * prefers => str (nearest|future|past)
709
710NOT YET IMPLEMENTED.
711
712This option decides what happens when an ambiguous date appears in the input.
713For example, "Friday" may refer to any number of Fridays. Possible choices are:
714C<nearest> (prefer the nearest date, the default), C<future> (prefer the closest
715future date), C<past> (prefer the closest past date).
716
717=item * returns => str (first|last|earliest|latest|all|all_cron)
718
719If the text has multiple possible dates, then this argument determines which
720date will be returned. Possible choices are: C<first> (return the first date
721found in the string, the default), C<last> (return the final date found in the
722string), C<earliest> (return the date found in the string that chronologically
723precedes any other date in the string), C<latest> (return the date found in the
724string that chronologically follows any other date in the string), C<all>
725(return all dates found in the string, in the order they were found in the
726string), C<all_cron> (return all dates found in the string, in chronological
727order).
728
729When C<all> or C<all_cron> is chosen, function will return array(ref) of results
730instead of a single result, even if there is only a single actual result.
731
732=back
733
734=head2 parse_datetime_duration($str[ , \%opts ]) => obj
735
736Parse/extract duration expression in C<$str>. Die if expression cannot be
737parsed. Otherwise return L<DateTime::Duration> object (or string/number if
738C<format> option is C<verbatim>/C<seconds>, or hash if C<format> option is
739C<combined>) or array of objects/strings/numbers (if C<returns> option is
740C<all>/C<all_sorted>).
741
742Known options:
743
744=over
745
746=item * format => str (Duration|verbatim|seconds|combined)
747
748The default is C<Duration>, which will return DateTime::Duration object. Other
749choices include C<verbatim> (returns the original text), C<seconds> (returns
750number of seconds, approximated), C<combined> (returns a hash containing keys
751like C<Duration>, C<verbatim>, C<seconds>, and other extra information: C<pos>
752[position of pattern in the string], C<pattern> [pattern name], C<m> [raw named
753capture groups]).
754
755You might think that choosing C<seconds> or C<verbatim> could avoid the overhead
756of DateTime::Duration, but actually you can't since DateTime::Duration is used
757as the primary format during parsing. The number of seconds is calculated from
758the DateTime::Duration object I<using an approximation> (for example, "1 month"
759does not convert exactly to seconds).
760
761=item * returns => str (first|last|smallest|largest|all|all_sorted)
762
763If the text has multiple possible durations, then this argument determines which
764date will be returned. Possible choices are: C<first> (return the first duration
765found in the string, the default), C<last> (return the final duration found in
766the string), C<smallest> (return the smallest duration), C<largest> (return the
767largest duration), C<all> (return all durations found in the string, in the
768order they were found in the string), C<all_sorted> (return all durations found
769in the string, in smallest-to-largest order).
770
771When C<all> or C<all_sorted> is chosen, function will return array(ref) of
772results instead of a single result, even if there is only a single actual
773result.
774
775=back
776
777
778=head1 FAQ
779
780=head2 What does "alami" mean?
781
782It is an Indonesian word, meaning "natural".
783
784=head2 How does it compare to similar modules?
785
786L<DateTime::Format::Natural> (DF:Natural) is a more established module (first
787released on 2006) and can understand a bit more English expression like 'last
788day of Sep'. Aside from English, it does not yet support other languages.
789
790DFA:EN's C<parse_datetime_duration()> produces a L<DateTime::Duration> object
791while DF:Natural's C<parse_datetime_duration()> returns two L<DateTime> objects
792instead. In other words, DF:Natural can parse "from 23 Jun to 29 Jun" in
793addition to "for 2 weeks".
794
795DF:Natural in general is slightly more strict about the formats it accepts, e.g.
796it rejects C<Jun 23st> (the error message even gives hints that the suffix must
797be 'rd'). DF:Natural can give a detailed error message on why parsing has failed
798(see its C<error()> method).
799
800L<DateTime::Format::Flexible> (DF:Flexible) is another established module (first
801released in 2007) that, aside from parsing human expression (like 'tomorrow',
802'sep 1st') can also parse date/time in several other formats like RFC 822,
803making it a convenient module to use as a 'one-stop' solution to parse date.
804Compared to DF:Natural, it has better support for timezone but cannot parse some
805English expressions. Aside from English, it currently supports German and
806Spanish. It does not support parsing duration expression. Performance-wise, it
807is the slowest (see L<Bencher::Scenario::DateTimeFormatAlami::Parsing>).
808
809This module itself: B<DateTime::Format::Alami> (DF:Alami) is yet another
810implementation. Internally, it uses recursive regex to make parsing simpler and
811adding more languages easier. It requires perl 5.14.0 or newer due to the use of
812C<(?{ ... })> code blocks inside regular expression (while DF:Natural and
813DF:Flexible can run on perl 5.8+). It currently supports English and Indonesian.
814It supports parsing duration expression and returns DateTime::Duration object.
815It has the smallest startup time (see see
816L<Bencher::Scenario::DateTimeFormatAlami::Startup>).
817
818
819=head1 SEE ALSO
820
821=head2 Similar modules on CPAN
822
823L<Date::Extract>. DateTime::Format::Alami has some features of Date::Extract so
824it can be used to replace Date::Extract.
825
826L<DateTime::Format::Flexible>. See L</"FAQ">.
827
828For Indonesian: L<DateTime::Format::Indonesian>, L<Date::Extract::ID> (currently
829this module uses DateTime::Format::Alami::ID as its backend).
830
831For English: L<DateTime::Format::Natural>. See L</"FAQ">.
832
833
834=head2 Other modules on CPAN
835
836L<DateTime::Format::Human> deals with formatting and not parsing.
837
838=head2 Similar non-Perl libraries
839
840Natt Java library, which the last time I tried sometimes gives weird answer,
841e.g. "32 Oct" becomes 1 Oct in the far future. http://natty.joestelmach.com/
842
843Duckling Clojure library, which can parse date/time as well as numbers with some
844other units like temperature. https://github.com/wit-ai/duckling
845
846=cut
 
# spent 2.38s (67.9ms+2.32) within DateTime::Format::Alami::CORE:match which was called 4246 times, avg 562µs/call: # 2000 times (1.35ms+0s) by DateTime::Format::Alami::parse_datetime at line 225, avg 675ns/call # 1000 times (66.2ms+2.32s) by DateTime::Format::Alami::__ANON__[(eval 12)[lib/DateTime/Format/Alami.pm:180]:1] at line 1 of (eval 12)[lib/DateTime/Format/Alami.pm:180], avg 2.38ms/call # 1000 times (237µs+0s) by DateTime::Format::Alami::parse_datetime at line 209, avg 237ns/call # 78 times (48µs+0s) by DateTime::Format::Alami::new at line 60, avg 617ns/call # 28 times (13µs+0s) by DateTime::Format::Alami::new at line 100, avg 468ns/call # 28 times (12µs+0s) by DateTime::Format::Alami::new at line 62, avg 429ns/call # 28 times (8µs+0s) by DateTime::Format::Alami::new at line 99, avg 268ns/call # 28 times (5µs+0s) by DateTime::Format::Alami::new at line 63, avg 189ns/call # 28 times (5µs+0s) by DateTime::Format::Alami::new at line 110, avg 171ns/call # 28 times (4µs+0s) by DateTime::Format::Alami::new at line 109, avg 154ns/call
sub DateTime::Format::Alami::CORE:match; # opcode
# spent 16µs within DateTime::Format::Alami::CORE:qr which was called 4 times, avg 4µs/call: # once (11µs+0s) by DateTime::Format::Alami::CORE:regcomp at line 1 of (eval 6)[lib/DateTime/Format/Alami.pm:115] # once (3µs+0s) by DateTime::Format::Alami::CORE:regcomp at line 1 of (eval 7)[lib/DateTime/Format/Alami.pm:116] # once (1µs+0s) by DateTime::Format::Alami::new at line 115 # once (1µs+0s) by DateTime::Format::Alami::new at line 116
sub DateTime::Format::Alami::CORE:qr; # opcode
# spent 2.27ms (2.26+13µs) within DateTime::Format::Alami::CORE:regcomp which was called 3 times, avg 757µs/call: # once (1.54ms+11µs) by DateTime::Format::Alami::new at line 115 # once (476µs+0s) by DateTime::Format::Alami::__ANON__[(eval 12)[lib/DateTime/Format/Alami.pm:180]:1] at line 1 of (eval 12)[lib/DateTime/Format/Alami.pm:180] # once (244µs+3µs) by DateTime::Format::Alami::new at line 116
sub DateTime::Format::Alami::CORE:regcomp; # opcode
# spent 25µs within DateTime::Format::Alami::CORE:sort which was called: # once (25µs+0s) by DateTime::Format::Alami::new at line 87
sub DateTime::Format::Alami::CORE:sort; # opcode
# spent 35µs within DateTime::Format::Alami::CORE:subst which was called 56 times, avg 620ns/call: # 28 times (20µs+0s) by DateTime::Format::Alami::new at line 64, avg 721ns/call # 28 times (14µs+0s) by DateTime::Format::Alami::new at line 66, avg 518ns/call
sub DateTime::Format::Alami::CORE:subst; # opcode
# spent 36µs within DateTime::Format::Alami::CORE:substcont which was called 33 times, avg 1µs/call: # 33 times (36µs+0s) by DateTime::Format::Alami::new at line 64, avg 1µs/call
sub DateTime::Format::Alami::CORE:substcont; # opcode