File Coverage

blib/lib/Renard/API/MuPDF/mutool/DateObject.pm
Criterion Covered Total %
statement 58 59 98.3
branch 10 14 71.4
condition 9 14 64.2
subroutine 9 9 100.0
pod 1 1 100.0
total 87 97 89.6


line stmt bran cond sub pod time code
1 3     3   287573 use Renard::Incunabula::Common::Setup;
  3         14  
  3         24  
2             package Renard::API::MuPDF::mutool::DateObject;
3             # ABSTRACT: An object to store the date
4             $Renard::API::MuPDF::mutool::DateObject::VERSION = '0.006';
5 3     3   22597 use Moo;
  3         2913  
  3         21  
6 3     3   2653 use Renard::Incunabula::Common::Types qw(Str HashRef InstanceOf);
  3         156379  
  3         37  
7              
8 3     3   8251 use overload '""' => \&stringify, eq => \&_string_eq;
  3         7  
  3         34  
9              
10             has string => (
11             is => 'ro',
12             isa => Str,
13             required => 1,
14             );
15              
16             has data => (
17             is => 'lazy',
18             isa => HashRef,
19             );
20              
21 5     5   5037 method _build_data() {
  5         8  
22 5         21 my $date_string = $self->string;
23              
24             # § 3.8.3 Dates (pg. 160)
25             # (D:YYYYMMDDHHmmSSOHH'mm')
26             # where
27 5         24 my $date_re = qr/
28             (?<Prefix>D:)?
29             (?<Year> \d{4} ) # YYYY is the year
30             (?<Month> \d{2} )? # MM is the month
31             (?<Day> \d{2} )? # DD is the day (01–31)
32             (?<Hour> \d{2} )? # HH is the hour (00–23)
33             (?<Minute> \d{2} )? # mm is the minute (00–59)
34             (?<Second> \d{2} )? # SS is the second (00–59)
35             (?<TzOffset> [-+Z] )? # O is the relationship of local time
36             # to Universal Time (UT), denoted by
37             # one of the characters +, −,
38             # or Z (see below)
39             (?<TzHourW>
40             (?<TzHour> \d{2})
41             '
42             )? # HH followed by ' is the absolute
43             # value of the offset from UT in hours
44             # (00–23)
45             (?<TzMinuteW>
46             (?<TzMinute> \d{2})
47             '
48             )? # mm followed by ' is the absolute
49             # value of the offset from UT in
50             # minutes (00–59)
51             /x;
52              
53 5         11 my $time = {};
54              
55 5 50       57 die "Not a date string" unless $date_string =~ $date_re;
56              
57 3     3   2258 $time->{year} = $+{Year};
  3         1217  
  3         454  
  5         42  
58 5   50     30 $time->{month} = $+{Month} // '01';
59 5   50     21 $time->{day} = $+{Day} // '01';
60              
61 5   50     32 $time->{hour} = $+{Hour} // '00';
62 5   50     20 $time->{minute} = $+{Minute} // '00';
63 5   50     26 $time->{second} = $+{Second} // '00';
64              
65 5 100       27 if( exists $+{TzOffset} ) {
66 4         16 $time->{tz}{offset} = $+{TzOffset};
67 4   100     20 $time->{tz}{hour} = $+{TzHour} // '00';
68 4   100     18 $time->{tz}{minute} = $+{TzMinute} // '00';
69             }
70              
71 5         86 $time;
72             }
73              
74 4         451939 method as_DateTime() :ReturnType(InstanceOf['DateTime']) {
  4         6  
75 4 50       7 eval { require DateTime; 1 } or die "require DateTime";
  4         21  
  4         11  
76              
77 4         59 my $dt_hash = $self->data;
78 4         80 my $dt_timezone = 'floating';
79 4 100       12 if( exists $dt_hash->{tz} ) {
80 3 100       9 if( $dt_hash->{tz}{offset} eq 'Z' ) {
81 1         3 $dt_timezone = 'UTC'; # UTC
82             } else {
83             $dt_timezone = join '', (
84             $dt_hash->{tz}{offset}, # ±
85             $dt_hash->{tz}{hour},
86             $dt_hash->{tz}{minute},
87 2         6 );
88             }
89             }
90              
91             return DateTime->new(
92             year => $dt_hash->{year},
93             month => $dt_hash->{month},
94             day => $dt_hash->{day},
95              
96             hour => $dt_hash->{hour},
97             minute => $dt_hash->{minute},
98             second => $dt_hash->{second},
99              
100 4         21 time_zone => $dt_timezone,
101             );
102 3     3   862 }
  3         6  
  3         24  
103              
104 1     1 1 2 method stringify() {
  1         2  
105             my $dt_part = sprintf(
106             "%4d-%02d-%02dT%02d:%02d:%02d",
107             $self->data->{year},
108             $self->data->{month},
109             $self->data->{day},
110              
111             $self->data->{hour},
112             $self->data->{minute},
113             $self->data->{second},
114 1         16 );
115              
116 1         31 my $tz_part = '';
117 1 50       14 if( exists $self->data->{tz} ) {
118 1 50       32 if( $self->data->{tz}{offset} eq 'Z' ) {
119 0         0 $tz_part = 'Z'; # UTC
120             } else {
121             $tz_part = $self->data->{tz}{offset} # ±
122             . $self->data->{tz}{hour}
123             . ":"
124 1         22 . $self->data->{tz}{minute};
125             }
126             }
127              
128              
129 1         28 $dt_part . $tz_part;
130             }
131              
132 1     1   1281 fun _string_eq($a, $b, $swap) {
  1         2  
133 1         3 "$a" eq "$b";
134             }
135              
136             1;
137              
138             __END__
139              
140             =pod
141              
142             =encoding UTF-8
143              
144             =head1 NAME
145              
146             Renard::API::MuPDF::mutool::DateObject - An object to store the date
147              
148             =head1 VERSION
149              
150             version 0.006
151              
152             =head1 EXTENDS
153              
154             =over 4
155              
156             =item * L<Moo::Object>
157              
158             =back
159              
160             =head1 ATTRIBUTES
161              
162             =head2 string
163              
164             A PDF date string in C<string> which are in the form:
165              
166             D:YYYYMMDDHHmmSSOHH'mm'
167              
168             =head2 data
169              
170             A C<HashRef> in the form
171              
172             Dict[
173             year => Str, # YYYY
174             month => Str, # MM: 01-12
175             day => Str, # DD: 01-31
176              
177             hour => Str, # HH: 00-23
178             minute => Str, # mm: 00-59
179             second => Str, # SS: 00-59
180              
181             tz => Dict[
182             offset => Str, # O: /[-+Z]/
183             hour => Str, # HH': 00-59
184             minute => Str, # mm': 00-59
185             ],
186             ]
187              
188             =head1 METHODS
189              
190             =head2 as_DateTime
191              
192             method as_DateTime() :ReturnType(InstanceOf['DateTime'])
193              
194             Returns a L<DateTime> representation of the date.
195              
196             =head2 stringify
197              
198             method stringify()
199              
200             Returns a C<Str> representation of the date.
201              
202             This follows the ISO 8601 format of
203              
204             YYYY-MM-DDThh:mm:ss±hh:mm
205              
206             which includes the timezone (either as an offset C<±hh:mm> or as C<Z> for UTC)
207             and using a C<T> separator for the date and time.
208              
209             =head1 AUTHOR
210              
211             Project Renard
212              
213             =head1 COPYRIGHT AND LICENSE
214              
215             This software is copyright (c) 2017 by Project Renard.
216              
217             This is free software; you can redistribute it and/or modify it under
218             the same terms as the Perl 5 programming language system itself.
219              
220             =cut