File Coverage

blib/lib/DateTime/Format/ISO8601/Format.pm
Criterion Covered Total %
statement 51 53 96.2
branch 25 28 89.2
condition 12 12 100.0
subroutine 8 8 100.0
pod 4 4 100.0
total 100 105 95.2


line stmt bran cond sub pod time code
1             package DateTime::Format::ISO8601::Format;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2020-08-21'; # DATE
5             our $DIST = 'DateTime-Format-ISO8601-Format'; # DIST
6             our $VERSION = '0.005'; # VERSION
7              
8 1     1   537520 use 5.010001;
  1         10  
9 1     1   6 use strict;
  1         1  
  1         21  
10 1     1   4 use warnings;
  1         2  
  1         577  
11              
12             sub new {
13 12     12 1 60582 my ($class, %args) = @_;
14              
15 12         31 my $self = {};
16              
17 12 100       56 if (defined(my $time_zone = delete $args{time_zone})) {
18 6         11 $self->{time_zone} = do {
19 6 50       17 if (ref $time_zone) {
20 0         0 $time_zone;
21             } else {
22 6         49 require DateTime::TimeZone;
23 6         42 DateTime::TimeZone->new(name => $time_zone);
24             }
25             };
26             }
27 11         628 $self->{second_precision} = delete $args{second_precision};
28 11 100       43 if (keys %args) {
29 1         15 die "Unknown attribute(s): ".join(", ", sort keys %args);
30             }
31              
32 10         30 bless $self, $class;
33             }
34              
35             sub _format_date_or_time_or_datetime {
36 40     40   74 my ($self, $which, $dt) = @_;
37              
38 40 100       115 if ($self->{time_zone}) {
39 20         59 $dt = $dt->clone->set_time_zone($self->{time_zone});
40             }
41              
42 40         3597 my ($s_date, $s_time);
43              
44 40 100 100     151 if ($which eq 'date' || $which eq 'datetime') {
45 24         67 $s_date = $dt->ymd('-');
46             }
47              
48 40 100 100     425 if ($which eq 'time' || $which eq 'datetime') {
49 32         79 $s_time = $dt->hms(':');
50 32 100 100     308 if (($dt->nanosecond &&
      100        
51             !defined($self->{second_precision}) ||
52             $self->{second_precision})) {
53 14         99 my $s_secfrac;
54 14 100       33 if (!defined($self->{second_precision})) {
55 6         15 $s_secfrac = sprintf("%s", $dt->nanosecond / 1e9);
56             } else {
57 8         23 $s_secfrac .= sprintf("%.$self->{second_precision}f",
58             $dt->nanosecond / 1e9);
59             }
60 14         139 $s_time .= substr($s_secfrac, 1); # remove the "0" part
61             }
62 32         206 my $tz = $dt->time_zone;
63 32 100       193 if ($tz->is_floating) {
    100          
64             # do nothing, no time zone designation
65             } elsif ($tz->is_utc) {
66 12         85 $s_time .= "Z";
67             } else {
68 12         82 my $offset_secs = $tz->offset_for_datetime($dt);
69 12 50       745 my $sign = $offset_secs >= 0 ? "+" : "-";
70 12         24 my $h = int(abs($offset_secs) / 3600);
71 12         23 my $m = int((abs($offset_secs) - $h*3600) / 60);
72 12         43 $s_time .= sprintf "%s%02d:%02d", $sign, $h, $m;
73             }
74             }
75              
76 40 100       126 if ($which eq 'date') {
    100          
    50          
77 8         43 return $s_date;
78             } elsif ($which eq 'time') {
79 16         82 return $s_time;
80             } elsif ($which eq 'datetime') {
81 16         92 return $s_date . 'T' . $s_time;
82             } else {
83 0         0 die "BUG: Unknown which '$which'"; # shouldn't happen
84             }
85             }
86              
87             sub format_date {
88 8     8 1 24 my ($self, $dt) = @_;
89 8         19 $self->_format_date_or_time_or_datetime('date', $dt);
90             }
91              
92             sub format_time {
93 16     16 1 45 my ($self, $dt) = @_;
94 16         38 $self->_format_date_or_time_or_datetime('time', $dt);
95             }
96              
97             sub format_datetime {
98 16     16 1 49 my ($self, $dt) = @_;
99 16         39 $self->_format_date_or_time_or_datetime('datetime', $dt);
100             }
101              
102             1;
103             # ABSTRACT: Format DateTime object as ISO8601 date/time string
104              
105             __END__
106              
107             =pod
108              
109             =encoding UTF-8
110              
111             =head1 NAME
112              
113             DateTime::Format::ISO8601::Format - Format DateTime object as ISO8601 date/time string
114              
115             =head1 VERSION
116              
117             This document describes version 0.005 of DateTime::Format::ISO8601::Format (from Perl distribution DateTime-Format-ISO8601-Format), released on 2020-08-21.
118              
119             =head1 SYNOPSIS
120              
121             use DateTime::Format::ISO8601::Format;
122              
123             my $format = DateTime::Format::ISO8601::Format->new(
124             # time_zone => '...', # optional, default is DateTime object's time zone
125             # second_precision => 3, # optional, default is undef
126             );
127              
128             my $dt_floating = DateTime->new(year=>2018, month=>6, day=>23, hour=>19, minute=>2, second=>3);
129             my $dt_floating_frac = DateTime->new(year=>2018, month=>6, day=>23, hour=>19, minute=>2, second=>3, nanosecond=>0.456e9);
130             my $dt_utc = DateTime->new(year=>2018, month=>6, day=>23, hour=>19, minute=>2, second=>3, time_zone=>'UTC');
131             my $dt_sometz = DateTime->new(year=>2018, month=>6, day=>23, hour=>19, minute=>2, second=>3, time_zone=>'Asia/Jakarta');
132              
133             Formatting dates:
134              
135             say $format->format_date($dt_floating); # => 2018-06-23
136             say $format->format_date($dt_floating_frac); # => 2018-06-23
137             say $format->format_date($dt_utc); # => 2018-06-23
138             say $format->format_date($dt_sometz); # => 2018-06-23
139              
140             # effect of setting time_zone attribute to 'Asia/Jakarta' (which has the offset +07:00):
141              
142             say $format->format_date($dt_floating); # => 2018-06-23
143             say $format->format_date($dt_floating_frac); # => 2018-06-23
144             say $format->format_date($dt_utc); # => 2018-06-24
145             say $format->format_date($dt_sometz); # => 2018-06-23
146              
147             Formatting times:
148              
149             say $format->format_time($dt_floating); # => 19:02:03
150             say $format->format_time($dt_floating_frac); # => 19:02:03.456
151             say $format->format_time($dt_utc); # => 19:02:03Z
152             say $format->format_time($dt_sometz); # => 19:02:03+07:00
153              
154             # effect of setting time_zone attribute to 'Asia/Jakarta' (which has the offset of +07:00):
155              
156             say $format->format_time($dt_floating); # => 19:02:03+07:00
157             say $format->format_time($dt_floating_frac); # => 19:02:03.456+07:00
158             say $format->format_time($dt_utc); # => 02:02:03+07:00
159             say $format->format_time($dt_sometz); # => 19:02:03+07:00
160              
161             # effect of setting second_precision to 3
162              
163             say $format->format_time($dt_floating); # => 19:02:03.000
164             say $format->format_time($dt_floating_frac); # => 19:02:03.456
165             say $format->format_time($dt_utc); # => 19:02:03.000Z
166             say $format->format_time($dt_sometz); # => 19:02:03.000+07:00
167              
168             Formatting date+time:
169              
170             say $format->format_datetime($dt_floating); # => 2018-06-23T19:02:03
171             say $format->format_datetime($dt_floating_frac); # => 2018-06-23T19:02:03.456
172             say $format->format_datetime($dt_utc); # => 2018-06-23T19:02:03Z
173             say $format->format_datetime($dt_sometz); # => 2018-06-23T19:02:03+07:00
174              
175             =head1 DESCRIPTION
176              
177             This module formats L<DateTime> objects as ISO8601 date/time strings. It
178             complements L<DateTime::Format::ISO8601>.
179              
180             =head1 ATTRIBUTES
181              
182             =head2 time_zone
183              
184             Optional. Used to force the time zone of DateTime objects to be formatted.
185             Either string containing time zone name (e.g. "Asia/Jakarta", "UTC") or
186             L<DateTime::TimeZone> object. Will be converted to DateTime::TimeZone
187             internally.
188              
189             The default is to use the DateTime object's time zone.
190              
191             DateTime object with floating time zone will not have the time zone designation
192             in the ISO8601 string, e.g.:
193              
194             19:02:03
195             2018-06-23T19:02:03
196              
197             DateTime object with UTC time zone will have the "Z" time zone designation:
198              
199             19:02:03Z
200             2018-06-23T19:02:03Z
201              
202             DateTime object with other time zones will have the "+hh:mm" time zone
203             designation:
204              
205             19:02:03+07:00
206             2018-06-23T19:02:03+07:00
207              
208             =head2 second_precision
209              
210             Optional. A non-negative integer. Used to control formatting (number of
211             decimals) of the second fraction. The default is to only show fraction when they
212             exist, with whatever precision C<sprintf("%s")> outputs.
213              
214             =head1 METHODS
215              
216             =head2 new
217              
218             Usage:
219              
220             DateTime::Format::ISO8601::Format->new(%attrs) => obj
221              
222             =head2 format_date
223              
224             Usage:
225              
226             $format->format_date($dt) => str
227              
228             =head2 format_time
229              
230             Usage:
231              
232             $format->format_time($dt) => str
233              
234             =head2 format_datetime
235              
236             Usage:
237              
238             $format->format_datetime($dt) => str
239              
240             =head1 HOMEPAGE
241              
242             Please visit the project's homepage at L<https://metacpan.org/release/DateTime-Format-ISO8601-Format>.
243              
244             =head1 SOURCE
245              
246             Source repository is at L<https://github.com/perlancar/perl-DateTime-Format-ISO8601-Format>.
247              
248             =head1 BUGS
249              
250             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=DateTime-Format-ISO8601-Format>
251              
252             When submitting a bug or request, please include a test-file or a
253             patch to an existing test-file that illustrates the bug or desired
254             feature.
255              
256             =head1 SEE ALSO
257              
258             L<DateTime::Format::ISO8601>. Before v0.12, DateTime::Format::ISO8601 does not
259             feature a C<format_datetime()> method, so DateTime::Format::ISO8601::Format
260             supplies that functionality. After v0.12, DateTime::Format::ISO8601 already has
261             C<format_datetime()>, but currently DateTime::Format::ISO8601::Format's version
262             is faster (see L<Bencher::Scenario::FormattingISO8601DateTime>) and there are
263             C<format_date> and C<format_time> as well. So I'm keeping this module for now.
264              
265             L<DateTime::Format::Duration::ISO8601> to parse and format ISO8601 durations.
266              
267             =head1 AUTHOR
268              
269             perlancar <perlancar@cpan.org>
270              
271             =head1 COPYRIGHT AND LICENSE
272              
273             This software is copyright (c) 2020, 2018 by perlancar@cpan.org.
274              
275             This is free software; you can redistribute it and/or modify it under
276             the same terms as the Perl 5 programming language system itself.
277              
278             =cut