File Coverage

blib/lib/DateTime/Format/Flexible/lang.pm
Criterion Covered Total %
statement 217 220 98.6
branch 80 102 78.4
condition 12 12 100.0
subroutine 19 19 100.0
pod 2 2 100.0
total 330 355 92.9


line stmt bran cond sub pod time code
1             package DateTime::Format::Flexible::lang;
2              
3 22     22   176 use strict;
  22         56  
  22         795  
4 22     22   148 use warnings;
  22         56  
  22         827  
5              
6 22     22   141 use List::MoreUtils 'any';
  22         51  
  22         294  
7              
8             sub new
9             {
10 6354     6354 1 1837767 my ( $class , %params ) = @_;
11 6354         18351 my $self = bless \%params , $class;
12              
13 6354 100 100     21812 if ($self->{lang} and not ref($self->{lang}) eq 'ARRAY')
14             {
15 137         397 $self->{lang} = [$self->{lang}];
16             }
17              
18             $self->{_plugins} = [
19 6354         19688 'DateTime::Format::Flexible::lang::de',
20             'DateTime::Format::Flexible::lang::en',
21             'DateTime::Format::Flexible::lang::es',
22             ];
23              
24 6354         10623 foreach my $plugin (@{$self->{_plugins}}) {
  6354         16354  
25 19062         37812 my $path = $plugin . ".pm";
26 19062         76680 $path =~ s{::}{/}g;
27 19062         126254 require $path;
28             }
29 6354         20247 return $self;
30             }
31              
32 3807     3807 1 6676 sub plugins {return @{$_[0]->{_plugins}}}
  3807         12947  
33              
34             sub _cleanup
35             {
36 3807     3807   10273 my ( $self , $date , $p ) = @_;
37 3807         10097 foreach my $plug ( $self->plugins )
38             {
39 11421 100       28346 if ( $self->{lang} )
40             {
41 417         1619 my ( $lang ) = $plug =~ m{(\w{2}\z)}mx;
42 417 100   417   1499 if ( not any { $_ eq $lang } @{ $self->{lang} } )
  417         1167  
  417         1404  
43             {
44 278 50       623 printf( "# skipping %s\n", $plug ) if $ENV{DFF_DEBUG};
45 278         1088 next;
46             }
47             }
48 11143 50       23623 printf( "# not skipping %s\n", $plug ) if $ENV{DFF_DEBUG};
49              
50 11143 50       22987 printf( "# before math: %s\n", $date ) if $ENV{DFF_DEBUG};
51 11143         27607 $date = $self->_do_math( $plug , $date );
52 11143 50       28501 printf( "# before string_dates: %s\n", $date ) if $ENV{DFF_DEBUG};
53 11143         30142 $date = $self->_string_dates( $plug , $date );
54 11143 50       36853 printf( "# before fix_alpha_month: %s\n", $date ) if $ENV{DFF_DEBUG};
55 11143         28942 ( $date , $p ) = $self->_fix_alpha_month( $plug , $date , $p );
56 11143 50       35401 printf( "# before remove_day_names: %s\n", $date ) if $ENV{DFF_DEBUG};
57 11143         32231 $date = $self->_remove_day_names( $plug , $date );
58 11143 50       31336 printf( "# before fix_hours: %s\n", $date ) if $ENV{DFF_DEBUG};
59 11143         31804 $date = $self->_fix_hours( $plug , $date );
60 11143 50       29399 printf( "# before remove_strings: %s\n", $date ) if $ENV{DFF_DEBUG};
61 11143         27311 $date = $self->_remove_strings( $plug , $date );
62 11143 50       27263 printf( "# before locate_time: %s\n", $date ) if $ENV{DFF_DEBUG};
63 11143         25811 $date = $self->_locate_time( $plug , $date );
64 11143 50       24642 printf( "# before fix_internal_tz: %s\n", $date ) if $ENV{DFF_DEBUG};
65 11143         26948 ( $date , $p ) = $self->_fix_internal_tz( $plug , $date , $p );
66 11143 50       30397 printf( "# finished: %s\n", $date ) if $ENV{DFF_DEBUG};
67             }
68 3807         14385 return ( $date , $p );
69             }
70              
71             sub _fix_internal_tz
72             {
73 11143     11143   25254 my ( $self , $plug , $date , $p ) = @_;
74 11143         31293 my %tzs = $plug->timezone_map;
75 11143         41996 while( my( $orig_tz , $new_tz ) = each ( %tzs ) )
76             {
77 93478 100       623209 if( $date =~ m{$orig_tz}mxi )
78             {
79 4         10 $p->{ time_zone } = $new_tz;
80 4         22 $date =~ s{$orig_tz}{}mxi;
81 4         22 return ( $date , $p );
82             }
83             }
84 11139         43992 return ( $date , $p );
85             }
86              
87             sub _do_math
88             {
89 11143     11143   22087 my ( $self , $plug , $date ) = @_;
90              
91 11143         43921 my %relative_strings = $plug->relative;
92 11143         41620 my $day_strings = $plug->days;
93 11143         31899 my %month_strings = $plug->months;
94              
95 11143         89327 my $instructions = {
96             ago => {direction => 'past', units => 1},
97             from => {direction => 'future', units => 1},
98             last => {direction => 'past'},
99             next => {direction => 'future'},
100             };
101              
102 11143         33722 foreach my $keyword (keys %relative_strings)
103             {
104 44572         74699 my $rx = $relative_strings{$keyword};
105              
106 44572 50       90601 next if not (exists $instructions->{$keyword});
107              
108 44572         68361 my $has_units = $instructions->{$keyword}->{units};
109 44572         67857 my $direction = $instructions->{$keyword}->{direction};
110              
111 44572 100       234036 if ( $date =~ m{$rx}mix )
112             {
113 101         514 $date =~ s{$rx}{}mix;
114 101 100       333 if ($has_units)
115             {
116 24         79 $date = $self->_set_units( $plug , $date, $direction );
117             }
118             else
119             {
120 77         129 foreach my $set (@{$day_strings})
  77         166  
121             {
122 1155         1866 foreach my $day (keys %{$set})
  1155         2628  
123             {
124              
125 1155 100       7070 if ($date =~ m{$day}mix)
126             {
127 29         101 $date = $self->_set_day( $plug , $date , $day , $direction );
128 29         397 $date =~ s{$day}{}mix;
129             }
130             }
131             }
132 77         262 foreach my $month (keys %month_strings)
133             {
134 924 100       7035 if ($date =~ m{$month}mix)
135             {
136 48         175 $date = $self->_set_month( $plug , $date , $month , $direction );
137 48         818 $date =~ s{$month}{}mix;
138             }
139             }
140             }
141 101 50       375 printf("# after removing rx (%s): [%s]\n", $rx, $date) if $ENV{DFF_DEBUG};
142              
143 101         498 $date =~ s{$keyword}{}mx;
144 101         483 $date =~ s{\s+}{ }gm;
145 101         401 $date =~ s{\s+\z}{}gm;
146 101 50       343 printf("# after removing keyword (%s): [%s]\n", $keyword, $date) if $ENV{DFF_DEBUG};
147             }
148              
149             }
150              
151 11143         100849 return $date;
152             }
153              
154             sub _set_units
155             {
156 24     24   65 my ( $self , $plug , $date , $direction ) = @_;
157              
158 24         70 my %strings = $plug->math_strings;
159 24 100       154 if ( my ( $amount , $unit ) = $date =~ m{(\d+)\s+([^\s]+)}mx )
160             {
161 21 50       63 printf( "# %s => %s\n", $amount, $unit ) if $ENV{DFF_DEBUG};
162 21 50       53 if ( exists( $strings{$unit} ) )
163             {
164 21         69 my $base_dt = DateTime::Format::Flexible->base->clone;
165              
166 21 100       1257 if ( $direction eq 'past' )
167             {
168 13         49 $base_dt->subtract( $strings{$unit} => $amount );
169             }
170 21 100       15090 if ( $direction eq 'future' )
171             {
172 8         35 $base_dt->add( $strings{$unit} => $amount );
173             }
174 21         8531 $date =~ s{\s{0,}$amount\s+$unit\s{0,}}{}mx;
175              
176 21 50       102 if ($ENV{DFF_DEBUG})
177             {
178 0         0 printf("# found: %s\n", $strings{$unit}) ;
179 0         0 printf("# after removing amount, unit: [%s]\n", $date);
180             }
181              
182 21         72 $date = $base_dt->datetime . ' ' . $date;
183             }
184             }
185              
186 24         735 return $date;
187             }
188              
189             sub _set_day
190             {
191 29     29   80 my ( $self , $plug , $date , $day , $direction ) = @_;
192              
193 29         98 my $base_dt = DateTime::Format::Flexible->base->clone;
194 29         536 my $dow = $base_dt->day_of_week;
195 29         157 my $date_dow = $self->_alpha_day_to_int($plug, $day);
196              
197 29 100       94 if ( $direction eq 'past' )
198             {
199 14         28 my $amount = $dow - $date_dow;
200 14 100       401 if ($amount < 1) {$amount = 7 + $amount}
  12         23  
201 14 50       31 printf("# subtracting %s days\n", $amount) if $ENV{DFF_DEBUG};
202              
203 14         50 my $ret = $base_dt->subtract( 'days' => $amount )->truncate( to => 'day' );
204 14         19935 $date = $ret->datetime . ' ' . $date;
205              
206             }
207 29 100       445 if ( $direction eq 'future' )
208             {
209 15         30 my $amount = $date_dow - $dow;
210 15 100       35 if ($amount < 1) {$amount = 7 + $amount}
  4         6  
211 15 50       37 printf("# adding %s days\n", $amount) if $ENV{DFF_DEBUG};
212              
213 15         52 my $ret = $base_dt->add( 'days' => $amount )->truncate( to => 'day' );
214 15         19193 $date = $ret->datetime . ' ' . $date;
215             }
216              
217              
218 29         560 return $date;
219             }
220              
221             sub _set_month
222             {
223 48     48   138 my ( $self , $plug , $date , $month , $direction ) = @_;
224              
225 48         151 my %month_strings = $plug->months;
226              
227 48         351 my $base_dt = DateTime::Format::Flexible->base->clone;
228 48         932 my $mon = $base_dt->month;
229 48         279 my $date_mon = $month_strings{$month};
230              
231 48 50       169 printf("# setting month to: %s\n", $date_mon) if $ENV{DFF_DEBUG};
232              
233 48         161 $base_dt->set_month($date_mon);
234 48 100 100     22808 if ($direction eq 'past' and $date_mon >= $mon)
235             {
236 15         51 $base_dt->set_year($base_dt->year - 1);
237             }
238 48 100 100     6938 if ($direction eq 'future' and $date_mon <= $mon)
239             {
240 11         33 $base_dt->set_year($base_dt->year + 1);
241             }
242 48         5169 $base_dt->truncate( to => 'month' );
243 48 50       12094 printf("# set year to: %s\n", $base_dt->year) if $ENV{DFF_DEBUG};
244              
245 48         134 $date = $base_dt->datetime . ' ' . $date;
246              
247 48         1519 return $date;
248             }
249              
250             sub _string_dates
251             {
252 11143     11143   23203 my ( $self , $plug , $date ) = @_;
253 11143         33901 my %strings = $plug->string_dates;
254 11143         48688 foreach my $key ( keys %strings )
255             {
256 96619 100       599996 if ( $date =~ m{\Q$key\E}mxi )
257             {
258 45         144 my $new_value = $strings{$key}->();
259 45         18705 $date =~ s{\Q$key\E}{$new_value}mix;
260             }
261             }
262              
263 11143         47921 my %day_numbers = $plug->day_numbers;
264 11143         73810 foreach my $key ( keys %day_numbers )
265             {
266 416237 100       836893 if (index(lc($date), lc($key)) >= 0)
267             {
268 8         22 my $new_value = $day_numbers{$key};
269 8         91 $date =~ s{$key}{n${new_value}n}mix;
270             }
271             }
272 11143         203764 return $date;
273             }
274              
275             # turn month names into month numbers with surrounding X
276             # Sep => X9X
277             sub _fix_alpha_month
278             {
279 11143     11143   28068 my ( $self , $plug , $date , $p ) = @_;
280 11143         32317 my %months = $plug->months;
281 11143         67861 while( my( $month_name , $month_number ) = each ( %months ) )
282             {
283 134292 100       3609389 if( $date =~ m{\b$month_name\b}mxi )
    100          
    100          
284             {
285 2408         7755 $p->{ month } = $month_number;
286 2408         21251 $date =~ s{\b$month_name\b}{X${month_number}X}mxi;
287              
288 2408         17426 return ( $date , $p );
289             }
290             elsif ( $date =~ m{\d$month_name}mxi )
291             {
292 11         41 $p->{ month } = $month_number;
293 11         137 $date =~ s{(\d)$month_name}{$1X${month_number}X}mxi;
294              
295 11         82 return ( $date , $p );
296             }
297              
298             elsif( $date =~ m{\b$month_name\d.*\b}mxi )
299             {
300 4         13 $p->{ month } = $month_number;
301 4         46 $date =~ s{\b$month_name(\d.*)\b}{X${month_number}X$1}mxi;
302              
303 4         30 return ( $date , $p );
304             }
305             }
306 8720         49177 return ( $date , $p );
307             }
308              
309             # remove any day names, we do not need them
310             sub _remove_day_names
311             {
312 11143     11143   24185 my ( $self , $plug , $date ) = @_;
313 11143         36599 my $days = $plug->days;
314 11143         21806 foreach my $set (@{$days})
  11143         29551  
315             {
316 136008         228888 foreach my $day_name ( keys %{$set} )
  136008         314530  
317             {
318             # if the day name is by itself, make it the upcoming day
319             # eg: monday = next monday
320 136008 100 100     531618 if (( lc($date) eq lc($day_name)) or (index(lc($date), lc($day_name) . ' at') >= 0 ))
321             {
322 16         75 my $dt = $self->{base}->clone->truncate( to => 'day' );
323 16         4505 my $date_dow = $set->{$day_name};
324              
325 16 100       47 if ( $date_dow == $dt->dow )
    100          
326             {
327 2         11 my $str = $dt->ymd;
328 2         55 $date =~ s{$day_name}{$str}i;
329 2         22 return $date;
330             }
331             elsif ( $date_dow > $dt->dow )
332             {
333 12         103 $dt->add( days => $date_dow - $dt->dow );
334 12         11955 my $str = $dt->ymd;
335 12         320 $date =~ s{$day_name}{$str}i;
336 12         136 return $date;
337             }
338             else
339             {
340 2         20 $dt->add( days => $date_dow - $dt->dow + 7 );
341 2         1964 my $str = $dt->ymd;
342 2         54 $date =~ s{$day_name}{$str}i;
343 2         21 return $date;
344             }
345             }
346             # otherwise, just strip it out
347 135992 100       1070334 if ( $date =~ m{\b$day_name\b}mxi )
348             {
349 233         1364 $date =~ s{$day_name,?}{}gmix;
350 233         1776 return $date;
351             }
352             }
353             }
354 10894         64364 return $date;
355             }
356              
357             sub _alpha_day_to_int
358             {
359 29     29   66 my ( $self, $plug, $day ) = @_;
360              
361 29         85 my $day_strings = $plug->days;
362 29         57 foreach my $set (@{$day_strings})
  29         58  
363             {
364 238         311 foreach my $key (keys %{$set})
  238         488  
365             {
366 238 100       555 if (lc($key) eq lc($day))
367             {
368 29         182 return $set->{$key};
369             }
370             }
371             }
372 0         0 return;
373             }
374              
375             # fix noon and midnight, named hours
376             sub _fix_hours
377             {
378 11143     11143   25789 my ( $self , $plug , $date ) = @_;
379 11143         34063 my %hours = $plug->hours;
380 11143         37200 foreach my $hour ( keys %hours )
381             {
382 75104 100       427005 if ( $date =~ m{$hour}mxi )
383             {
384 34         104 my $realtime = $hours{ $hour };
385 34         100 $date =~ s{T[^\s]+}{};
386 34         226 $date =~ s{$hour}{${realtime}}gmix;
387 34         200 return $date;
388             }
389             }
390 11109         41461 return $date;
391             }
392              
393             sub _remove_strings
394             {
395 11143     11143   23030 my ( $self , $plug , $date ) = @_;
396 11143         29020 my @rs = $plug->remove_strings;
397 11143         24644 foreach my $rs ( @rs )
398             {
399 26093 100       117621 if ( $date =~ m{$rs}mxi )
400             {
401 162 50       480 printf( "# removing string: %s\n", $rs ) if $ENV{DFF_DEBUG};
402              
403 162         1162 $date =~ s{$rs}{ }gmix;
404             }
405             }
406 11143         31429 $date =~ s{\A\s+}{};
407 11143         26508 $date =~ s{\s+\z}{};
408              
409 11143         34112 return $date;
410             }
411              
412             sub _locate_time
413             {
414 11143     11143   23020 my ( $self , $plug , $date ) = @_;
415 11143         30860 $date = $plug->parse_time( $date );
416 11143         23655 return $date;
417             }
418              
419             1;
420              
421             __END__
422              
423             =encoding utf-8
424              
425             =head1 NAME
426              
427             DateTime::Format::Flexible::lang - base language module to handle plugins for DateTime::Format::Flexible.
428              
429             =head1 DESCRIPTION
430              
431             You should not need to use this module directly
432              
433             =head2 new
434              
435             Instantiate a new instance of this module.
436              
437             =head2 plugins
438              
439             Returns a list of available language plugins.
440              
441             =head1 AUTHOR
442              
443             Tom Heady
444             CPAN ID: thinc
445             Punch, Inc.
446             cpan@punch.net
447             http://www.punch.net/
448              
449             =head1 COPYRIGHT & LICENSE
450              
451             Copyright 2011 Tom Heady.
452              
453             This program is free software; you can redistribute it and/or
454             modify it under the terms of either:
455              
456             =over 4
457              
458             =item * the GNU General Public License as published by the Free
459             Software Foundation; either version 1, or (at your option) any
460             later version, or
461              
462             =item * the Artistic License.
463              
464             =back
465              
466             =head1 SEE ALSO
467              
468             F<DateTime::Format::Flexible>
469              
470             =cut