File Coverage

blib/lib/HTML/FormHandler/Field/Date.pm
Criterion Covered Total %
statement 51 51 100.0
branch 18 22 81.8
condition 3 6 50.0
subroutine 8 8 100.0
pod 1 4 25.0
total 81 91 89.0


line stmt bran cond sub pod time code
1             package HTML::FormHandler::Field::Date;
2             # ABSTRACT: a date field with formats
3             $HTML::FormHandler::Field::Date::VERSION = '0.40067';
4 3     3   2058 use Moose;
  3         4  
  3         23  
5             extends 'HTML::FormHandler::Field::Text';
6 3     3   14875 use DateTime;
  3         560847  
  3         102  
7 3     3   1808 use DateTime::Format::Strptime;
  3         99157  
  3         19  
8              
9              
10             has '+html5_type_attr' => ( default => 'date' );
11             has 'format' => ( is => 'rw', isa => 'Str', default => "%Y-%m-%d" );
12             has 'locale' => ( is => 'rw', isa => 'Str' ); # TODO
13             has 'time_zone' => ( is => 'rw', isa => 'Str' ); # TODO
14             has 'date_start' => ( is => 'rw', isa => 'Str', clearer => 'clear_date_start' );
15             has 'date_end' => ( is => 'rw', isa => 'Str', clearer => 'clear_date_end' );
16             has '+size' => ( default => '10' );
17             has '+deflate_method' => ( default => sub { \&date_deflate } );
18              
19             # translator for Datepicker formats to DateTime strftime formats
20             my $dp_to_dt = {
21             "d" => "\%e", # day of month (no leading zero)
22             "dd" => "\%1", # day of month (2 digits) "%d"
23             "o" => "\%4", # day of year (no leading zero) "%{day_of_year}"
24             "oo" => "\%j", # day of year (3 digits)
25             "D" => "\%a", # day name long
26             "DD" => "\%A", # day name short
27             "m" => "\%5", # month of year (no leading zero) "%{day_of_month}"
28             "mm" => "\%3", # month of year (two digits) "%m"
29             "M" => "\%b", # Month name short
30             "MM" => "\%B", # Month name long
31             "y" => "\%2", # year (2 digits) "%y"
32             "yy" => "\%Y", # year (4 digits)
33             "@" => "\%s", # epoch
34             };
35              
36             our $class_messages = {
37             'date_early' => 'Date is too early',
38             'date_late' => 'Date is too late',
39             };
40             sub get_class_messages {
41 3     3 0 4 my $self = shift;
42             return {
43 3         5 %{ $self->next::method },
  3         20  
44             %$class_messages,
45             }
46             }
47              
48             sub date_deflate {
49 5     5 0 9 my ( $self, $value ) = @_;
50              
51             # if not a DateTime, assume correctly formatted string and return
52 5 100 66     51 return $value unless blessed $value && $value->isa('DateTime');
53 3         14 my $format = $self->get_strf_format;
54 3         20 my $string = $value->strftime($format);
55 3         179 return $string;
56             }
57              
58             sub validate {
59 14     14 1 20 my $self = shift;
60              
61 14         49 my $format = $self->get_strf_format;
62 14         21 my @options;
63 14 100       480 push @options, ( time_zone => $self->time_zone ) if $self->time_zone;
64 14 50       470 push @options, ( locale => $self->locale ) if $self->locale;
65 14         122 my $strp = DateTime::Format::Strptime->new( pattern => $format, @options );
66              
67 14         28931 my $dt = eval { $strp->parse_datetime( $self->value ) };
  14         69  
68 14 100       8546 unless ($dt) {
69 5   33     21 $self->add_error( $strp->errmsg || $@ );
70 5         279 return;
71             }
72 9         287 $self->_set_value($dt);
73 9         41 my $val_strp = DateTime::Format::Strptime->new( pattern => "%Y-%m-%d", @options );
74 9 100       8139 if ( $self->date_start ) {
75 1         26 my $date_start = $val_strp->parse_datetime( $self->date_start );
76 1 50       388 die "date_start: " . $val_strp->errmsg unless $date_start;
77 1         26 my $cmp = DateTime->compare( $date_start, $dt );
78 1 50       103 $self->add_error($self->get_message('date_early')) if $cmp eq 1;
79             }
80 9 100       301 if ( $self->date_end ) {
81 3         94 my $date_end = $val_strp->parse_datetime( $self->date_end );
82 3 50       1695 die "date_end: " . $val_strp->errmsg unless $date_end;
83 3         91 my $cmp = DateTime->compare( $date_end, $dt );
84 3 100       403 $self->add_error($self->get_message('date_late')) if $cmp eq -1;
85             }
86             }
87              
88             sub get_strf_format {
89 17     17 0 28 my $self = shift;
90              
91             # if contains %, then it's a strftime format
92 17 100       572 return $self->format if $self->format =~ /\%/;
93 6         180 my $format = $self->format;
94 6         9 foreach my $dpf ( reverse sort keys %{$dp_to_dt} ) {
  6         87  
95 78         141 my $strf = $dp_to_dt->{$dpf};
96 78         548 $format =~ s/$dpf/$strf/g;
97             }
98 6         61 $format =~ s/\%1/\%d/g,
99             $format =~ s/\%2/\%y/g,
100             $format =~ s/\%3/\%m/g,
101             $format =~ s/\%4/\%{day_of_year}/g,
102             $format =~ s/\%5/\%{day_of_month}/g,
103             return $format;
104             }
105              
106             before 'get_tag' => sub {
107             my $self = shift;
108              
109             if (
110             $self->form
111             && $self->form->is_html5
112             && $self->html5_type_attr eq 'date' # subclass may be using different input type
113             && not( $self->format =~ /^(yy|%Y)-(mm|%m)-(dd|%d)$/ )
114             ) {
115             warn "Form is HTML5, but date field '" . $self->full_name
116             . "' has a format other than %Y-%m-%d, which HTML5 requires for date "
117             . "fields. Either correct the date format or set the is_html5 flag to false.";
118             }
119             };
120              
121             __PACKAGE__->meta->make_immutable;
122 3     3   2290 use namespace::autoclean;
  3         5  
  3         22  
123             1;
124              
125             __END__
126              
127             =pod
128              
129             =encoding UTF-8
130              
131             =head1 NAME
132              
133             HTML::FormHandler::Field::Date - a date field with formats
134              
135             =head1 VERSION
136              
137             version 0.40067
138              
139             =head1 SUMMARY
140              
141             This field may be used with the jQuery Datepicker plugin.
142              
143             You can specify the format for the date using jQuery formatDate strings
144             or DateTime strftime formats. (Default format is format => '%Y-%m-%d'.)
145              
146             d - "%e" - day of month (no leading zero)
147             dd - "%d" - day of month (two digit)
148             o - "%{day_of_year}" - day of the year (no leading zeros)
149             oo - "%j" - day of the year (three digit)
150             D - "%a" - day name short
151             DD - "%A" - day name long
152             m - "%{day_of_month}" - month of year (no leading zero)
153             mm - "%m" - month of year (two digit) "%m"
154             M - "%b" - month name short
155             MM - "%B" - month name long
156             y - "%y" - year (two digit)
157             yy - "%Y" - year (four digit)
158             @ - "%s" - Unix timestamp (ms since 01/01/1970)
159              
160             For example:
161              
162             has_field 'start_date' => ( type => 'Date', format => "dd/mm/y" );
163              
164             or
165              
166             has_field 'start_date' => ( type => 'Date', format => "%d/%m/%y" );
167              
168             You can also set 'date_end' and 'date_start' attributes for validation
169             of the date range. Use iso_8601 formats for these dates ("yyyy-mm-dd");
170              
171             has_field 'start_date' => ( type => 'Date', date_start => "2009-12-25" );
172              
173             Customize error messages 'date_early' and 'date_late':
174              
175             has_field 'start_date' => ( type => 'Date,
176             messages => { date_early => 'Pick a later date',
177             date_late => 'Pick an earlier date', } );
178              
179             =head2 Using with HTML5
180              
181             If the field's form has its 'is_html5' flag active, then the field's rendering
182             behavior changes in two ways:
183              
184             =over
185              
186             =item *
187              
188             It will render as <input type="date" ... /> instead of type="text".
189              
190             =item *
191              
192             If the field's format is set to anything other than ISO date format
193             (%Y-%m-%d), then attempting to render the field will result in a warning.
194              
195             (Note that the default value for the field's format attribute is, in fact,
196             the ISO date format.)
197              
198             =back
199              
200             =head1 AUTHOR
201              
202             FormHandler Contributors - see HTML::FormHandler
203              
204             =head1 COPYRIGHT AND LICENSE
205              
206             This software is copyright (c) 2016 by Gerda Shank.
207              
208             This is free software; you can redistribute it and/or modify it under
209             the same terms as the Perl 5 programming language system itself.
210              
211             =cut