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   236 use strict;
  26         59  
  26         1187  
4 26     26   148 use warnings;
  26         53  
  26         1460  
5 26     26   474 use boolean qw(true false);
  26         186  
  26         303  
6              
7             our $VERSION = '0.12';
8              
9             sub _parse_formatted_ymd
10             {
11 271     271   586 my $self = shift;
12 271         853 my ($date_string, $count) = @_;
13              
14 271         1082 my $date = $self->_split_formatted($date_string);
15              
16 271         1038 my $date_sep = quotemeta((keys %$count)[0]);
17 271         1934 my @date_chunks = split /$date_sep/, $date;
18              
19 271 100 66     1626 if ($date_chunks[1] =~ /^[a-zA-Z]+$/
20 84         888 and my ($month_abbrev) = grep { $date_chunks[1] =~ /^${_}$/i } keys %{$self->{data}->{months_abbrev}}
  7         55  
21             ) {
22 7         54 my ($months_abbrev, $months) = map $self->{data}->{$_}, qw(months_abbrev months);
23 7         198 $date_chunks[1] = sprintf '%02d', $months->{$months_abbrev->{$month_abbrev}};
24             }
25              
26 271         635 my $i = 0;
27 271         738 my %chunks_length = map { length $_ => $i++ } @date_chunks;
  813         2876  
28              
29 271         1093 my $format = lc $self->{Format};
30 271         511 my $format_sep;
31              
32 271         1109 my $lax = false;
33              
34 271 100 66     1613 if (exists $chunks_length{4}) {
    100          
35             $format = join $date_sep,
36 253 100       1489 ($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         724 $lax = true;
44             }
45             elsif ($date_sep =~ /^\\[-.]$/ && $format !~ /$date_sep/) {
46 12         42 $format = join $date_sep, qw(dd mm yy);
47 12         60 $lax = true;
48             }
49             else {
50 6         9 $format_sep = do { local $_ = $format;
  6         13  
51 6         15 tr/a-zA-Z//d;
52 6         12 tr/a-zA-Z//cs;
53 6         15 quotemeta; };
54             }
55 271   66     2062 $format_sep ||= $date_sep;
56              
57 271 50 66     1218 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         5379 my @format_order = split /$format_sep/, $format;
64              
65 271         602 my ($d, $m, $y) = do {
66 271         615 my %f = map { substr($_, 0, 1) => true } @format_order;
  813         3348  
67 271         2514 ($f{d}, $f{m}, $f{y});
68             };
69 271 50 33     1506 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         8441 $i = 0;
76 271         653 my %format_index = map { substr($_, 0, 1) => $i++ } @format_order;
  813         2320  
77              
78             my $century = $self->{datetime}
79 271 50       1927 ? int($self->{datetime}->year / 100)
80             : substr((localtime)[5] + 1900, 0, 2);
81              
82 271         5458 my ($day, $month, $year) = map $date_chunks[$format_index{$_}], qw(d m y);
83              
84 271 100       965 if (length $year == 2) { $year = "$century$year" };
  18         49  
85              
86 271 100       1481 unless ($self->_check_date($year, $month, $day)) {
87 1         6 $self->_set_failure;
88 1         7 $self->_set_error("(invalid date)");
89 1         5 return $self->_get_datetime_object;
90             }
91              
92             $self->_set(
93 270         1686 year => $year,
94             month => $month,
95             day => $day,
96             );
97 270         1604 $self->{datetime}->truncate(to => 'day');
98 270         100164 $self->_set_truncated;
99 270         1772 $self->_set_valid_exp;
100              
101 270         2467 $self->_process_tokens;
102              
103 270         8911 return undef;
104             }
105              
106             sub _parse_formatted_md
107             {
108 193     193   443 my $self = shift;
109 193         548 my ($date_string) = @_;
110              
111 193         748 my $date = $self->_split_formatted($date_string);
112              
113 193         889 my @date_chunks = split /\//, $date;
114              
115             my $format = $self->{Format} =~ m{^[dm]{1,2}/[dm]{1,2}$}i
116 193 100       1492 ? do { local $_ = lc $self->{Format}; tr/dm//s; $_ }
  192         737  
  192         662  
  192         653  
117             : undef;
118              
119 193 100 66     1679 unless (defined $format && $format =~ m{^(?:(?:m/d)|(?:d/m))$}) {
120 1         6 $self->_set_failure;
121 1         7 $self->_set_error("('format' parameter invalid)");
122 1         4 return $self->_get_datetime_object;
123             }
124              
125 192         494 my $i = 0;
126 192         756 my %format_index = map { $_ => $i++ } split /\//, $format;
  384         1627  
127              
128 192         1376 my ($day, $month) = map $date_chunks[$format_index{$_}], qw(d m);
129              
130 192 50       1302 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         1304 month => $month,
138             day => $day,
139             );
140 192         1235 $self->{datetime}->truncate(to => 'day');
141 192         65002 $self->_set_truncated;
142 192         1422 $self->_set_valid_exp;
143              
144 192         1323 $self->_process_tokens;
145              
146 192         4204 return undef;
147             }
148              
149             sub _split_formatted
150             {
151 464     464   925 my $self = shift;
152 464         1069 my ($date_string) = @_;
153              
154 464         881 my $date;
155 464 100       2686 if ($date_string =~ /^\S+\b \s+ \b\S+/x) {
156 321         1240 ($date, @{$self->{tokens}}) = split /\s+/, $date_string;
  321         1162  
157 321         817 $self->{count}{tokens} = 1 + @{$self->{tokens}};
  321         2030  
158             }
159             else {
160 143         794 $self->{count}{tokens} = 1;
161             }
162              
163 464 100       1978 return defined $date ? $date : $date_string;
164             }
165              
166             sub _process_tokens
167             {
168 462     462   1036 my $self = shift;
169              
170 462 100       977 if (@{$self->{tokens}}) {
  462         2300  
171 321         900 $self->{count}{tokens}--;
172 321         1221 $self->_unset_truncated;
173 321         1617 $self->_unset_valid_exp;
174 321         1929 $self->_process;
175             }
176             }
177              
178             sub _count_separators
179             {
180 510772     510772   944664 my $self = shift;
181 510772         1078208 my ($formatted) = @_;
182              
183 510772         916271 my %count;
184 510772 100       1307233 if (defined $formatted) {
185 10535         61197 my @count = $formatted =~ m![-./]!g;
186 10535         50526 $count{$_}++ foreach @count;
187             }
188              
189 510772         1413259 return %count;
190             }
191              
192             sub _check_formatted
193             {
194 523605     523605   892192 my $self = shift;
195 523605         1125030 my ($check, $count) = @_;
196              
197             my %checks = (
198             ymd => sub
199             {
200 510772     510772   1035728 my ($count) = @_;
201 510772   100     4274615 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 2;
202             },
203             md => sub
204             {
205 12833     12833   39800 my ($count) = @_;
206 12833   66     187640 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 1 && (keys %$count)[0] eq '/';
207             },
208 523605         3774838 );
209              
210 523605         1271304 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