File Coverage

blib/lib/DateTime/Format/LDAP.pm
Criterion Covered Total %
statement 58 59 98.3
branch 38 44 86.3
condition 4 6 66.6
subroutine 6 6 100.0
pod 3 3 100.0
total 109 118 92.3


line stmt bran cond sub pod time code
1             package DateTime::Format::LDAP;
2             $DateTime::Format::LDAP::VERSION = '0.001';
3 2     2   33522 use strict;
  2         5  
  2         77  
4              
5 2     2   2902 use DateTime;
  2         362137  
  2         106  
6              
7 2     2   39 use Params::Validate qw( validate_with SCALAR );
  2         3  
  2         1877  
8              
9             sub new
10             {
11 1     1 1 466 my $class = shift;
12 1         3 my $self;
13 1         5 %$self = @_;
14              
15 1         21 return bless $self, $class;
16             }
17              
18             # key is string length
19             my %valid_formats =
20             ( 14 =>
21             { params => [ qw( year month day hour minute second ) ],
22             regex => qr/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/,
23             },
24             12 =>
25             { params => [ qw( year month day hour minute ) ],
26             regex => qr/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/,
27             },
28             10 =>
29             { params => [ qw( year month day hour ) ],
30             regex => qr/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)$/,
31             },
32             );
33              
34             sub parse_datetime
35             {
36 15     15 1 4829 my ( $self, $date ) = @_;
37              
38 15 100       46 $self = {} unless ref $self;
39 15 100       47 my $asn1 = defined $self->{asn1} ? $self->{asn1} : 0;
40              
41             # save for error messages
42 15         19 my $original = $date;
43              
44 15         20 my %p;
45 15 100       77 if ( $date =~ s/Z$// )
    100          
    50          
46             {
47 9         20 $p{time_zone} = 'UTC';
48             }
49             elsif ( $date =~ s/([+-]([01]\d|2[0-3])[0-5]\d)$// )
50             {
51 5         16 $p{time_zone} = $1;
52             }
53             elsif ( $asn1 )
54             {
55 1         3 $p{time_zone} = 'floating';
56             }
57             else
58             {
59 0         0 die "Invalid LDAP datetime string ($original)\n";
60             }
61              
62 15         21 my $fraction;
63 15 100       47 if ( $date =~ s/[.,](\d+)// ) {
64 3         5 $fraction = "0.$1";
65             }
66              
67 15 50       49 my $format = $valid_formats{ length $date }
68             or die "Invalid LDAP datetime string ($original)\n";
69              
70 15         100 @p{ @{ $format->{params} } } = $date =~ /$format->{regex}/;
  15         96  
71              
72             # If is omitted, then represents a fraction of an
73             # hour; otherwise, if and are omitted, then
74             # represents a fraction of a minute; otherwise,
75             # represents a fraction of a second.
76 15 100       48 if (defined $fraction )
77             {
78 3 100       12 if ( exists $p{second} )
    100          
79             {
80 1         6 $p{nanosecond} = int($fraction * 1000**3 + 0.5);
81             }
82             elsif ( exists $p{minute} )
83             {
84 1         5 $p{second} = int($fraction * 60);
85 1         3 $fraction = $fraction * 60 - $p{second};
86 1 50       3 if ( $fraction )
87             {
88 1         4 $p{nanosecond} = int($fraction * 1000**3 + 0.5);
89             }
90             }
91             else
92             {
93 1         5 $p{minute} = int($fraction * 60);
94 1         3 $fraction = $fraction * 60 - $p{minute};
95 1 50       4 if ( $fraction )
96             {
97 1         2 $p{second} = int($fraction * 60);
98 1         3 $fraction = $fraction * 60 - $p{second};
99 1 50       3 if ( $fraction )
100             {
101 1         3 $p{nanosecond} = int($fraction * 1000**3 + 0.5);
102             }
103             }
104             }
105             }
106 15         77 return DateTime->new(%p);
107             }
108              
109             sub format_datetime
110             {
111 16     16 1 25287 my ( $self, $dt ) = @_;
112              
113 16 100       45 $self = {} unless ref $self;
114 16 100       44 my $asn1 = defined $self->{asn1} ? $self->{asn1} : 0;
115 16 100       35 my $offset = defined $self->{offset} ? $self->{offset} : 0;
116              
117 16         44 my $tz = $dt->time_zone;
118              
119 16 50 66     97 die 'LDAP datetime cannot be floating' if $tz->is_floating and !$asn1;
120              
121 16 100       122 unless ( $offset )
122             {
123 14 100 66     39 unless ( $tz->is_utc || $tz->is_floating )
124             {
125 4         47 $dt = $dt->clone->set_time_zone('UTC');
126 4         663 $tz = $dt->time_zone;
127             }
128             }
129              
130 16         90 my $base =
131             sprintf( '%04d%02d%02d%02d%02d%02d',
132             $dt->year, $dt->month, $dt->day,
133             $dt->hour, $dt->minute, $dt->second );
134              
135 16 100       355 $base .= substr sprintf( '%.9g', $dt->nanosecond / 1000**3 ), 1 if $dt->nanosecond;
136              
137 16 100       138 return $base if $tz->is_floating;
138              
139 15 100       71 return $base . 'Z' if $tz->is_utc;
140              
141 1         6 return $base . $tz->offset_as_string($dt->offset);
142             }
143              
144             1;
145              
146             __END__