File Coverage

blib/lib/PGObject/Type/DateTime.pm
Criterion Covered Total %
statement 104 107 97.2
branch 49 54 90.7
condition 31 36 86.1
subroutine 21 21 100.0
pod 12 12 100.0
total 217 230 94.3


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