File Coverage

blib/lib/DateTime/Format/Natural/Formatted.pm
Criterion Covered Total %
statement 115 124 92.7
branch 30 34 88.2
condition 18 30 60.0
subroutine 11 11 100.0
pod n/a
total 174 199 87.4


line stmt bran cond sub pod time code
1             package DateTime::Format::Natural::Formatted;
2              
3 26     26   222 use strict;
  26         80  
  26         784  
4 26     26   167 use warnings;
  26         58  
  26         8898  
5 26     26   170 use boolean qw(true false);
  26         64  
  26         161  
6              
7             our $VERSION = '0.12';
8              
9             sub _parse_formatted_ymd
10             {
11 271     271   496 my $self = shift;
12 271         580 my ($date_string, $count) = @_;
13              
14 271         746 my $date = $self->_split_formatted($date_string);
15              
16 271         824 my $date_sep = quotemeta((keys %$count)[0]);
17 271         1299 my @date_chunks = split /$date_sep/, $date;
18              
19 271 100 66     1124 if ($date_chunks[1] =~ /^[a-zA-Z]+$/
20 84         602 and my ($month_abbrev) = grep { $date_chunks[1] =~ /^${_}$/i } keys %{$self->{data}->{months_abbrev}}
  7         46  
21             ) {
22 7         43 my ($months_abbrev, $months) = map $self->{data}->{$_}, qw(months_abbrev months);
23 7         42 $date_chunks[1] = sprintf '%02d', $months->{$months_abbrev->{$month_abbrev}};
24             }
25              
26 271         525 my $i = 0;
27 271         632 my %chunks_length = map { length $_ => $i++ } @date_chunks;
  813         2224  
28              
29 271         814 my $format = lc $self->{Format};
30 271         440 my $format_sep;
31              
32 271         779 my $lax = false;
33              
34 271 100 66     1228 if (exists $chunks_length{4}) {
    100          
35             $format = join $date_sep,
36 253 100       1042 ($chunks_length{4} == 0
    100          
37             ? qw(yyyy mm dd)
38             : ($format =~ /^m/
39             ? qw(mm dd yyyy)
40             : qw(dd mm yyyy)
41             )
42             );
43 253         607 $lax = true;
44             }
45             elsif ($date_sep =~ /^\\[-.]$/ && $format !~ /$date_sep/) {
46 12         40 $format = join $date_sep, qw(dd mm yy);
47 12         31 $lax = true;
48             }
49             else {
50 6         14 $format_sep = do { local $_ = $format;
  6         14  
51 6         14 tr/a-zA-Z//d;
52 6         13 tr/a-zA-Z//cs;
53 6         13 quotemeta; };
54             }
55 271   66     1716 $format_sep ||= $date_sep;
56              
57 271 50 66     873 if (!$lax && $format_sep ne $date_sep) {
58 0         0 $self->_set_failure;
59 0         0 $self->_set_error("(mismatch between format and date separator)");
60 0         0 return $self->_get_datetime_object;
61             }
62              
63 271         3931 my @format_order = split /$format_sep/, $format;
64              
65 271         490 my ($d, $m, $y) = do {
66 271         489 my %f = map { substr($_, 0, 1) => true } @format_order;
  813         2727  
67 271         1996 ($f{d}, $f{m}, $f{y});
68             };
69 271 50 33     1107 unless (@format_order == 3 and $d && $m && $y) {
      33        
      33        
70 0         0 $self->_set_failure;
71 0         0 $self->_set_error("('format' parameter invalid)");
72 0         0 return $self->_get_datetime_object;
73             }
74              
75 271         6908 $i = 0;
76 271         567 my %format_index = map { substr($_, 0, 1) => $i++ } @format_order;
  813         1929  
77              
78             my $century = $self->{datetime}
79 271 50       1182 ? int($self->{datetime}->year / 100)
80             : substr((localtime)[5] + 1900, 0, 2);
81              
82 271         4085 my ($day, $month, $year) = map $date_chunks[$format_index{$_}], qw(d m y);
83              
84 271 100       818 if (length $year == 2) { $year = "$century$year" };
  18         52  
85              
86 271 100       1006 unless ($self->_check_date($year, $month, $day)) {
87 1         8 $self->_set_failure;
88 1         7 $self->_set_error("(invalid date)");
89 1         4 return $self->_get_datetime_object;
90             }
91              
92             $self->_set(
93 270         1267 year => $year,
94             month => $month,
95             day => $day,
96             );
97 270         1199 $self->{datetime}->truncate(to => 'day');
98 270         69442 $self->_set_truncated;
99 270         1429 $self->_set_valid_exp;
100              
101 270         1397 $self->_process_tokens;
102              
103 270         5116 return undef;
104             }
105              
106             sub _parse_formatted_md
107             {
108 193     193   381 my $self = shift;
109 193         475 my ($date_string) = @_;
110              
111 193         528 my $date = $self->_split_formatted($date_string);
112              
113 193         641 my @date_chunks = split /\//, $date;
114              
115             my $format = $self->{Format} =~ m{^[dm]{1,2}/[dm]{1,2}$}i
116 193 100       894 ? do { local $_ = lc $self->{Format}; tr/dm//s; $_ }
  192         597  
  192         429  
  192         485  
117             : undef;
118              
119 193 100 66     1153 unless (defined $format && $format =~ m{^(?:(?:m/d)|(?:d/m))$}) {
120 1         11 $self->_set_failure;
121 1         13 $self->_set_error("('format' parameter invalid)");
122 1         6 return $self->_get_datetime_object;
123             }
124              
125 192         370 my $i = 0;
126 192         558 my %format_index = map { $_ => $i++ } split /\//, $format;
  384         1201  
127              
128 192         896 my ($day, $month) = map $date_chunks[$format_index{$_}], qw(d m);
129              
130 192 50       794 unless ($self->_check_date($self->{datetime}->year, $month, $day)) {
131 0         0 $self->_set_failure;
132 0         0 $self->_set_error("(invalid date)");
133 0         0 return $self->_get_datetime_object;
134             }
135              
136             $self->_set(
137 192         874 month => $month,
138             day => $day,
139             );
140 192         835 $self->{datetime}->truncate(to => 'day');
141 192         49854 $self->_set_truncated;
142 192         998 $self->_set_valid_exp;
143              
144 192         876 $self->_process_tokens;
145              
146 192         2942 return undef;
147             }
148              
149             sub _split_formatted
150             {
151 464     464   790 my $self = shift;
152 464         914 my ($date_string) = @_;
153              
154 464         751 my $date;
155 464 100       1895 if ($date_string =~ /^\S+\b \s+ \b\S+/x) {
156 321         1294 ($date, @{$self->{tokens}}) = split /\s+/, $date_string;
  321         949  
157 321         978 $self->{count}{tokens} = 1 + @{$self->{tokens}};
  321         1000  
158             }
159             else {
160 143         442 $self->{count}{tokens} = 1;
161             }
162              
163 464 100       1431 return defined $date ? $date : $date_string;
164             }
165              
166             sub _process_tokens
167             {
168 462     462   801 my $self = shift;
169              
170 462 100       792 if (@{$self->{tokens}}) {
  462         1484  
171 321         698 $self->{count}{tokens}--;
172 321         1025 $self->_unset_truncated;
173 321         1394 $self->_unset_valid_exp;
174 321         1346 $self->_process;
175             }
176             }
177              
178             sub _count_separators
179             {
180 506239     506239   783108 my $self = shift;
181 506239         827393 my ($formatted) = @_;
182              
183 506239         696114 my %count;
184 506239 100       994515 if (defined $formatted) {
185 10522         41300 my @count = $formatted =~ m![-./]!g;
186 10522         38780 $count{$_}++ foreach @count;
187             }
188              
189 506239         1237794 return %count;
190             }
191              
192             sub _check_formatted
193             {
194 518840     518840   794922 my $self = shift;
195 518840         859751 my ($check, $count) = @_;
196              
197             my %checks = (
198             ymd => sub
199             {
200 506239     506239   832213 my ($count) = @_;
201 506239   100     3123587 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 2;
202             },
203             md => sub
204             {
205 12601     12601   24518 my ($count) = @_;
206 12601   66     133154 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 1 && (keys %$count)[0] eq '/';
207             },
208 518840         2537642 );
209              
210 518840         1159977 return $checks{$check}->($count);
211             }
212              
213             1;
214             __END__
215              
216             =head1 NAME
217              
218             DateTime::Format::Natural::Formatted - Processing of formatted dates
219              
220             =head1 SYNOPSIS
221              
222             Please see the DateTime::Format::Natural documentation.
223              
224             =head1 DESCRIPTION
225              
226             The C<DateTime::Format::Natural::Formatted> class contains methods
227             to parse formatted dates.
228              
229             =head1 SEE ALSO
230              
231             L<DateTime::Format::Natural>
232              
233             =head1 AUTHOR
234              
235             Steven Schubiger <schubiger@cpan.org>
236              
237             =head1 LICENSE
238              
239             This program is free software; you may redistribute it and/or
240             modify it under the same terms as Perl itself.
241              
242             See L<http://dev.perl.org/licenses/>
243              
244             =cut