File Coverage

blib/lib/WARC/Date.pm
Criterion Covered Total %
statement 42 42 100.0
branch 12 12 100.0
condition 12 12 100.0
subroutine 11 11 100.0
pod 5 5 100.0
total 82 82 100.0


line stmt bran cond sub pod time code
1             package WARC::Date; # -*- CPerl -*-
2              
3 28     28   71523 use strict;
  28         124  
  28         757  
4 28     28   118 use warnings;
  28         43  
  28         608  
5              
6 28     28   120 use Carp;
  28         45  
  28         1448  
7 28     28   12573 use Time::Local;
  28         56756  
  28         2631  
8              
9             our @ISA = qw();
10              
11             require WARC; *WARC::Date::VERSION = \$WARC::VERSION;
12              
13             =head1 NAME
14              
15             WARC::Date - datestamp objects for WARC library
16              
17             =head1 SYNOPSIS
18              
19             use WARC::Date;
20              
21             $datestamp = WARC::Date->now(); # construct from current time
22             $datestamp = WARC::Date->from_epoch(time); # likewise
23             $datestamp = WARC::Date->from_string($string);# construct from string
24              
25             $time = $datestamp->as_epoch; # as seconds since epoch
26             $text = $datestamp->as_string; # as "YYYY-MM-DDThh:mm:ssZ"
27              
28             =cut
29              
30 28     28   1403 use overload '""' => \&as_string, '0+' => \&as_epoch;
  28         1084  
  28         196  
31 28     28   1872 use overload fallback => 1;
  28         52  
  28         102  
32              
33             # This implementation needs to store only a single value, either an epoch
34             # timestamp or a [W3C-NOTE-datetime] string. The underlying
35             # implementation is threrefore a blessed scalar, with the formats
36             # distinguished by the presence or absence of a capital letter "T".
37              
38             =head1 DESCRIPTION
39              
40             C objects encapsulate the details of the required format for
41             timestamps in WARC headers.
42              
43             These objects have overloaded string and number conversions. As a string,
44             a C object produces the [W3C-NOTE-datetime] format, while
45             conversion to a number yields an epoch timestamp.
46              
47             =head2 Methods
48              
49             =over
50              
51             =item $datestamp = WARC::Date-Enow
52              
53             Construct a C object representing the current time.
54              
55             =cut
56              
57 4     4 1 2144 sub now { (shift)->from_epoch(time) }
58              
59             =item $datestamp = WARC::Date-Efrom_epoch( $timestamp )
60              
61             Construct a C object representing the time indicated by an
62             epoch timestamp.
63              
64             =cut
65              
66             sub from_epoch {
67 7     7 1 997 my $class = shift;
68 7         15 my $timestamp = shift;
69              
70 7 100       455 croak "alleged epoch timestamp is not a number: $timestamp"
71             unless $timestamp =~ m/^([0123456789]+)$/;
72              
73             # reconstruct value to ensure object is not tainted
74 5         23 my $ob = 0 + "$1";
75 5         46 bless \ $ob, $class;
76             }
77              
78             =item $datestamp = WARC::Date-Efrom_string( $string )
79              
80             Construct a C object representing the time indicated by a
81             string in the same format returned by the C method.
82              
83             =cut
84              
85             sub from_string {
86 298     298 1 4619 my $class = shift;
87 298         345 my $timestamp = shift;
88              
89 298 100       1221 croak "input contains invalid character: $timestamp"
90             unless $timestamp =~ m/^[-T:Z0123456789]+$/;
91 296 100       1778 croak "input not in required format: $timestamp"
92             unless $timestamp =~ m/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/;
93 291 100 100     2982 croak "input not valid as timestamp: $timestamp"
      100        
      100        
      100        
94             unless ($2 <= 12 && $3 < 32 && $4 < 24 && $5 < 60 && $6 <= 60);
95              
96             # reconstruct string to ensure object is not tainted
97 286         1854 bless \ "$1-$2-$3T$4:$5:$6Z", $class;
98             }
99              
100             =item $datestamp-Eas_epoch
101              
102             Return the represented time as an epoch timestamp.
103              
104             =cut
105              
106             sub as_epoch {
107 181     181 1 3952 my $self = shift;
108              
109 181 100       414 if ($$self =~ m/T/) {
110             # convert string to epoch time
111 176         554 $$self =~ m/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/;
112 176         486 return timegm($6, $5, $4, $3, $2 - 1, $1); # adjust month: 1..12 -> 0..11
113             } else {
114 5         43 return $$self;
115             }
116             }
117              
118             =item $datestamp-Eas_string
119              
120             Return a string in the format specified by [W3C-NOTE-datetime] restricted
121             to 14 digits and UTC time zone, which is
122             "I-I-I
BI:I:IB".
123              
124             =cut
125              
126             sub as_string {
127 70     70 1 1239 my $self = shift;
128              
129 70 100       216 if ($$self =~ m/T/) {
130 61         208 return $$self;
131             } else {
132             # convert epoch time to string
133 9         59 my ($sec, $min, $hour, $mdy, $mon, $year_o, $wdy, $ydy) = gmtime $$self;
134 9         23 my $year = $year_o + 1900; my $month = $mon + 1;
  9         19  
135 9         456 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',
136             $year, $month, $mdy, $hour, $min, $sec);
137             }
138             }
139              
140             =back
141              
142             =cut
143              
144             1;
145             __END__