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   221 use strict;
  26         67  
  26         754  
4 26     26   153 use warnings;
  26         61  
  26         728  
5 26     26   140 use boolean qw(true false);
  26         63  
  26         144  
6              
7             our $VERSION = '0.12';
8              
9             sub _parse_formatted_ymd
10             {
11 271     271   513 my $self = shift;
12 271         599 my ($date_string, $count) = @_;
13              
14 271         754 my $date = $self->_split_formatted($date_string);
15              
16 271         832 my $date_sep = quotemeta((keys %$count)[0]);
17 271         1347 my @date_chunks = split /$date_sep/, $date;
18              
19 271 100 66     1115 if ($date_chunks[1] =~ /^[a-zA-Z]+$/
20 84         569 and my ($month_abbrev) = grep { $date_chunks[1] =~ /^${_}$/i } keys %{$self->{data}->{months_abbrev}}
  7         42  
21             ) {
22 7         36 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         506 my $i = 0;
27 271         588 my %chunks_length = map { length $_ => $i++ } @date_chunks;
  813         2264  
28              
29 271         872 my $format = lc $self->{Format};
30 271         452 my $format_sep;
31              
32 271         761 my $lax = false;
33              
34 271 100 66     1218 if (exists $chunks_length{4}) {
    100          
35             $format = join $date_sep,
36 253 100       990 ($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         631 $lax = true;
44             }
45             elsif ($date_sep =~ /^\\[-.]$/ && $format !~ /$date_sep/) {
46 12         45 $format = join $date_sep, qw(dd mm yy);
47 12         31 $lax = true;
48             }
49             else {
50 6         10 $format_sep = do { local $_ = $format;
  6         13  
51 6         15 tr/a-zA-Z//d;
52 6         13 tr/a-zA-Z//cs;
53 6         14 quotemeta; };
54             }
55 271   66     1680 $format_sep ||= $date_sep;
56              
57 271 50 66     827 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         3977 my @format_order = split /$format_sep/, $format;
64              
65 271         472 my ($d, $m, $y) = do {
66 271         573 my %f = map { substr($_, 0, 1) => true } @format_order;
  813         2729  
67 271         1914 ($f{d}, $f{m}, $f{y});
68             };
69 271 50 33     1060 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         7141 $i = 0;
76 271         512 my %format_index = map { substr($_, 0, 1) => $i++ } @format_order;
  813         1908  
77              
78             my $century = $self->{datetime}
79 271 50       1135 ? int($self->{datetime}->year / 100)
80             : substr((localtime)[5] + 1900, 0, 2);
81              
82 271         4105 my ($day, $month, $year) = map $date_chunks[$format_index{$_}], qw(d m y);
83              
84 271 100       840 if (length $year == 2) { $year = "$century$year" };
  18         40  
85              
86 271 100       970 unless ($self->_check_date($year, $month, $day)) {
87 1         9 $self->_set_failure;
88 1         9 $self->_set_error("(invalid date)");
89 1         7 return $self->_get_datetime_object;
90             }
91              
92             $self->_set(
93 270         1176 year => $year,
94             month => $month,
95             day => $day,
96             );
97 270         1202 $self->{datetime}->truncate(to => 'day');
98 270         70049 $self->_set_truncated;
99 270         1366 $self->_set_valid_exp;
100              
101 270         1341 $self->_process_tokens;
102              
103 270         5055 return undef;
104             }
105              
106             sub _parse_formatted_md
107             {
108 193     193   350 my $self = shift;
109 193         396 my ($date_string) = @_;
110              
111 193         490 my $date = $self->_split_formatted($date_string);
112              
113 193         625 my @date_chunks = split /\//, $date;
114              
115             my $format = $self->{Format} =~ m{^[dm]{1,2}/[dm]{1,2}$}i
116 193 100       891 ? do { local $_ = lc $self->{Format}; tr/dm//s; $_ }
  192         537  
  192         432  
  192         473  
117             : undef;
118              
119 193 100 66     1134 unless (defined $format && $format =~ m{^(?:(?:m/d)|(?:d/m))$}) {
120 1         5 $self->_set_failure;
121 1         8 $self->_set_error("('format' parameter invalid)");
122 1         9 return $self->_get_datetime_object;
123             }
124              
125 192         354 my $i = 0;
126 192         516 my %format_index = map { $_ => $i++ } split /\//, $format;
  384         1177  
127              
128 192         874 my ($day, $month) = map $date_chunks[$format_index{$_}], qw(d m);
129              
130 192 50       775 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         827 month => $month,
138             day => $day,
139             );
140 192         829 $self->{datetime}->truncate(to => 'day');
141 192         49662 $self->_set_truncated;
142 192         960 $self->_set_valid_exp;
143              
144 192         932 $self->_process_tokens;
145              
146 192         2975 return undef;
147             }
148              
149             sub _split_formatted
150             {
151 464     464   847 my $self = shift;
152 464         864 my ($date_string) = @_;
153              
154 464         770 my $date;
155 464 100       1880 if ($date_string =~ /^\S+\b \s+ \b\S+/x) {
156 321         1216 ($date, @{$self->{tokens}}) = split /\s+/, $date_string;
  321         892  
157 321         620 $self->{count}{tokens} = 1 + @{$self->{tokens}};
  321         985  
158             }
159             else {
160 143         429 $self->{count}{tokens} = 1;
161             }
162              
163 464 100       1439 return defined $date ? $date : $date_string;
164             }
165              
166             sub _process_tokens
167             {
168 462     462   930 my $self = shift;
169              
170 462 100       756 if (@{$self->{tokens}}) {
  462         1466  
171 321         713 $self->{count}{tokens}--;
172 321         946 $self->_unset_truncated;
173 321         1374 $self->_unset_valid_exp;
174 321         1320 $self->_process;
175             }
176             }
177              
178             sub _count_separators
179             {
180 507196     507196   841382 my $self = shift;
181 507196         905589 my ($formatted) = @_;
182              
183 507196         741341 my %count;
184 507196 100       1097224 if (defined $formatted) {
185 10492         45267 my @count = $formatted =~ m![-./]!g;
186 10492         39770 $count{$_}++ foreach @count;
187             }
188              
189 507196         1340197 return %count;
190             }
191              
192             sub _check_formatted
193             {
194 519797     519797   790542 my $self = shift;
195 519797         905452 my ($check, $count) = @_;
196              
197             my %checks = (
198             ymd => sub
199             {
200 507196     507196   839525 my ($count) = @_;
201 507196   100     3219374 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 2;
202             },
203             md => sub
204             {
205 12601     12601   22611 my ($count) = @_;
206 12601   66     130162 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 1 && (keys %$count)[0] eq '/';
207             },
208 519797         2620615 );
209              
210 519797         1205293 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