File Coverage

blib/lib/MooseX/Timestamp.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1              
2             package MooseX::Timestamp;
3              
4             our $VERSION = '0.07';
5              
6             =head1 NAME
7              
8             MooseX::Timestamp - simple timestamp type for Moose
9              
10             =head1 SYNOPSIS
11              
12             use MooseX::Timestamp;
13              
14             print timestamp; # 2007-12-06 23:15:42
15             print timestamp 0; # 1970-01-01 12:00:00
16             print timestamp 0.0001; # 1970-01-01 12:00:00.0001
17             print timestamp gmtime 0; # 1970-01-01 00:00:00
18              
19             use POSIX qw(strftime);
20             print strftime("%a", posixtime "2007-12-06 23:15"); # Thu
21              
22             #...
23              
24             package MyClass;
25             use Moose;
26             has 'stamp' =>
27             isa => "Timestamp",
28             is => "rw",
29             coerce => 1;
30              
31             package main;
32             my $obj = MyClass->new(stamp => "2007-01-02 12:00:12"); # ok
33             $obj->stamp("2007-01-02 12:01");
34             $obj->stamp("2007-01-02 12");
35             $obj->stamp("2007-01-02 12:00:00Gibbons"); #fail
36              
37             =head1 DESCRIPTION
38              
39             This module provides a timestamp type as a Str subtype for Moose.
40             This is a much more lightweight format than, say, L<DateTime>, with
41             the disadvantage that it does not support native operations on the
42             dates.
43              
44             This module provides floating dates on the Gregorian calendar without
45             much code. It operates in (one or two particular variants of)
46             ISO-8601 date format, and POSIX-style 6-number lists.
47              
48             Note: you probably want the functions provided by MooseX::TimestampTZ
49             most of the time, as they deal in unix epoch times.
50              
51             =cut
52              
53 4     4   49111 use Moose::Util::TypeConstraints;
  0            
  0            
54             my @exports;
55             use Sub::Exporter -setup =>
56             { exports => [ qw(timestamp posixtime valid_posixtime) ],
57             groups => { default => [qw(timestamp posixtime)] },
58             };
59             use Carp;
60              
61             #use MooseX::Timestamp::__version;
62              
63             subtype Timestamp
64             => as Str
65             => where {
66             m{^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$} and
67             eval { valid_posixtime(posixtime($_)) };
68             };
69              
70             use POSIX qw(strftime);
71             sub timestamp {
72             if ( @_ == 0 ) {
73             @_ = time;
74             }
75             if ( @_ == 1 ) {
76             my $time = shift;
77             my $frac = $time - int($time);
78             @_ = localtime(int($time));
79             $_[0] += $frac;
80             }
81             valid_posixtime(@_);
82             if ( int($_[0]) == $_[0] ) {
83             strftime("%Y-%m-%d %H:%M:%S", @_ ),
84             }
85             else {
86             # microseconds only. Any more and you start seeing FP
87             # precision weirdness a lot more than you'd expect.
88             my $sec = sprintf("%.6f", $_[0]);
89             $sec =~ s{0+$}{};
90             join(
91             "",
92             strftime("%Y-%m-%d %H:%M:", @_ ),
93             ($_[0]<10)?("0"):(),
94             $sec,
95             );
96             }
97             }
98              
99             my @short = qw(0 1 0 1 0 1 0 0 1 0 1 0);
100             sub valid_posixtime {
101             my @lt = @_;
102             croak "invalid month ".($lt[4]+1) if $lt[4]<0 or $lt[4]>11;
103             croak "invalid day $lt[3]" if !$lt[3] or $lt[3]>31 or
104             (($lt[3]==31 and $short[$lt[4]]) or
105             ($lt[3] > 28 and $lt[4] == 1 and
106             !($lt[3] == 29 and
107             (($lt[5]%4) == 0 and
108             ($lt[5]%100 != 0 or ($lt[5]+300)%400 == 0)))));
109             croak "invalid hour $lt[2]" if $lt[2]<0 or $lt[2]>23;
110             croak "invalid minute $lt[1]" if $lt[1]<0 or $lt[1]>59;
111             croak "invalid second $lt[0]"
112             if ($lt[0]<0 or $lt[0]>=61 or ($lt[0]>=60 and $lt[1]!=59));
113             1;
114             }
115              
116             sub posixtime {
117             return localtime time unless @_;
118             my @lt = ($_[0] =~ m{^(\d{4})(-\d{1,2}|\d{2})(-\d{1,2}|\d{2})T?
119             \s*(?:(\d{1,2})
120             (?::(\d{2})
121             (?::(\d{2}(?:\.\d+)?))?
122             )?
123             )?$}x)
124             or croak "bad timestamp '$_[0]'";
125             $lt[1]=abs($lt[1]);
126             $lt[2]=abs($lt[2]);
127             $lt[0]-=1900;
128             $lt[1]--;
129             $_ ||= 0 for (@lt[3..5]);
130             reverse(@lt);
131             }
132              
133             coerce Timestamp
134             => from Timestamp
135             => via { $_ },
136             => from Str
137             => via { timestamp posixtime $_ };
138              
139             =head1 FUNCTIONS
140              
141             The following functions are available for import. If you want to
142             import them all, use the C<:all> import group, as below:
143              
144             use MooseX::Timestamp qw(:all);
145              
146             =head2 timestamp(time_t $time = time())
147              
148             =head2 timestamp(@posixtime)
149              
150             Converts from a POSIX-style array of time components, or an epoch
151             time, into a Timestamp. If an epoch time is passed, the local
152             timezone rules are used for conversion into a wallclock time. See
153             L<TimestampTZ/timestamptz> for a version which returns the time zone
154             as well.
155              
156             =head2 posixtime()
157              
158             Alias for the built-in C<localtime>; this will not return a hi-res
159             time unless one is passed in.
160              
161             =head2 posixtime(Timestamp)
162              
163             Converts a Timestamp into a POSIX-style array of time components.
164             They are B<NOT> guaranteed to be valid.
165              
166             This accepts a similar set of input values to C<TimestampTZ::epoch>;
167             see its documentation (L<TimestampTZ/epoch>) for a list. The defining
168             difference is that Timestamps passed into this function MUST NOT have
169             a time zone (or "Z") attached.
170              
171             =head2 valid_posixtime(@posixtime)
172              
173             This function croaks with an error if the passed POSIX-style array of
174             time components are found to be out of range in any way. This
175             function contains leap year rules and passes through leap seconds.
176              
177             =head1 TYPES AND COERCIONS
178              
179             One type is defined by this module.
180              
181             =head2 Timestamp
182              
183             This is a subtype of C<Str> which conforms to the normalized form of a
184             Timestamp.
185              
186             Rules exist to coerce C<Str> objects to this type, and are available
187             by using the C<coerce =E<gt> 1> flag on a Moose attribute declaration:
188              
189             package Widget;
190             use MooseX::Timestamp;
191             has 'created' => (
192             isa => Timestamp,
193             is => "rw",
194             coerce => 1,
195             );
196              
197             package main;
198             my $widget = new Widget;
199             $widget->created("2007-12-07");
200             print $widget->created; # 2007-12-07 00:00:00
201              
202             With the above, if you set C<created> to a value such as automatically
203             get converted into a Timestamp in the current time zone.
204              
205             Timestamps may contain fractional components, but the results of
206             conversions from floating point are truncated at the microsecond
207             level.
208              
209             =head2 EXPORTS
210              
211             The default exporting action of this module is to export the
212             C<posixtime> and C<timestamp> methods. To avoid this, pass an empty
213             argument list to the use statement:
214              
215             use MooseX::Timestamp ();
216              
217             =head1 BUGS
218              
219             This module is relatively slow, as conversions and calls to C<timegm>
220             and friends happen far too often, really - especially with coercion.
221              
222             =head1 AUTHOR AND LICENSE
223              
224             Sam Vilain, <samv@cpan.org>
225              
226             Copyright 2007, Sam Vilain. All Rights Reserved. This program is
227             Free Software; you may use it and/or redistribute it under the terms
228             of Perl itself.
229              
230             =cut
231              
232             1;