File Coverage

blib/lib/DateTime/Format/LDAP.pm
Criterion Covered Total %
statement 64 65 98.4
branch 38 44 86.3
condition 4 6 66.6
subroutine 8 8 100.0
pod 3 3 100.0
total 117 126 92.8


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