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 26     26   69920 use strict;
  26         54  
  26         717  
4 26     26   129 use warnings;
  26         118  
  26         612  
5              
6 26     26   124 use Carp;
  26         38  
  26         1344  
7 26     26   11347 use Time::Local;
  26         57753  
  26         2519  
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 26     26   1287 use overload '""' => \&as_string, '0+' => \&as_epoch;
  26         1014  
  26         186  
31 26     26   1822 use overload fallback => 1;
  26         57  
  26         80  
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 2     2 1 599 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 5     5 1 1015 my $class = shift;
68 5         10 my $timestamp = shift;
69              
70 5 100       396 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 3         13 my $ob = 0 + "$1";
75 3         19 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 269     269 1 4763 my $class = shift;
87 269         337 my $timestamp = shift;
88              
89 269 100       1238 croak "input contains invalid character: $timestamp"
90             unless $timestamp =~ m/^[-T:Z0123456789]+$/;
91 267 100       1694 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 262 100 100     3129 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 257         1928 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 4192 my $self = shift;
108              
109 181 100       456 if ($$self =~ m/T/) {
110             # convert string to epoch time
111 176         576 $$self =~ m/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/;
112 176         566 return timegm($6, $5, $4, $3, $2 - 1, $1); # adjust month: 1..12 -> 0..11
113             } else {
114 5         41 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 8     8 1 1161 my $self = shift;
128              
129 8 100       49 if ($$self =~ m/T/) {
130 3         32 return $$self;
131             } else {
132             # convert epoch time to string
133 5         35 my ($sec, $min, $hour, $mdy, $mon, $year_o, $wdy, $ydy) = gmtime $$self;
134 5         14 my $year = $year_o + 1900; my $month = $mon + 1;
  5         9  
135 5         274 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__