File Coverage

blib/lib/Catmandu/Fix/datetime_format.pm
Criterion Covered Total %
statement 59 63 93.6
branch 6 8 75.0
condition 2 3 66.6
subroutine 15 15 100.0
pod 0 1 0.0
total 82 90 91.1


line stmt bran cond sub pod time code
1             package Catmandu::Fix::datetime_format;
2 2     2   179321 use Catmandu::Sane;
  2         117569  
  2         13  
3 2     2   437 use Moo;
  2         3  
  2         44  
4 2     2   473 use Catmandu::Util qw(:is :check :array);
  2         6  
  2         835  
5 2     2   1153 use DateTime::Format::Strptime;
  2         613644  
  2         9  
6 2     2   163 use DateTime::TimeZone;
  2         3  
  2         33  
7 2     2   6 use DateTime::Locale;
  2         3  
  2         22  
8 2     2   6 use DateTime;
  2         3  
  2         1958  
9             our $VERSION = "0.0131";
10              
11             with 'Catmandu::Fix::Base';
12              
13             has validate => (
14             is => 'ro',
15             required => 0,
16             lazy => 1,
17             default => sub { 1; }
18             );
19             has source => (
20             is => 'ro' ,
21             required => 1
22             );
23             has locale => (
24             is => 'ro',
25             required => 1,
26             isa => sub {
27             check_string($_[0]);
28             },
29             default => sub {
30             "en_US"
31             }
32             );
33             has _locale => (
34             is => 'ro',
35             required => 0,
36             lazy => 1,
37             builder => '_build_locale'
38             );
39             has set_locale => (
40             is => 'ro',
41             required => 1,
42             isa => sub {
43             check_string($_[0]);
44             },
45             default => sub {
46             "en_US"
47             }
48             );
49             has _set_locale => (
50             is => 'ro',
51             required => 0,
52             lazy => 1,
53             builder => '_build_set_locale'
54             );
55             has default => ( is => 'ro' );
56             has delete => ( is => 'ro' );
57             has time_zone => (
58             is => 'ro',
59             required => 1,
60             isa => sub {
61             check_string($_[0]);
62             },
63             default => sub {
64             "UTC"
65             }
66             );
67             has _time_zone => (
68             is => 'ro',
69             required => 0,
70             lazy => 1,
71             builder => '_build_time_zone'
72             );
73             has set_time_zone => (
74             is => 'ro',
75             required => 1,
76             isa => sub {
77             check_string($_[0]);
78             },
79             default => sub {
80             "UTC"
81             }
82             );
83             has _set_time_zone => (
84             is => 'ro',
85             required => 0,
86             lazy => 1,
87             builder => '_build_set_time_zone'
88             );
89              
90             has source_pattern => (
91             is => 'ro',
92             required => 1,
93             isa => sub {
94             check_string($_[0]);
95             },
96             default => sub {
97             "%s"
98             }
99             );
100             has destination_pattern => (
101             is => 'ro',
102             required => 1,
103             isa => sub {
104             check_string($_[0]);
105             },
106             default => sub {
107             "%FT%T.%NZ"
108             }
109             );
110             has _datetime_parser => (
111             is => 'ro',
112             lazy => 1,
113             default => sub {
114             my $self = $_[0];
115             DateTime::Format::Strptime->new(
116             pattern => $self->source_pattern,
117             locale => $self->_locale,
118             time_zone => $self->_time_zone,
119             on_error => 'undef'
120             );
121             }
122             );
123             sub _get_locale {
124 8     8   12 state $l = {};
125 8         8 my $name = $_[0];
126 8   66     49 $l->{$name} ||= DateTime::Locale->load($name);
127             }
128             sub _get_time_zone {
129 8     8   27 state $t = {};
130 8         9 my $name = $_[0];
131 8         28 $t->{$name} = DateTime::TimeZone->new( name => $name );
132             }
133             sub _build_locale {
134 4     4   357 _get_locale($_[0]->locale);
135             }
136             sub _build_set_locale {
137 4     4   321 _get_locale($_[0]->set_locale);
138             }
139             sub _build_time_zone {
140 4     4   313 _get_time_zone( $_[0]->time_zone );
141             }
142             sub _build_set_time_zone {
143 4     4   312 _get_time_zone( $_[0]->set_time_zone );
144             }
145             around BUILDARGS => sub {
146             my($orig,$class,$source,%args) = @_;
147              
148             $orig->($class,source => $source,%args);
149             };
150              
151             sub emit {
152 4     4 0 2101 my($self,$fixer) = @_;
153              
154 4         5 my $perl = "";
155              
156 4         14 my $source = $fixer->split_path($self->source());
157 4         61 my $key = pop @$source;
158              
159 4         43 my $time_zone = $fixer->capture($self->_time_zone());
160 4         10082 my $locale = $fixer->capture($self->_locale());
161 4         332 my $set_time_zone = $fixer->capture($self->_set_time_zone());
162 4         482 my $set_locale = $fixer->capture($self->_set_locale());
163              
164 4         216 my $parser = $fixer->capture($self->_datetime_parser());
165              
166             #cf. http://www.nntp.perl.org/group/perl.datetime/2012/05/msg7838.html
167 4 50       3301 $perl .= "local \$Params::Validate::NO_VALIDATION = ".($self->validate() ? 0 : 1).";";
168              
169             $perl .= $fixer->emit_walk_path($fixer->var,$source,sub{
170              
171 4     4   56 my $pvar = shift;
172              
173             $fixer->emit_get_key($pvar,$key, sub {
174              
175 4         76 my $var = shift;
176 4         10 my $d = $fixer->generate_var();
177              
178 4         160 my $p = $fixer->emit_declare_vars($d);
179              
180             #no parsing needed (fast)
181 4 50       33 if($self->source_pattern() =~ /\s*%s\s*/o){
182 0         0 $p .= "if( is_string(${var}) ) {";
183 0         0 $p .= " ${var} =~ s\/^\\s+|\\s+\$\/\/go;";
184 0         0 $p .= " $d = DateTime->from_epoch(epoch => ${var},time_zone => ${time_zone},locale => ${locale});";
185 0         0 $p .= "}"
186             }
187             #parsing needed (slow)
188             else{
189 4         17 $p .= " $d = ".${parser}."->parse_datetime($var) if is_string(${var});";
190             }
191 4         6 $p .= " if($d){";
192 4         14 $p .= " $d->set_time_zone(${set_time_zone}) if ".${d}."->time_zone->name() ne ".${set_time_zone}."->name();";
193 4         7 $p .= " $d->set_locale($set_locale);";
194 4         14 $p .= " ${var} = DateTime::Format::Strptime::strftime('".$self->destination_pattern()."',$d);";
195 4         4 $p .= " }";
196 4 100       37 if($self->delete){
    100          
197 1         6 $p .= " else { ".$fixer->emit_delete_key($pvar,$key)." }";
198             }elsif(defined($self->default)){
199 1         4 $p .= " else { ${var} = ".$fixer->emit_string($self->default)."; }";
200             }
201              
202 4         38 $p;
203              
204 4         27 });
205              
206 4         53 });
207              
208 4         59 $perl;
209             }
210              
211             1;
212             __END__
213              
214             =head1 NAME
215              
216             Catmandu::Fix::datetime_format - Catmandu Fix for converting between datetime formats
217              
218             =head1 SYNOPSIS
219              
220             datetime_format( 'timestamp',
221             'source_pattern' => '%s',
222             'destination_pattern' => '%Y-%m-%d',
223             'time_zone' => 'UTC',
224             'set_time_zone' => 'Europe/Brussels',
225             'delete' => 1,
226             validate => 0,
227             locale => 'en_US',
228             set_locale => 'nl_NL'
229             )
230              
231             =head1 OPTIONS
232              
233             =over 4
234              
235             =item source_pattern
236              
237             Pattern of the source date string to parse. See L<DateTime::Format::Strptime>
238             for documentation of the format. The default is C<%s> (Unix timestamp).
239              
240             =item destination_pattern
241              
242             Pattern of the destination date string. This is the way your datetime needs to
243             be formatted. The default is C<%FT%T.%NZ> (UTC timestamp).
244              
245             =item time_zone
246              
247             Time zone of the source date string. In case the source date string does not
248             contain any time zone information, the parser will use this time_zone to
249             interpret the date. When not set correctly, the resulting date string will be
250             wrong. The default value is C<UTC>. For a complete list of time zone codes see
251             L<http://en.wikipedia.org/wiki/List_of_tz_database_time_zones>.
252              
253             Most parsers assume 'local', but this can lead to different results on
254             different systems. 'local' simply means the same time zone as the one
255             configured on your system.
256              
257             =item set_time_zone
258              
259             Reset the time zone for the destination string. This is usefull for converting
260             dates between time zones, e.g. C<Europe/Brussels>. The default value is C<UTC>.
261              
262             =item locale
263              
264             Language code for the source date string. This is only important when your date
265             string contains names of week days or months. For a complete list of locale
266             codes see L<DateTime::Locale::Catalog>. The default value is C<en_US>.
267              
268             =item set_locale
269              
270             Language code for the destination date string. This is only important when your
271             destination date string contains codes for names of week days or months (C<%a>,
272             C<%A>, C<%b>, C<%B>, and C<%h>). This is usefull for converting dates between
273             languages. For a complete list of locale codes see
274             L<DateTime::Locale::Catalog>. The default value is C<en_US>.
275              
276             =item delete
277              
278             Delete the key when the source date string cannot be parsed. When used, the
279             option C<default> is ignored. Disabled (C<0>) by default.
280              
281             =item default
282              
283             Set the value of the destination string to this value, when parsing fails. By
284             default both the options C<delete> and C<default> are not set, which means that
285             the destination date string will not be created. Not set (C<undef>) by default.
286              
287             =item validate
288              
289             Validate source date string when parsing. Disabled (C<0>) by default to
290             increase speed.
291              
292             =back
293              
294             =head1 AUTHOR
295              
296             Nicolas Franck, C<< <nicolas.franck at ugent.be> >>
297              
298             =head1 SEE ALSO
299              
300             L<Catmandu::Fix>
301              
302             =cut