File Coverage

blib/lib/DateTime/Format/Flexible/lang.pm
Criterion Covered Total %
statement 218 221 98.6
branch 80 102 78.4
condition 12 12 100.0
subroutine 19 19 100.0
pod 2 2 100.0
total 331 356 92.9


line stmt bran cond sub pod time code
1             package DateTime::Format::Flexible::lang;
2              
3 22     22   167 use strict;
  22         47  
  22         719  
4 22     22   107 use warnings;
  22         46  
  22         725  
5              
6 22     22   115 use List::MoreUtils 'any';
  22         44  
  22         242  
7              
8             sub new
9             {
10 6358     6358 1 1505781 my ( $class , %params ) = @_;
11 6358         14698 my $self = bless \%params , $class;
12              
13 6358 100 100     17817 if ($self->{lang} and not ref($self->{lang}) eq 'ARRAY')
14             {
15 137         296 $self->{lang} = [$self->{lang}];
16             }
17              
18             $self->{_plugins} = [
19 6358         16151 'DateTime::Format::Flexible::lang::de',
20             'DateTime::Format::Flexible::lang::en',
21             'DateTime::Format::Flexible::lang::es',
22             ];
23              
24 6358         9107 foreach my $plugin (@{$self->{_plugins}}) {
  6358         15025  
25 19074         31352 my $path = $plugin . ".pm";
26 19074         61501 $path =~ s{::}{/}g;
27 19074         103984 require $path;
28             }
29 6358         16152 return $self;
30             }
31              
32 3811     3811 1 5172 sub plugins {return @{$_[0]->{_plugins}}}
  3811         10448  
33              
34             sub _cleanup
35             {
36 3811     3811   9404 my ( $self , $date , $p ) = @_;
37 3811         8408 foreach my $plug ( $self->plugins )
38             {
39 11433 100       25668 if ( $self->{lang} )
40             {
41 417         1269 my ( $lang ) = $plug =~ m{(\w{2}\z)}mx;
42 417 100   417   1221 if ( not any { $_ eq $lang } @{ $self->{lang} } )
  417         945  
  417         1097  
43             {
44 278 50       489 printf( "# skipping %s\n", $plug ) if $ENV{DFF_DEBUG};
45 278         872 next;
46             }
47             }
48 11155 50       19387 printf( "# not skipping %s\n", $plug ) if $ENV{DFF_DEBUG};
49              
50 11155 50       19121 printf( "# before math: %s\n", $date ) if $ENV{DFF_DEBUG};
51 11155         23980 $date = $self->_do_math( $plug , $date );
52 11155 50       24150 printf( "# before string_dates: %s\n", $date ) if $ENV{DFF_DEBUG};
53 11155         24580 $date = $self->_string_dates( $plug , $date );
54 11155 50       29837 printf( "# before fix_alpha_month: %s\n", $date ) if $ENV{DFF_DEBUG};
55 11155         24831 ( $date , $p ) = $self->_fix_alpha_month( $plug , $date , $p );
56 11155 50       30219 printf( "# before remove_day_names: %s\n", $date ) if $ENV{DFF_DEBUG};
57 11155         26223 $date = $self->_remove_day_names( $plug , $date );
58 11155 50       26682 printf( "# before fix_hours: %s\n", $date ) if $ENV{DFF_DEBUG};
59 11155         24211 $date = $self->_fix_hours( $plug , $date );
60 11155 50       23914 printf( "# before remove_strings: %s\n", $date ) if $ENV{DFF_DEBUG};
61 11155         22460 $date = $self->_remove_strings( $plug , $date );
62 11155 50       22578 printf( "# before locate_time: %s\n", $date ) if $ENV{DFF_DEBUG};
63 11155         21087 $date = $self->_locate_time( $plug , $date );
64 11155 50       22091 printf( "# before fix_internal_tz: %s\n", $date ) if $ENV{DFF_DEBUG};
65 11155         23283 ( $date , $p ) = $self->_fix_internal_tz( $plug , $date , $p );
66 11155 50       25066 printf( "# finished: %s\n", $date ) if $ENV{DFF_DEBUG};
67             }
68 3811         11034 return ( $date , $p );
69             }
70              
71             sub _fix_internal_tz
72             {
73 11155     11155   19474 my ( $self , $plug , $date , $p ) = @_;
74 11155         24459 my %tzs = $plug->timezone_map;
75 11155         33847 while( my( $orig_tz , $new_tz ) = each ( %tzs ) )
76             {
77 93573 100       527076 if( $date =~ m{$orig_tz}mxi )
78             {
79 5         12 $p->{ time_zone } = $new_tz;
80 5         23 $date =~ s{$orig_tz}{}mxi;
81 5         13 $date =~ s{\(\)}{}g; # remove empty parens
82 5         85 return ( $date , $p );
83             }
84             }
85 11150         34821 return ( $date , $p );
86             }
87              
88             sub _do_math
89             {
90 11155     11155   20320 my ( $self , $plug , $date ) = @_;
91              
92 11155         37805 my %relative_strings = $plug->relative;
93 11155         31112 my $day_strings = $plug->days;
94 11155         27895 my %month_strings = $plug->months;
95              
96 11155         79270 my $instructions = {
97             ago => {direction => 'past', units => 1},
98             from => {direction => 'future', units => 1},
99             last => {direction => 'past'},
100             next => {direction => 'future'},
101             };
102              
103 11155         28861 foreach my $keyword (keys %relative_strings)
104             {
105 44620         63964 my $rx = $relative_strings{$keyword};
106              
107 44620 50       73220 next if not (exists $instructions->{$keyword});
108              
109 44620         57127 my $has_units = $instructions->{$keyword}->{units};
110 44620         54897 my $direction = $instructions->{$keyword}->{direction};
111              
112 44620 100       205967 if ( $date =~ m{$rx}mix )
113             {
114 101         352 $date =~ s{$rx}{}mix;
115 101 100       220 if ($has_units)
116             {
117 24         62 $date = $self->_set_units( $plug , $date, $direction );
118             }
119             else
120             {
121 77         127 foreach my $set (@{$day_strings})
  77         153  
122             {
123 1155         1556 foreach my $day (keys %{$set})
  1155         2135  
124             {
125              
126 1155 100       5847 if ($date =~ m{$day}mix)
127             {
128 29         77 $date = $self->_set_day( $plug , $date , $day , $direction );
129 29         325 $date =~ s{$day}{}mix;
130             }
131             }
132             }
133 77         220 foreach my $month (keys %month_strings)
134             {
135 924 100       5698 if ($date =~ m{$month}mix)
136             {
137 48         128 $date = $self->_set_month( $plug , $date , $month , $direction );
138 48         634 $date =~ s{$month}{}mix;
139             }
140             }
141             }
142 101 50       312 printf("# after removing rx (%s): [%s]\n", $rx, $date) if $ENV{DFF_DEBUG};
143              
144 101         372 $date =~ s{$keyword}{}mx;
145 101         380 $date =~ s{\s+}{ }gm;
146 101         324 $date =~ s{\s+\z}{}gm;
147 101 50       265 printf("# after removing keyword (%s): [%s]\n", $keyword, $date) if $ENV{DFF_DEBUG};
148             }
149              
150             }
151              
152 11155         83243 return $date;
153             }
154              
155             sub _set_units
156             {
157 24     24   54 my ( $self , $plug , $date , $direction ) = @_;
158              
159 24         65 my %strings = $plug->math_strings;
160 24 100       124 if ( my ( $amount , $unit ) = $date =~ m{(\d+)\s+([^\s]+)}mx )
161             {
162 21 50       66 printf( "# %s => %s\n", $amount, $unit ) if $ENV{DFF_DEBUG};
163 21 50       54 if ( exists( $strings{$unit} ) )
164             {
165 21         64 my $base_dt = DateTime::Format::Flexible->base->clone;
166              
167 21 100       984 if ( $direction eq 'past' )
168             {
169 13         49 $base_dt->subtract( $strings{$unit} => $amount );
170             }
171 21 100       12628 if ( $direction eq 'future' )
172             {
173 8         27 $base_dt->add( $strings{$unit} => $amount );
174             }
175 21         7145 $date =~ s{\s{0,}$amount\s+$unit\s{0,}}{}mx;
176              
177 21 50       74 if ($ENV{DFF_DEBUG})
178             {
179 0         0 printf("# found: %s\n", $strings{$unit}) ;
180 0         0 printf("# after removing amount, unit: [%s]\n", $date);
181             }
182              
183 21         62 $date = $base_dt->datetime . ' ' . $date;
184             }
185             }
186              
187 24         585 return $date;
188             }
189              
190             sub _set_day
191             {
192 29     29   65 my ( $self , $plug , $date , $day , $direction ) = @_;
193              
194 29         76 my $base_dt = DateTime::Format::Flexible->base->clone;
195 29         434 my $dow = $base_dt->day_of_week;
196 29         124 my $date_dow = $self->_alpha_day_to_int($plug, $day);
197              
198 29 100       62 if ( $direction eq 'past' )
199             {
200 14         23 my $amount = $dow - $date_dow;
201 14 100       23 if ($amount < 1) {$amount = 7 + $amount}
  12         16  
202 14 50       29 printf("# subtracting %s days\n", $amount) if $ENV{DFF_DEBUG};
203              
204 14         41 my $ret = $base_dt->subtract( 'days' => $amount )->truncate( to => 'day' );
205 14         16059 $date = $ret->datetime . ' ' . $date;
206              
207             }
208 29 100       388 if ( $direction eq 'future' )
209             {
210 15         22 my $amount = $date_dow - $dow;
211 15 100       28 if ($amount < 1) {$amount = 7 + $amount}
  4         6  
212 15 50       32 printf("# adding %s days\n", $amount) if $ENV{DFF_DEBUG};
213              
214 15         43 my $ret = $base_dt->add( 'days' => $amount )->truncate( to => 'day' );
215 15         15466 $date = $ret->datetime . ' ' . $date;
216             }
217              
218              
219 29         430 return $date;
220             }
221              
222             sub _set_month
223             {
224 48     48   101 my ( $self , $plug , $date , $month , $direction ) = @_;
225              
226 48         120 my %month_strings = $plug->months;
227              
228 48         295 my $base_dt = DateTime::Format::Flexible->base->clone;
229 48         724 my $mon = $base_dt->month;
230 48         220 my $date_mon = $month_strings{$month};
231              
232 48 50       102 printf("# setting month to: %s\n", $date_mon) if $ENV{DFF_DEBUG};
233              
234 48         127 $base_dt->set_month($date_mon);
235 48 100 100     18366 if ($direction eq 'past' and $date_mon >= $mon)
236             {
237 15         36 $base_dt->set_year($base_dt->year - 1);
238             }
239 48 100 100     5555 if ($direction eq 'future' and $date_mon <= $mon)
240             {
241 11         32 $base_dt->set_year($base_dt->year + 1);
242             }
243 48         4070 $base_dt->truncate( to => 'month' );
244 48 50       10179 printf("# set year to: %s\n", $base_dt->year) if $ENV{DFF_DEBUG};
245              
246 48         132 $date = $base_dt->datetime . ' ' . $date;
247              
248 48         1199 return $date;
249             }
250              
251             sub _string_dates
252             {
253 11155     11155   20036 my ( $self , $plug , $date ) = @_;
254 11155         28784 my %strings = $plug->string_dates;
255 11155         39933 foreach my $key ( keys %strings )
256             {
257 96723 100       508742 if ( $date =~ m{\Q$key\E}mxi )
258             {
259 45         121 my $new_value = $strings{$key}->();
260 45         15411 $date =~ s{\Q$key\E}{$new_value}mix;
261             }
262             }
263              
264 11155         38686 my %day_numbers = $plug->day_numbers;
265 11155         59399 foreach my $key ( keys %day_numbers )
266             {
267 416685 100       697264 if (index(lc($date), lc($key)) >= 0)
268             {
269 8         17 my $new_value = $day_numbers{$key};
270 8         72 $date =~ s{$key}{n${new_value}n}mix;
271             }
272             }
273 11155         169847 return $date;
274             }
275              
276             # turn month names into month numbers with surrounding X
277             # Sep => X9X
278             sub _fix_alpha_month
279             {
280 11155     11155   23631 my ( $self , $plug , $date , $p ) = @_;
281 11155         28159 my %months = $plug->months;
282 11155         59085 while( my( $month_name , $month_number ) = each ( %months ) )
283             {
284 134665 100       3009555 if( $date =~ m{\b$month_name\b}mxi )
    100          
    100          
285             {
286 2409         6443 $p->{ month } = $month_number;
287 2409         17804 $date =~ s{\b$month_name\b}{X${month_number}X}mxi;
288              
289 2409         14061 return ( $date , $p );
290             }
291             elsif ( $date =~ m{\d$month_name}mxi )
292             {
293 11         36 $p->{ month } = $month_number;
294 11         89 $date =~ s{(\d)$month_name}{$1X${month_number}X}mxi;
295              
296 11         61 return ( $date , $p );
297             }
298              
299             elsif( $date =~ m{\b$month_name\d.*\b}mxi )
300             {
301 4         11 $p->{ month } = $month_number;
302 4         40 $date =~ s{\b$month_name(\d.*)\b}{X${month_number}X$1}mxi;
303              
304 4         23 return ( $date , $p );
305             }
306             }
307 8731         39600 return ( $date , $p );
308             }
309              
310             # remove any day names, we do not need them
311             sub _remove_day_names
312             {
313 11155     11155   19931 my ( $self , $plug , $date ) = @_;
314 11155         31309 my $days = $plug->days;
315 11155         16986 foreach my $set (@{$days})
  11155         21671  
316             {
317 136145         185995 foreach my $day_name ( keys %{$set} )
  136145         255041  
318             {
319             # if the day name is by itself, make it the upcoming day
320             # eg: monday = next monday
321 136145 100 100     446664 if (( lc($date) eq lc($day_name)) or (index(lc($date), lc($day_name) . ' at') >= 0 ))
322             {
323 16         55 my $dt = $self->{base}->clone->truncate( to => 'day' );
324 16         3707 my $date_dow = $set->{$day_name};
325              
326 16 100       38 if ( $date_dow == $dt->dow )
    100          
327             {
328 2         10 my $str = $dt->ymd;
329 2         43 $date =~ s{$day_name}{$str}i;
330 2         19 return $date;
331             }
332             elsif ( $date_dow > $dt->dow )
333             {
334 12         77 $dt->add( days => $date_dow - $dt->dow );
335 12         9867 my $str = $dt->ymd;
336 12         277 $date =~ s{$day_name}{$str}i;
337 12         108 return $date;
338             }
339             else
340             {
341 2         19 $dt->add( days => $date_dow - $dt->dow + 7 );
342 2         1638 my $str = $dt->ymd;
343 2         46 $date =~ s{$day_name}{$str}i;
344 2         18 return $date;
345             }
346             }
347             # otherwise, just strip it out
348 136129 100       896791 if ( $date =~ m{\b$day_name\b}mxi )
349             {
350 234         1276 $date =~ s{$day_name,?}{}gmix;
351 234         1494 return $date;
352             }
353             }
354             }
355 10905         55751 return $date;
356             }
357              
358             sub _alpha_day_to_int
359             {
360 29     29   48 my ( $self, $plug, $day ) = @_;
361              
362 29         65 my $day_strings = $plug->days;
363 29         49 foreach my $set (@{$day_strings})
  29         46  
364             {
365 238         261 foreach my $key (keys %{$set})
  238         390  
366             {
367 238 100       460 if (lc($key) eq lc($day))
368             {
369 29         145 return $set->{$key};
370             }
371             }
372             }
373 0         0 return;
374             }
375              
376             # fix noon and midnight, named hours
377             sub _fix_hours
378             {
379 11155     11155   20721 my ( $self , $plug , $date ) = @_;
380 11155         27769 my %hours = $plug->hours;
381 11155         29628 foreach my $hour ( keys %hours )
382             {
383 75173 100       362803 if ( $date =~ m{$hour}mxi )
384             {
385 34         71 my $realtime = $hours{ $hour };
386 34         81 $date =~ s{T[^\s]+}{};
387 34         186 $date =~ s{$hour}{${realtime}}gmix;
388 34         154 return $date;
389             }
390             }
391 11121         34797 return $date;
392             }
393              
394             sub _remove_strings
395             {
396 11155     11155   19238 my ( $self , $plug , $date ) = @_;
397 11155         25222 my @rs = $plug->remove_strings;
398 11155         20120 foreach my $rs ( @rs )
399             {
400 26121 100       104072 if ( $date =~ m{$rs}mxi )
401             {
402 162 50       352 printf( "# removing string: %s\n", $rs ) if $ENV{DFF_DEBUG};
403              
404 162         1012 $date =~ s{$rs}{ }gmix;
405             }
406             }
407 11155         24821 $date =~ s{\A\s+}{};
408 11155         20954 $date =~ s{\s+\z}{};
409              
410 11155         27149 return $date;
411             }
412              
413             sub _locate_time
414             {
415 11155     11155   19594 my ( $self , $plug , $date ) = @_;
416 11155         26501 $date = $plug->parse_time( $date );
417 11155         18203 return $date;
418             }
419              
420             1;
421              
422             __END__
423              
424             =encoding utf-8
425              
426             =head1 NAME
427              
428             DateTime::Format::Flexible::lang - base language module to handle plugins for DateTime::Format::Flexible.
429              
430             =head1 DESCRIPTION
431              
432             You should not need to use this module directly
433              
434             =head2 new
435              
436             Instantiate a new instance of this module.
437              
438             =head2 plugins
439              
440             Returns a list of available language plugins.
441              
442             =head1 AUTHOR
443              
444             Tom Heady
445             CPAN ID: thinc
446             Punch, Inc.
447             cpan@punch.net
448             http://www.punch.net/
449              
450             =head1 COPYRIGHT & LICENSE
451              
452             Copyright 2011 Tom Heady.
453              
454             This program is free software; you can redistribute it and/or
455             modify it under the terms of either:
456              
457             =over 4
458              
459             =item * the GNU General Public License as published by the Free
460             Software Foundation; either version 1, or (at your option) any
461             later version, or
462              
463             =item * the Artistic License.
464              
465             =back
466              
467             =head1 SEE ALSO
468              
469             F<DateTime::Format::Flexible>
470              
471             =cut