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   227 use strict;
  26         68  
  26         761  
4 26     26   140 use warnings;
  26         56  
  26         744  
5 26     26   135 use boolean qw(true false);
  26         60  
  26         186  
6              
7             our $VERSION = '0.12';
8              
9             sub _parse_formatted_ymd
10             {
11 271     271   568 my $self = shift;
12 271         587 my ($date_string, $count) = @_;
13              
14 271         747 my $date = $self->_split_formatted($date_string);
15              
16 271         906 my $date_sep = quotemeta((keys %$count)[0]);
17 271         1300 my @date_chunks = split /$date_sep/, $date;
18              
19 271 100 66     1134 if ($date_chunks[1] =~ /^[a-zA-Z]+$/
20 84         572 and my ($month_abbrev) = grep { $date_chunks[1] =~ /^${_}$/i } keys %{$self->{data}->{months_abbrev}}
  7         52  
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         511 my $i = 0;
27 271         601 my %chunks_length = map { length $_ => $i++ } @date_chunks;
  813         2256  
28              
29 271         884 my $format = lc $self->{Format};
30 271         437 my $format_sep;
31              
32 271         785 my $lax = false;
33              
34 271 100 66     1317 if (exists $chunks_length{4}) {
    100          
35             $format = join $date_sep,
36 253 100       1093 ($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         643 $lax = true;
44             }
45             elsif ($date_sep =~ /^\\[-.]$/ && $format !~ /$date_sep/) {
46 12         47 $format = join $date_sep, qw(dd mm yy);
47 12         29 $lax = true;
48             }
49             else {
50 6         12 $format_sep = do { local $_ = $format;
  6         13  
51 6         16 tr/a-zA-Z//d;
52 6         13 tr/a-zA-Z//cs;
53 6         14 quotemeta; };
54             }
55 271   66     1800 $format_sep ||= $date_sep;
56              
57 271 50 66     867 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         4005 my @format_order = split /$format_sep/, $format;
64              
65 271         510 my ($d, $m, $y) = do {
66 271         504 my %f = map { substr($_, 0, 1) => true } @format_order;
  813         2726  
67 271         1972 ($f{d}, $f{m}, $f{y});
68             };
69 271 50 33     1082 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         6848 $i = 0;
76 271         520 my %format_index = map { substr($_, 0, 1) => $i++ } @format_order;
  813         2188  
77              
78             my $century = $self->{datetime}
79 271 50       1173 ? int($self->{datetime}->year / 100)
80             : substr((localtime)[5] + 1900, 0, 2);
81              
82 271         4203 my ($day, $month, $year) = map $date_chunks[$format_index{$_}], qw(d m y);
83              
84 271 100       816 if (length $year == 2) { $year = "$century$year" };
  18         55  
85              
86 271 100       1069 unless ($self->_check_date($year, $month, $day)) {
87 1         7 $self->_set_failure;
88 1         10 $self->_set_error("(invalid date)");
89 1         5 return $self->_get_datetime_object;
90             }
91              
92             $self->_set(
93 270         1272 year => $year,
94             month => $month,
95             day => $day,
96             );
97 270         1227 $self->{datetime}->truncate(to => 'day');
98 270         69851 $self->_set_truncated;
99 270         1409 $self->_set_valid_exp;
100              
101 270         1430 $self->_process_tokens;
102              
103 270         5039 return undef;
104             }
105              
106             sub _parse_formatted_md
107             {
108 193     193   372 my $self = shift;
109 193         406 my ($date_string) = @_;
110              
111 193         555 my $date = $self->_split_formatted($date_string);
112              
113 193         649 my @date_chunks = split /\//, $date;
114              
115             my $format = $self->{Format} =~ m{^[dm]{1,2}/[dm]{1,2}$}i
116 193 100       928 ? do { local $_ = lc $self->{Format}; tr/dm//s; $_ }
  192         563  
  192         431  
  192         481  
117             : undef;
118              
119 193 100 66     1184 unless (defined $format && $format =~ m{^(?:(?:m/d)|(?:d/m))$}) {
120 1         10 $self->_set_failure;
121 1         8 $self->_set_error("('format' parameter invalid)");
122 1         6 return $self->_get_datetime_object;
123             }
124              
125 192         412 my $i = 0;
126 192         612 my %format_index = map { $_ => $i++ } split /\//, $format;
  384         1195  
127              
128 192         925 my ($day, $month) = map $date_chunks[$format_index{$_}], qw(d m);
129              
130 192 50       782 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         890 month => $month,
138             day => $day,
139             );
140 192         837 $self->{datetime}->truncate(to => 'day');
141 192         50333 $self->_set_truncated;
142 192         987 $self->_set_valid_exp;
143              
144 192         917 $self->_process_tokens;
145              
146 192         2911 return undef;
147             }
148              
149             sub _split_formatted
150             {
151 464     464   787 my $self = shift;
152 464         906 my ($date_string) = @_;
153              
154 464         785 my $date;
155 464 100       1936 if ($date_string =~ /^\S+\b \s+ \b\S+/x) {
156 321         1239 ($date, @{$self->{tokens}}) = split /\s+/, $date_string;
  321         919  
157 321         622 $self->{count}{tokens} = 1 + @{$self->{tokens}};
  321         977  
158             }
159             else {
160 143         451 $self->{count}{tokens} = 1;
161             }
162              
163 464 100       1460 return defined $date ? $date : $date_string;
164             }
165              
166             sub _process_tokens
167             {
168 462     462   917 my $self = shift;
169              
170 462 100       738 if (@{$self->{tokens}}) {
  462         1509  
171 321         695 $self->{count}{tokens}--;
172 321         949 $self->_unset_truncated;
173 321         1375 $self->_unset_valid_exp;
174 321         1442 $self->_process;
175             }
176             }
177              
178             sub _count_separators
179             {
180 508163     508163   840033 my $self = shift;
181 508163         871861 my ($formatted) = @_;
182              
183 508163         744922 my %count;
184 508163 100       1029445 if (defined $formatted) {
185 10499         42147 my @count = $formatted =~ m![-./]!g;
186 10499         40158 $count{$_}++ foreach @count;
187             }
188              
189 508163         1342788 return %count;
190             }
191              
192             sub _check_formatted
193             {
194 520764     520764   817506 my $self = shift;
195 520764         927044 my ($check, $count) = @_;
196              
197             my %checks = (
198             ymd => sub
199             {
200 508163     508163   854896 my ($count) = @_;
201 508163   100     3249747 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 2;
202             },
203             md => sub
204             {
205 12601     12601   23044 my ($count) = @_;
206 12601   66     132481 return scalar keys %$count == 1 && $count->{(keys %$count)[0]} == 1 && (keys %$count)[0] eq '/';
207             },
208 520764         2639424 );
209              
210 520764         1182354 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