File Coverage

blib/lib/Data/MuForm/Field/Date.pm
Criterion Covered Total %
statement 12 51 23.5
branch 0 22 0.0
condition 0 6 0.0
subroutine 4 8 50.0
pod 1 4 25.0
total 17 91 18.6


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