File Coverage

lib/DateTime/Format/Salesforce.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # Copyright (C) 2012 Carl Vincent
2             # based on DateTime::Format::ISO8601 by Joshua Hoblitt
3              
4             package DateTime::Format::Salesforce;
5              
6 3     3   243905 use strict;
  3         8  
  3         195  
7 3     3   16 use warnings;
  3         7  
  3         98  
8              
9 3     3   15 use vars qw( $VERSION );
  3         12  
  3         286  
10             $VERSION = '0.01_03';
11              
12 3     3   14 use Carp qw( croak );
  3         3  
  3         206  
13 3     3   2694 use DateTime;
  0            
  0            
14             use DateTime::Format::Builder;
15             use Params::Validate qw( validate validate_pos BOOLEAN OBJECT SCALAR );
16              
17             {
18             my $default_legacy_year;
19             sub DefaultLegacyYear {
20             my $class = shift;
21              
22             ( $default_legacy_year ) = validate_pos( @_,
23             {
24             type => BOOLEAN,
25             callbacks => {
26             'is 0, 1, or undef' =>
27             sub { ! defined( $_[0] ) || $_[0] == 0 || $_[0] == 1 },
28             },
29             }
30             ) if @_;
31              
32             return $default_legacy_year;
33             }
34             }
35             __PACKAGE__->DefaultLegacyYear( 1 );
36              
37             {
38             my $default_cut_off_year;
39             sub DefaultCutOffYear {
40             my $class = shift;
41              
42             ( $default_cut_off_year ) = validate_pos( @_,
43             {
44             type => SCALAR,
45             callbacks => {
46             'is between 0 and 99' =>
47             sub { $_[0] >= 0 && $_[0] <= 99 },
48             },
49             }
50             ) if @_;
51              
52             return $default_cut_off_year;
53             }
54             }
55             # the same default value as DT::F::Mail
56             __PACKAGE__->DefaultCutOffYear( 49 );
57              
58             sub new {
59             my( $class ) = shift;
60              
61             my %args = validate( @_,
62             {
63             base_datetime => {
64             type => OBJECT,
65             can => 'utc_rd_values',
66             optional => 1,
67             },
68             legacy_year => {
69             type => BOOLEAN,
70             default => $class->DefaultLegacyYear,
71             callbacks => {
72             'is 0, 1, or undef' =>
73             sub { ! defined( $_[0] ) || $_[0] == 0 || $_[0] == 1 },
74             },
75             },
76             cut_off_year => {
77             type => SCALAR,
78             default => $class->DefaultCutOffYear,
79             callbacks => {
80             'is between 0 and 99' =>
81             sub { $_[0] >= 0 && $_[0] <= 99 },
82             },
83             },
84             }
85             );
86              
87             $class = ref( $class ) || $class;
88              
89             my $self = bless( \%args, $class );
90              
91             if ( $args{ base_datetime } ) {
92             $self->set_base_datetime( object => $args{ base_datetime } );
93             }
94              
95             return( $self );
96             }
97              
98             # lifted from DateTime
99             sub clone { bless { %{ $_[0] } }, ref $_[0] }
100              
101             sub base_datetime { $_[0]->{ base_datetime } }
102              
103             sub set_base_datetime {
104             my $self = shift;
105              
106             my %args = validate( @_,
107             {
108             object => {
109             type => OBJECT,
110             can => 'utc_rd_values',
111             },
112             }
113             );
114            
115             # ISO8601 only allows years 0 to 9999
116             # this implimentation ignores the needs of expanded formats
117             my $dt = DateTime->from_object( object => $args{ object } );
118             my $lower_bound = DateTime->new( year => 0 );
119             my $upper_bound = DateTime->new( year => 10000 );
120              
121             if ( $dt < $lower_bound ) {
122             croak "base_datetime must be greater then or equal to ",
123             $lower_bound->iso8601;
124             }
125             if ( $dt >= $upper_bound ) {
126             croak "base_datetime must be less then ", $upper_bound->iso8601;
127             }
128              
129             $self->{ base_datetime } = $dt;
130              
131             return $self;
132             }
133              
134             sub legacy_year { $_[0]->{ legacy_year } }
135              
136             sub set_legacy_year {
137             my $self = shift;
138              
139             my @args = validate_pos( @_,
140             {
141             type => BOOLEAN,
142             callbacks => {
143             'is 0, 1, or undef' =>
144             sub { ! defined( $_[0] ) || $_[0] == 0 || $_[0] == 1 },
145             },
146             }
147             );
148              
149             $self->{ legacy_year } = $args[0];
150              
151             return $self;
152             }
153              
154             sub cut_off_year { $_[0]->{ cut_off_year } }
155              
156             sub set_cut_off_year {
157             my $self = shift;
158              
159             my @args = validate_pos( @_,
160             {
161             type => SCALAR,
162             callbacks => {
163             'is between 0 and 99' =>
164             sub { $_[0] >= 0 && $_[0] <= 99 },
165             },
166             }
167             );
168              
169             $self->{ cut_off_year } = $args[0];
170              
171             return $self;
172             }
173              
174             DateTime::Format::Builder->create_class(
175             parsers => {
176             parse_datetime => [
177             {
178             #YYYY-MM-DDThh:mm:ss.ss[+-]hh:mm 1985-04-12T10:15:30.5+01:00 1985-04-12T10:15:30.5-05:00
179             regex => qr/^ (\d{4}) - (\d\d) - (\d\d)
180             T?? (\d\d) : (\d\d) : (\d\d) [\.,] (\d+)
181             ([+-] \d\d \d\d) $/x,
182             params => [ qw( year month day hour minute second nanosecond time_zone ) ],
183             postprocess => [
184             \&_fractional_second,
185             \&_normalize_offset,
186             ],
187             },
188             ],
189              
190             }
191             );
192             sub _fractional_second {
193             my %p = @_;
194              
195             $p{ parsed }{ nanosecond } = ".$p{ parsed }{ nanosecond }" * 10**9;
196              
197             return 1;
198             }
199              
200             sub _normalize_offset {
201             my %p = @_;
202              
203             $p{ parsed }{ time_zone } =~ s/://;
204              
205             if( length $p{ parsed }{ time_zone } == 3 ) {
206             $p{ parsed }{ time_zone } .= '00';
207             }
208              
209             return 1;
210             }
211              
212             1;
213             __END__