File Coverage

blib/lib/DateTime/Format/Flexible/lang.pm
Criterion Covered Total %
statement 219 222 98.6
branch 82 104 78.8
condition 15 15 100.0
subroutine 18 18 100.0
pod 2 2 100.0
total 336 361 93.0


line stmt bran cond sub pod time code
1             package DateTime::Format::Flexible::lang;
2              
3 22     22   212 use strict;
  22         63  
  22         975  
4 22     22   138 use warnings;
  22         47  
  22         79910  
5              
6             #use List::MoreUtils 'any';
7              
8             sub new
9             {
10 6312     6312 1 2123434 my ( $class , %params ) = @_;
11 6312         18930 my $self = bless \%params , $class;
12              
13 6312 100 100     27074 if ($self->{lang} and not ref($self->{lang}) eq 'ARRAY')
14             {
15 137         460 $self->{lang} = [$self->{lang}];
16             }
17              
18             $self->{_plugins} = [
19 6312         24791 'DateTime::Format::Flexible::lang::de',
20             'DateTime::Format::Flexible::lang::en',
21             'DateTime::Format::Flexible::lang::es',
22             ];
23              
24 6312         11072 foreach my $plugin (@{$self->{_plugins}}) {
  6312         20878  
25 18936         36413 my $path = $plugin . ".pm";
26 18936         83399 $path =~ s{::}{/}g;
27 18936         134611 require $path;
28             }
29 6312         23388 return $self;
30             }
31              
32 4044     4044 1 7063 sub plugins {return @{$_[0]->{_plugins}}}
  4044         16589  
33              
34             sub _cleanup
35             {
36 4044     4044   12007 my ( $self , $date , $p ) = @_;
37 4044         13232 PLUGIN: foreach my $plug ( $self->plugins )
38             {
39 12132 100       33658 if ( $self->{lang} )
40             {
41 417         1729 my ( $lang ) = $plug =~ m{(\w{2}\z)}mx;
42 417         761 foreach my $l (@{ $self->{lang} }) {
  417         1018  
43 417 100       1688 if ( not $l eq $lang ) {
44 278 50       673 printf( "# skipping %s\n", $plug ) if $ENV{DFF_DEBUG};
45 278         928 next PLUGIN;
46             }
47             }
48             }
49 11854 50       25060 printf( "# not skipping %s\n", $plug ) if $ENV{DFF_DEBUG};
50              
51 11854 50       27442 printf( "# before math: %s\n", $date ) if $ENV{DFF_DEBUG};
52 11854         45402 $date = $self->_do_math( $plug , $date );
53 11854 50       35209 printf( "# before string_dates: %s\n", $date ) if $ENV{DFF_DEBUG};
54 11854         37142 $date = $self->_string_dates( $plug , $date );
55 11854 50       42759 printf( "# before fix_alpha_month: %s\n", $date ) if $ENV{DFF_DEBUG};
56 11854         40877 ( $date , $p ) = $self->_fix_alpha_month( $plug , $date , $p );
57 11854 50       39292 printf( "# before remove_day_names: %s\n", $date ) if $ENV{DFF_DEBUG};
58 11854         43881 $date = $self->_remove_day_names( $plug , $date );
59 11854 50       34285 printf( "# before fix_hours: %s\n", $date ) if $ENV{DFF_DEBUG};
60 11854         40373 $date = $self->_fix_hours( $plug , $date );
61 11854 50       33379 printf( "# before remove_strings: %s\n", $date ) if $ENV{DFF_DEBUG};
62 11854         34315 $date = $self->_remove_strings( $plug , $date );
63 11854 50       30715 printf( "# before locate_time: %s\n", $date ) if $ENV{DFF_DEBUG};
64 11854         31228 $date = $self->_locate_time( $plug , $date );
65 11854 50       31828 printf( "# before fix_internal_tz: %s\n", $date ) if $ENV{DFF_DEBUG};
66 11854         32492 ( $date , $p ) = $self->_check_internal_tz( $plug , $date , $p );
67 11854 50       34483 printf( "# finished: %s\n", $date ) if $ENV{DFF_DEBUG};
68             }
69 4044         18741 return ( $date , $p );
70             }
71              
72             sub _check_internal_tz
73             {
74 11854     11854   26579 my ( $self , $plug , $date , $p ) = @_;
75 11854         35708 my %tzs = $plug->timezone_map;
76 11854         47901 while( my( $orig_tz , $new_tz ) = each ( %tzs ) )
77             {
78 99274 100       590685 if( $date =~ m{$orig_tz}mxi )
79             {
80 5         97 return ($self->_fix_internal_tz( $date , $p , $orig_tz , $new_tz ));
81             }
82 99269 100 100     361630 if( exists $p->{time_zone} and $p->{time_zone} eq $orig_tz )
83             {
84 29         152 return ($self->_fix_internal_tz( $date , $p , $orig_tz , $new_tz ));
85             }
86             }
87 11820         53552 return ( $date , $p );
88             }
89              
90             sub _fix_internal_tz
91             {
92 34     34   129 my ( $self , $date , $p , $orig_tz , $new_tz ) = @_;
93 34         94 $p->{ time_zone } = $new_tz;
94 34         172 $date =~ s{$orig_tz}{}mxi;
95 34         139 $date =~ s{\(\)}{}g; # remove empty parens
96 34         274 return ( $date , $p );
97             }
98              
99             sub _do_math
100             {
101 11854     11854   27504 my ( $self , $plug , $date ) = @_;
102              
103 11854         55112 my %relative_strings = $plug->relative;
104 11854         46784 my $day_strings = $plug->days;
105 11854         38532 my %month_strings = $plug->months;
106              
107 11854         124649 my $instructions = {
108             ago => {direction => 'past', units => 1},
109             from => {direction => 'future', units => 1},
110             last => {direction => 'past'},
111             next => {direction => 'future'},
112             };
113              
114 11854         41174 foreach my $keyword (keys %relative_strings)
115             {
116 47416         83816 my $rx = $relative_strings{$keyword};
117              
118 47416 50       98730 next if not (exists $instructions->{$keyword});
119              
120 47416         92133 my $has_units = $instructions->{$keyword}->{units};
121 47416         78948 my $direction = $instructions->{$keyword}->{direction};
122              
123 47416 100       315871 if ( $date =~ m{$rx}mix )
124             {
125 101         512 $date =~ s{$rx}{}mix;
126 101 100       302 if ($has_units)
127             {
128 24         103 $date = $self->_set_units( $plug , $date, $direction );
129             }
130             else
131             {
132 77         207 foreach my $set (@{$day_strings})
  77         194  
133             {
134 1155         1800 foreach my $day (keys %{$set})
  1155         2817  
135             {
136              
137 1155 100       8167 if ($date =~ m{$day}mix)
138             {
139 29         134 $date = $self->_set_day( $plug , $date , $day , $direction );
140 29         564 $date =~ s{$day}{}mix;
141             }
142             }
143             }
144 77         375 foreach my $month (keys %month_strings)
145             {
146 924 100       9514 if ($date =~ m{$month}mix)
147             {
148 48         217 $date = $self->_set_month( $plug , $date , $month , $direction );
149 48         1253 $date =~ s{$month}{}mix;
150             }
151             }
152             }
153 101 50       436 printf("# after removing rx (%s): [%s]\n", $rx, $date) if $ENV{DFF_DEBUG};
154              
155 101         674 $date =~ s{$keyword}{}mx;
156 101         508 $date =~ s{\s+}{ }gm;
157 101         508 $date =~ s{\s+\z}{}gm;
158 101 50       390 printf("# after removing keyword (%s): [%s]\n", $keyword, $date) if $ENV{DFF_DEBUG};
159             }
160              
161             }
162              
163 11854         154346 return $date;
164             }
165              
166             sub _set_units
167             {
168 24     24   89 my ( $self , $plug , $date , $direction ) = @_;
169              
170 24         88 my %strings = $plug->math_strings;
171 24 100       269 if ( my ( $amount , $unit ) = $date =~ m{(\d+)\s+([^\s]+)}mx )
172             {
173 21 50       73 printf( "# %s => %s\n", $amount, $unit ) if $ENV{DFF_DEBUG};
174 21 50       67 if ( exists( $strings{$unit} ) )
175             {
176 21         79 my $base_dt = DateTime::Format::Flexible->base->clone;
177              
178 21 100       1653 if ( $direction eq 'past' )
179             {
180 13         71 $base_dt->subtract( $strings{$unit} => $amount );
181             }
182 21 100       18347 if ( $direction eq 'future' )
183             {
184 8         40 $base_dt->add( $strings{$unit} => $amount );
185             }
186 21         9590 $date =~ s{\s{0,}$amount\s+$unit\s{0,}}{}mx;
187              
188 21 50       103 if ($ENV{DFF_DEBUG})
189             {
190 0         0 printf("# found: %s\n", $strings{$unit}) ;
191 0         0 printf("# after removing amount, unit: [%s]\n", $date);
192             }
193              
194 21         114 $date = $base_dt->datetime . ' ' . $date;
195             }
196             }
197              
198 24         868 return $date;
199             }
200              
201             sub _set_day
202             {
203 29     29   123 my ( $self , $plug , $date , $day , $direction ) = @_;
204              
205 29         128 my $base_dt = DateTime::Format::Flexible->base->clone;
206 29         604 my $dow = $base_dt->day_of_week;
207 29         190 my $date_dow = $self->_alpha_day_to_int($plug, $day);
208              
209 29 100       83 if ( $direction eq 'past' )
210             {
211 14         22 my $amount = $dow - $date_dow;
212 14 100       31 if ($amount < 1) {$amount = 7 + $amount}
  12         19  
213 14 50       37 printf("# subtracting %s days\n", $amount) if $ENV{DFF_DEBUG};
214              
215 14         61 my $ret = $base_dt->subtract( 'days' => $amount )->truncate( to => 'day' );
216 14         18268 $date = $ret->datetime . ' ' . $date;
217              
218             }
219 29 100       424 if ( $direction eq 'future' )
220             {
221 15         31 my $amount = $date_dow - $dow;
222 15 100       43 if ($amount < 1) {$amount = 7 + $amount}
  4         9  
223 15 50       73 printf("# adding %s days\n", $amount) if $ENV{DFF_DEBUG};
224              
225 15         70 my $ret = $base_dt->add( 'days' => $amount )->truncate( to => 'day' );
226 15         26865 $date = $ret->datetime . ' ' . $date;
227             }
228              
229              
230 29         1326 return $date;
231             }
232              
233             sub _set_month
234             {
235 48     48   177 my ( $self , $plug , $date , $month , $direction ) = @_;
236              
237 48         196 my %month_strings = $plug->months;
238              
239 48         391 my $base_dt = DateTime::Format::Flexible->base->clone;
240 48         1200 my $mon = $base_dt->month;
241 48         476 my $date_mon = $month_strings{$month};
242              
243 48 50       153 printf("# setting month to: %s\n", $date_mon) if $ENV{DFF_DEBUG};
244              
245 48         202 $base_dt->set_month($date_mon);
246 48 100 100     44375 if ($direction eq 'past' and $date_mon >= $mon)
247             {
248 15         69 $base_dt->set_year($base_dt->year - 1);
249             }
250 48 100 100     8595 if ($direction eq 'future' and $date_mon <= $mon)
251             {
252 11         38 $base_dt->set_year($base_dt->year + 1);
253             }
254 48         7060 $base_dt->truncate( to => 'month' );
255 48 50       16275 printf("# set year to: %s\n", $base_dt->year) if $ENV{DFF_DEBUG};
256              
257 48         172 $date = $base_dt->datetime . ' ' . $date;
258              
259 48         1972 return $date;
260             }
261              
262             sub _string_dates
263             {
264 11854     11854   28206 my ( $self , $plug , $date ) = @_;
265 11854         42121 my %strings = $plug->string_dates;
266 11854         57797 foreach my $key ( keys %strings )
267             {
268 102781 100       905959 if ( $date =~ m{\Q$key\E}mxi )
269             {
270 45         141 my $new_value = $strings{$key}->();
271 45         20578 $date =~ s{\Q$key\E}{$new_value}mix;
272             }
273             }
274              
275 11854         60239 my %day_numbers = $plug->day_numbers;
276 11854         89540 foreach my $key ( keys %day_numbers )
277             {
278 442781 100       949018 if (index(lc($date), lc($key)) >= 0)
279             {
280 8         18 my $new_value = $day_numbers{$key};
281 8         133 $date =~ s{$key}{n${new_value}n}mix;
282             }
283             }
284 11854         329391 return $date;
285             }
286              
287             # turn month names into month numbers with surrounding X
288             # Sep => X9X
289             sub _fix_alpha_month
290             {
291 11854     11854   31879 my ( $self , $plug , $date , $p ) = @_;
292 11854         39017 my %months = $plug->months;
293 11854         86086 while( my( $month_name , $month_number ) = each ( %months ) )
294             {
295 143920 100       6073576 if( $date =~ m{\b$month_name\b}mxi )
    100          
    100          
296             {
297 2409         9263 $p->{ month } = $month_number;
298 2409         30342 $date =~ s{\b$month_name\b}{X${month_number}X}mxi;
299              
300 2409         21405 return ( $date , $p );
301             }
302             elsif ( $date =~ m{\d$month_name}mxi )
303             {
304 11         58 $p->{ month } = $month_number;
305 11         170 $date =~ s{(\d)$month_name}{$1X${month_number}X}mxi;
306              
307 11         101 return ( $date , $p );
308             }
309              
310             elsif( $date =~ m{\b$month_name\d.*\b}mxi )
311             {
312 4         8 $p->{ month } = $month_number;
313 4         47 $date =~ s{\b$month_name(\d.*)\b}{X${month_number}X$1}mxi;
314              
315 4         25 return ( $date , $p );
316             }
317             }
318 9430         73418 return ( $date , $p );
319             }
320              
321             # remove any day names, we do not need them
322             sub _remove_day_names
323             {
324 11854     11854   30247 my ( $self , $plug , $date ) = @_;
325 11854         62298 my $days = $plug->days;
326 11854         22405 foreach my $set (@{$days})
  11854         31103  
327             {
328 144766         221801 foreach my $day_name ( keys %{$set} )
  144766         290149  
329             {
330             # if the day name is by itself, make it the upcoming day
331             # eg: monday = next monday
332 144766 100 100     583740 if (( lc($date) eq lc($day_name)) or (index(lc($date), lc($day_name) . ' at') >= 0 ))
333             {
334 16         81 my $dt = $self->{base}->clone->truncate( to => 'day' );
335 16         6004 my $date_dow = $set->{$day_name};
336              
337 16 100       55 if ( $date_dow == $dt->dow )
    100          
338             {
339 2         13 my $str = $dt->ymd;
340 2         68 $date =~ s{$day_name}{$str}i;
341 2         33 return $date;
342             }
343             elsif ( $date_dow > $dt->dow )
344             {
345 12         145 $dt->add( days => $date_dow - $dt->dow );
346 12         16037 my $str = $dt->ymd;
347 12         408 $date =~ s{$day_name}{$str}i;
348 12         204 return $date;
349             }
350             else
351             {
352 2         24 $dt->add( days => $date_dow - $dt->dow + 7 );
353 2         2663 my $str = $dt->ymd;
354 2         69 $date =~ s{$day_name}{$str}i;
355 2         33 return $date;
356             }
357             }
358             # otherwise, just strip it out
359 144750 100       1315026 if ( $date =~ m{\b$day_name\b}mxi )
360             {
361 234         2275 $date =~ s{$day_name,?}{}gmix;
362 234         2399 return $date;
363             }
364             }
365             }
366 11604         94762 return $date;
367             }
368              
369             sub _alpha_day_to_int
370             {
371 29     29   69 my ( $self, $plug, $day ) = @_;
372              
373 29         138 my $day_strings = $plug->days;
374 29         51 foreach my $set (@{$day_strings})
  29         63  
375             {
376 238         2248 foreach my $key (keys %{$set})
  238         417  
377             {
378 238 100       580 if (lc($key) eq lc($day))
379             {
380 29         228 return $set->{$key};
381             }
382             }
383             }
384 0         0 return;
385             }
386              
387             # fix noon and midnight, named hours
388             sub _fix_hours
389             {
390 11854     11854   28325 my ( $self , $plug , $date ) = @_;
391 11854         43962 my %hours = $plug->hours;
392 11854         42754 foreach my $hour ( keys %hours )
393             {
394 79824 100       518970 if ( $date =~ m{$hour}mxi )
395             {
396 34         109 my $realtime = $hours{ $hour };
397 34         105 $date =~ s{T[^\s]+}{};
398 34         276 $date =~ s{$hour}{${realtime}}gmix;
399 34         302 return $date;
400             }
401             }
402 11820         55648 return $date;
403             }
404              
405             sub _remove_strings
406             {
407 11854     11854   25748 my ( $self , $plug , $date ) = @_;
408 11854         38921 my @rs = $plug->remove_strings;
409 11854         25474 foreach my $rs ( @rs )
410             {
411 27752 100       147907 if ( $date =~ m{$rs}mxi )
412             {
413 162 50       584 printf( "# removing string: %s\n", $rs ) if $ENV{DFF_DEBUG};
414              
415 162         1634 $date =~ s{$rs}{ }gmix;
416             }
417             }
418 11854         35079 $date =~ s{\A\s+}{};
419 11854         31371 $date =~ s{\s+\z}{};
420              
421 11854         38523 return $date;
422             }
423              
424             sub _locate_time
425             {
426 11854     11854   23845 my ( $self , $plug , $date ) = @_;
427 11854         38810 $date = $plug->parse_time( $date );
428 11854         23314 return $date;
429             }
430              
431             1;
432              
433             __END__
434              
435             =encoding utf-8
436              
437             =head1 NAME
438              
439             DateTime::Format::Flexible::lang - base language module to handle plugins for DateTime::Format::Flexible.
440              
441             =head1 DESCRIPTION
442              
443             You should not need to use this module directly
444              
445             =head2 new
446              
447             Instantiate a new instance of this module.
448              
449             =head2 plugins
450              
451             Returns a list of available language plugins.
452              
453             =head1 AUTHOR
454              
455             Tom Heady
456             CPAN ID: thinc
457             Punch, Inc.
458             cpan@punch.net
459             http://www.punch.net/
460              
461             =head1 COPYRIGHT & LICENSE
462              
463             Copyright 2011 Tom Heady.
464              
465             This program is free software; you can redistribute it and/or
466             modify it under the terms of either:
467              
468             =over 4
469              
470             =item * the GNU General Public License as published by the Free
471             Software Foundation; either version 1, or (at your option) any
472             later version, or
473              
474             =item * the Artistic License.
475              
476             =back
477              
478             =head1 SEE ALSO
479              
480             F<DateTime::Format::Flexible>
481              
482             =cut