File Coverage

blib/lib/PGObject/Type/DateTime.pm
Criterion Covered Total %
statement 93 96 96.8
branch 45 50 90.0
condition 31 36 86.1
subroutine 17 17 100.0
pod 10 10 100.0
total 196 209 93.7


line stmt bran cond sub pod time code
1             package PGObject::Type::DateTime;
2              
3 6     6   180593 use 5.010;
  6         26  
4 6     6   37 use Carp;
  6         14  
  6         374  
5 6     6   38 use strict;
  6         17  
  6         136  
6 6     6   30 use warnings;
  6         11  
  6         195  
7 6     6   32 use base qw(DateTime);
  6         10  
  6         4554  
8 6     6   2634280 use DateTime::TimeZone;
  6         16  
  6         5385  
9              
10             =head1 NAME
11              
12             PGObject::Type::DateTime - DateTime Wrappers for PGObject
13              
14             =head1 VERSION
15              
16             Version 2
17              
18             =cut
19              
20             our $VERSION = 2.000000;
21             our $default_tz = DateTime::TimeZone->new(name => 'UTC');
22              
23              
24             =head1 SYNOPSIS
25              
26             PGObject::Type::DateTime->register();
27              
28             Now all Datetime, Timestamp, and TimestampTZ types are returned
29             returned as datetime objects. Date and time modules may require subclasses
30             to serialize properly to the database.
31              
32             =head1 ONGOING WORK IN 2.X
33              
34             During the 2.x series we expect to work on better NULL support. Right now this
35             is all delegated to clild classes, but there are likely to be cases where we
36             add this to our library directly.
37              
38             =head1 DESCRIPTION
39              
40             This module provides a basic wrapper around DateTime to allow PGObject-framework
41             types to automatically tie date/time related objects, but we handle date and
42             timestamp formats in our from_db routines.
43              
44             This specific module only supports the ISO YMD datestyle. The MDY or DMY
45             datestyles may be usable in future versions but datestyles other than ISO raise
46             ambiguity issues, sufficient that they cannot always even be used in PostgreSQL as input.
47              
48             This module also provides basic default handling. Times are assigned a date of
49             '0001-01-01' and dates are assigned a time of midnight. Whether this is set is
50             persisted, along with whether timezones are set, and these are returned to a
51             valid ISO YMD format on export, if a date component was initially set.
52              
53             This means you can use this for general math without worrying about many of the
54             other nicities. Parsing ISO YMD dates and standard times (24 hr format) is
55             supported via the from_db interface, which also provides a useful way of handing
56             dates in.
57              
58             =head1 SUBROUTINES/METHODS
59              
60             =head2 register
61              
62             By default registers 'date', 'time', 'timestamp', and 'timestamptz'
63              
64             =cut
65              
66             sub register {
67 5     5 1 1395 my $self = shift @_;
68 5 50       18 croak "Can't pass reference to register \n".
69             "Hint: use the class instead of the object" if ref $self;
70 5         15 my %args = @_;
71 5         14 my $registry = $args{registry};
72 5   100     24 $registry ||= 'default';
73 5         11 my $types = $args{types};
74 5 100 66     31 $types = ['date', 'time', 'timestamp', 'timestamptz']
75             unless defined $types and @$types;
76 5         14 for my $type (@$types){
77 14 50       602 if ($PGObject::VERSION =~ /1\.*/) { # 1.x
78 14         45 my $ret =
79             PGObject->register_type(registry => $registry, pg_type => $type,
80             perl_class => $self);
81             } else { # higher than 1.x
82 0         0 PGObject::Type::Registry->register_type(
83             registry => $registry, dbtype => $type, apptype => $self
84             );
85             }
86             }
87 5         76 return 1;
88             }
89              
90             =head2 _new
91              
92             Constructor for the PGDate object. Fully compliant with DateTime
93             C<_new> constructor which it uses internally to instantiate objects.
94              
95             We need to hook this constructor instead of the regular C<new> one,
96             because this one is referred to directly on numerous occasions.
97              
98             =cut
99              
100             sub _new {
101 30     30   6966 my $class = shift;
102 30         122 my (%args) = @_;
103 30         117 my $self = $class->SUPER::_new(@_);
104 30         5349 bless $self, $class;
105 30 100 66     182 $self->{_pgobject_is_date} = (defined $args{year} && $args{year} > 1) ? 1 : 0;
106 30 50       77 $self->{_pgobject_is_time} = (defined $args{hour}) ? 1 : 0;
107 30 100       71 $self->{_pgobject_is_tz} = (defined $args{time_zone}) ? 1 : 0;
108 30         111 return $self;
109             }
110              
111             =head2 today
112              
113             Wraps C<DateTime::today>, clearing the internal flag which
114             causes C<is_time()> to return a non-false value.
115              
116             =cut
117              
118             sub today {
119 1     1 1 150 my $class = shift;
120 1         16 my $self = $class->SUPER::today(@_);
121 1         3 $self->{_pgobject_is_time} = 0;
122 1         3 return $self;
123             }
124              
125             =head2 last_day_of_month
126              
127             Wraps C<DateTime::last_day_of_month>, clearing the internal flag which
128             causes C<is_time()> to return a non-false value.
129              
130             =cut
131              
132             sub last_day_of_month {
133 1     1 1 252 my $class = shift;
134 1         10 my $self = $class->SUPER::last_day_of_month(@_);
135 1         3 $self->{_pgobject_is_time} = 0;
136 1         4 return $self;
137             }
138              
139             =head2 from_day_of_year
140              
141             Wraps C<DateTime::from_day_of_year>, clearing the internal flag which
142             causes C<is_time()> to return a non-false value.
143              
144             =cut
145              
146             sub from_day_of_year {
147 1     1 1 2 my $class = shift;
148 1         8 my $self = $class->SUPER::from_day_of_year(@_);
149 1         3 $self->{_pgobject_is_time} = 0;
150 1         3 return $self;
151             }
152              
153             =head2 truncate( to => ... )
154              
155             Wraps C<DateTime::from_day_of_year>, clearing the internal flag which
156             causes C<is_time()> to return a non-false value, if the C<to> argument
157             is not one of C<second>, C<minute> or C<hour>.
158              
159             =cut
160              
161             sub truncate {
162 11     11 1 423 my $class = shift;
163 11         28 my %args = @_;
164 11         58 my $self = $class->SUPER::truncate(@_);
165             $self->{_pgobject_is_time} = 0
166 11 100       183 if ! grep { $args{to} eq $_} qw/ hour minute second /;
  33         92  
167 11         36 return $self;
168             }
169              
170             =head2 from_db
171              
172             Parses a date from YYYY-MM-DD format and generates the new object based on it.
173              
174             =cut
175              
176             sub from_db {
177 7     7 1 140 my ($class, $value) = @_;
178 7         17 my ($year, $month, $day, $hour, $min, $sec, $nanosec, $tz);
179 7 100       30 $value = '' if not defined $value;
180 7 100       63 $value =~ /(\d{4})-(\d{2})-(\d{2})/
181             and ($year, $month, $day) = ($1, $2, $3);
182 7 100       67 $value =~ /(\d+):(\d+):([0-9.]+)([+-]\d{1,4})?/
183             and ($hour, $min, $sec, $tz) = ($1, $2, $3, $4);
184 7   66     81 $tz ||= $default_tz; # defaults to UTC
185 7 100       44 $tz .= '00' if $tz =~ /([+-]\d{2}$)/;
186 7 100       39 ($sec, $nanosec) = split /\./, $sec if $sec;
187 7 100       30 $nanosec *= 1000 if $nanosec;
188 7   100     141 my $self = "$class"->new(
      100        
      100        
      100        
      100        
      100        
      100        
      50        
189             year => $year || 1,
190             month => $month || 1,
191             day => $day || 1,
192             hour => $hour || 0,
193             minute => $min || 0,
194             second => $sec || 0,
195             nanosecond => $nanosec || 0,
196             time_zone => $tz || 0,
197             );
198 7 100       22 $self->is_time(0) if ! defined $hour;
199 7 100       31 $self->is_tz(0) if $tz == $default_tz;
200 7         19 return $self;
201             }
202              
203             =head2 to_db
204              
205             Returns the date in YYYY-MM-DD format.
206              
207             =cut
208              
209             sub to_db {
210 8     8 1 6225 my ($self) = @_;
211 8 100 100     35 return undef unless ($self->is_date or $self->is_time);
212 7         20 my $dbst = '';
213 7         33 my $offset = $self->offset;
214 7         83 $offset = $offset / 60;
215 7         27 my $offset_min = $offset%60;
216 7         20 $offset = $offset / 60;
217 7 100       29 my $sign = ($offset > 0)? '+' : '-';
218 7         40 $offset = $sign . sprintf('%02d', abs($offset));
219              
220 7 50       32 if ($offset_min){
221 0         0 $offset = "$offset$offset_min";
222             }
223              
224 7 100       24 $dbst .= $self->ymd if $self->is_date;
225 7 100 100     139 $dbst .= ' ' if $self->is_date and $self->is_time;
226 7 100       21 $dbst .= $self->hms . '.' . $self->microsecond if $self->is_time;
227 7 100 66     129 $dbst .= $offset if $self->time_zone ne $default_tz and $self->is_time;
228 7         81 return $dbst;
229             }
230              
231             =head2 is_date($to_set)
232              
233             If $to_set is set, sets this. In both cases, returns whether the object is now
234             a date.
235              
236             =cut
237              
238             sub is_date {
239 22     22 1 77 my ($self, $val) = @_;
240 22 50       82 if (defined $val){
241 0         0 $self->{_pgobject_is_date} = $val;
242             }
243 22         140 return $self->{_pgobject_is_date};
244             }
245              
246             =head2 is_time($to_set)
247              
248             If $to_set is set, sets this. In both cases, returns whether the object is now
249             a time.
250              
251             =cut
252              
253              
254             sub is_time {
255 30     30 1 6800 my ($self, $val) = @_;
256 30 100       92 if (defined $val){
257 2         5 $self->{_pgobject_is_time} = $val;
258             }
259 30         143 return $self->{_pgobject_is_time};
260             }
261              
262             =head2 is_tz($to_set)
263              
264             If $to_set is set, sets this. In both cases, returns whether the object is now
265             a date.
266              
267             =cut
268              
269             sub is_tz {
270 10     10 1 29 my ($self, $val) = @_;
271 10 100       34 if (defined $val){
272 4         8 $self->{_pgobject_is_tz} = $val;
273             }
274 10         45 return $self->{_pgobject_is_tz};
275             }
276              
277             =head1 AUTHOR
278              
279             Chris Travers, C<< <chris.travers at gmail.com> >>
280              
281             =head1 BUGS
282              
283             Please report any bugs or feature requests to C<bug-pgobject-type-datetime at rt.cpan.org>, or through
284             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=PGObject-Type-DateTime>. I will be notified, and then you'll
285             automatically be notified of progress on your bug as I make changes.
286              
287              
288              
289              
290             =head1 SUPPORT
291              
292             You can find documentation for this module with the perldoc command.
293              
294             perldoc PGObject::Type::DateTime
295              
296              
297             You can also look for information at:
298              
299             =over 4
300              
301             =item * RT: CPAN's request tracker (report bugs here)
302              
303             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=PGObject-Type-DateTime>
304              
305             =item * AnnoCPAN: Annotated CPAN documentation
306              
307             L<http://annocpan.org/dist/PGObject-Type-DateTime>
308              
309             =item * CPAN Ratings
310              
311             L<http://cpanratings.perl.org/d/PGObject-Type-DateTime>
312              
313             =item * Search CPAN
314              
315             L<http://search.cpan.org/dist/PGObject-Type-DateTime/>
316              
317             =back
318              
319              
320             =head1 ACKNOWLEDGEMENTS
321              
322              
323             =head1 LICENSE AND COPYRIGHT
324              
325             Copyright 2013 Chris Travers.
326              
327             This program is released under the following license: BSD
328              
329              
330             =cut
331              
332             1; # End of PGObject::Type::DateTime