File Coverage

blib/lib/DateTimeX/Auto.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             {
2             package DateTimeX::Auto;
3              
4 2     2   66409 use 5.008;
  2         7  
  2         76  
5 2     2   12 use strict;
  2         4  
  2         74  
6 2     2   11 use base qw[DateTime Exporter];
  2         7  
  2         122816  
7 2     2   508406 use overload '""' => \&_dtxa_stringify;
  2         5  
  2         20  
8 2     2   3741 use Object::AUTHORITY;
  0            
  0            
9             use UNIVERSAL::ref;
10             use constant ref => 'DateTime';
11              
12             use Carp qw[];
13             use DateTime::Format::Strptime qw[];
14              
15             our %_const_handlers = (
16             q => sub
17             {
18             return $_[1] unless $_[2] eq 'q';
19             return (
20             __PACKAGE__->new($_[0])
21             || DateTimeX::Auto::Duration->new($_[0])
22             || $_[1]
23             );
24             },
25             );
26             our @EXPORT_OK = qw[d dt dur];
27              
28             BEGIN {
29             $DateTimeX::Auto::AUTHORITY = 'cpan:TOBYINK';
30             $DateTimeX::Auto::VERSION = '0.007';
31             }
32              
33             sub import
34             {
35             my $class = shift;
36             my $imports = join ' ', @_;
37            
38             if ($imports =~ /(?:\b|^)\:auto(?:\b|$)/)
39             {
40             overload::constant %_const_handlers;
41             }
42            
43             while ($imports =~ /(?:\b|^)(d|dt|dur)(?:\b|^)/g)
44             {
45             $class->export_to_level(1, undef, $1);
46             }
47             }
48              
49             sub unimport
50             {
51             overload::remove_constant(q => undef);
52             }
53              
54             sub d
55             {
56             my ($string) = @_;
57            
58             return DateTime->now unless @_;
59            
60             my $dt = __PACKAGE__->new("$string");
61             return $dt if $dt;
62            
63             Carp::croak("Could not turn '$string' into a DateTime.");
64             }
65              
66             *dt = \&d;
67              
68             sub dur
69             {
70             my ($string) = @_;
71            
72             my $dur = DateTimeX::Auto::Duration->new("$string");
73             return $dur if $dur;
74            
75             Carp::croak("Could not turn '$string' into a DateTime::Duration.");
76             }
77              
78             sub from_object
79             {
80             my ($proto, %args) = @_;
81            
82             my %x;
83             my $rv = $proto->SUPER::from_object(%args);
84             $rv->{+__PACKAGE__} = { %x } if %x = %{ $args{object}->{+__PACKAGE__} };
85            
86             return $rv;
87             }
88              
89             sub new
90             {
91             if (scalar @_ > 2)
92             {
93             my $class = shift;
94             return $class->SUPER::new(@_);
95             }
96            
97             my ($class, $string) = @_;
98            
99             if ($string =~ /^(\d{4})-(0[1-9]|1[0-2])-([0-2][0-9]|30|31)(Z?)$/)
100             {
101             my $dt;
102             my $z = defined($4) ? $4 : '';
103             eval {
104             $dt = $class->SUPER::new( year => $1, month=>$2, day=>$3, hour=>0, minute=>0, second=>0 );
105             $dt->{+__PACKAGE__}{format} = 'D';
106             if ($z eq 'Z' and defined $dt)
107             {
108             $dt->set_time_zone('UTC');
109             $dt->{+__PACKAGE__}{trailer} = $z;
110             }
111             };
112             return $dt if $dt;
113             }
114            
115             if ($string =~ /^(\d{4})-(0[1-9]|1[0-2])-([0-2][0-9]|30|31)T([0-1][0-9]|2[0-4]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z?)$/)
116             {
117             my $dt;
118             my $z = defined($8) ? $8 : '';
119             my $nano = defined($7) ? $7 : '';
120             eval {
121             $dt = $class->SUPER::new( year => $1, month=>$2, day=>$3, hour=>$4, minute=>$5, second=>$6 );
122             $dt->{+__PACKAGE__}{format} = 'DT';
123             if (length $nano and defined $dt)
124             {
125             $dt->{+__PACKAGE__}{format} = length($nano) - 1;
126             $dt->{rd_nanosecs} = substr($nano.('0' x 9), 1, 9) + 0;
127             }
128             if ($z eq 'Z' and defined $dt)
129             {
130             $dt->set_time_zone('UTC');
131             $dt->{+__PACKAGE__}{trailer} = $z;
132             }
133             };
134             return $dt if $dt;
135             }
136            
137             return undef;
138             }
139              
140             sub set_time_zone
141             {
142             my ($self, @args) = @_;
143             delete $self->{+__PACKAGE__}{trailer};
144             $self->SUPER::set_time_zone(@args);
145             }
146              
147             sub _dtxa_stringify
148             {
149             my ($self) = @_;
150            
151             unless (exists $self->{+__PACKAGE__})
152             {
153             return $self->SUPER::_stringify;
154             }
155            
156             my $trailer = $self->{+__PACKAGE__}{trailer};
157             $trailer = '' unless defined $trailer;
158            
159             if ($self->{+__PACKAGE__}{format} eq 'D')
160             {
161             return $self->ymd('-').$trailer;
162             }
163              
164             elsif ($self->{+__PACKAGE__}{format} eq 'DT')
165             {
166             return sprintf('%sT%s%s', $self->ymd('-'), $self->hms(':'), $trailer);
167             }
168              
169             else
170             {
171             my $nano = substr(
172             $self->strftime('%N') . ('0' x $self->{+__PACKAGE__}{format}),
173             0,
174             $self->{+__PACKAGE__}{format},
175             );
176             return sprintf('%sT%s.%s%s', $self->ymd('-'), $self->hms(':'), $nano, $trailer);
177             }
178             }
179             }
180              
181             {
182             package DateTimeX::Auto::Duration;
183              
184             use 5.008;
185             use strict;
186             use base qw[DateTime::Duration];
187             use overload '""' => \&_dtxda_stringify;
188             use Object::AUTHORITY;
189             use UNIVERSAL::ref;
190             use constant ref => 'DateTime::Duration';
191              
192             use Carp qw[];
193              
194             BEGIN {
195             $DateTimeX::Auto::Duration::AUTHORITY = 'cpan:TOBYINK';
196             $DateTimeX::Auto::Duration::VERSION = '0.007';
197             }
198              
199             sub new
200             {
201             if (scalar @_ > 2)
202             {
203             my $class = shift;
204             return $class->SUPER::new(@_);
205             }
206              
207             my ($class, $string) = @_;
208              
209             return undef unless $string =~ /^
210             ([\+\-])? # Potentially negitive...
211             P # Period of...
212             (?:([\d\.]*)Y)? # n Years
213             (?:([\d\.]*)M)? # n Months
214             (?:([\d\.]*)W)? # n Weeks
215             (?:([\d\.]*)D)? # n Days
216             (?:
217             T # And a time of...
218             (?:([\d\.]*)H)? # n Hours
219             (?:([\d\.]*)M)? # n Minutes
220             (?:([\d\.]*)S)? # n Seconds
221             )?
222             /ix;
223            
224             my $X = {
225             I => $1,
226             y => $2,
227             m => $3,
228             w => $4,
229             d => $5,
230             h => $6,
231             min => $7,
232             s => $8,
233             n => 0,
234             };
235              
236             # Handle fractional
237             foreach my $frac (qw(y=12.m m=30.d w=7.d d=24.h h=60.min min=60.s s=1000000000.n))
238             {
239             my ($big, $mult, $small) = split /[\=\.]/, $frac;
240             next unless $X->{$big} =~ /\./;
241              
242             my $int_part = int($X->{$big});
243             my $frac_part = $X->{$big} - $int_part;
244              
245             $X->{$big} = $int_part;
246             $X->{$small} += ($mult * $frac_part);
247             }
248             $X->{'n'} = int($X->{'n'});
249              
250             # Construct and return object.
251             my $dur = $class->SUPER::new(
252             years => $X->{'y'} || 0,
253             months => $X->{'m'} || 0,
254             weeks => $X->{'w'} || 0,
255             days => $X->{'d'} || 0,
256             hours => $X->{'h'} || 0,
257             minutes => $X->{'min'} || 0,
258             seconds => $X->{'s'} || 0,
259             nanoseconds => $X->{'n'} || 0,
260             );
261            
262             $X->{'I'} eq '-'
263             ? $dur->inverse
264             : $dur;
265             }
266              
267             sub _dtxda_stringify
268             {
269             my $self = shift;
270             my $str;
271              
272             # We coerce weeks into days and nanoseconds into fractions of a second
273             # for compatibility with xsd:duration.
274              
275             if ($self->is_negative)
276             { $str .= '-P'; }
277             else
278             { $str .= 'P'; }
279              
280             if ($self->years)
281             { $str .= $self->years.'Y'; }
282              
283             if ($self->months)
284             { $str .= $self->months.'M'; }
285              
286             if ($self->weeks || $self->days)
287             { $str .= ($self->days + (7 * $self->weeks)).'D'; }
288              
289             $str .= 'T';
290              
291             if ($self->hours)
292             { $str .= $self->hours.'H'; }
293              
294             if ($self->minutes)
295             { $str .= $self->minutes.'M'; }
296              
297             if ($self->seconds || $self->nanoseconds)
298             { $str .= ($self->seconds + ($self->nanoseconds / 1000000000)).'S'; }
299              
300             $str =~ s/T$//;
301              
302             return $str;
303             }
304             }
305              
306             __FILE__
307             __END__