File Coverage

lib/DateTimeX/Mashup/Shiras/Types.pm
Criterion Covered Total %
statement 48 49 97.9
branch 15 20 75.0
condition 2 3 66.6
subroutine 14 14 100.0
pod n/a
total 79 86 91.8


line stmt bran cond sub pod time code
1             package DateTimeX::Mashup::Shiras::Types;
2             our $AUTHORITY = 'cpan:JANDREW';
3 2     2   463113 use version; our $VERSION = version->declare("v0.36.12");
  2         1360  
  2         12  
4 2     2   156 use strict;
  2         2  
  2         34  
5 2     2   6 use warnings;
  2         2  
  2         86  
6 2     2   49 use 5.010;
  2         6  
7 2     2   7 use DateTime 1.34;
  2         31  
  2         39  
8 2     2   791 use DateTime::Format::Epoch 0.013;
  2         63440  
  2         63  
9 2     2   980 use DateTimeX::Format::Excel v0.12;
  2         456668  
  2         107  
10 2     2   677 use DateTime::Format::Flexible;
  2         81986  
  2         21  
11 2     2   121 use Type::Utils 1.000 -all;
  2         54  
  2         17  
12             use Type::Library 1.000
13 2         12 -base,
14             -declare => qw(
15             WeekDay
16             DateTimeDate
17              
18             WeekDayFromStr
19             DateTimeDateFromHashRef
20             DateTimeDateFromArrayRef
21             DateTimeDateFromNum
22             DateTimeDateFromStr
23 2     2   3780 );
  2         30  
24 2         15 use Types::Standard qw(
25             InstanceOf
26             HashRef
27             ArrayRef
28             Str
29             Num
30             is_Num
31             Int
32 2     2   1308 );
  2         3  
33 2     2   1581 BEGIN{ extends "Types::Standard" };
34             my $try_xs =
35             exists($ENV{PERL_TYPE_TINY_XS}) ? !!$ENV{PERL_TYPE_TINY_XS} :
36             exists($ENV{PERL_ONLY}) ? !$ENV{PERL_ONLY} :
37             1;
38             if( $try_xs and exists $INC{'Type/Tiny/XS.pm'} ){
39             eval "use Type::Tiny::XS 0.010";
40             if( $@ ){
41             die "You have loaded Type::Tiny::XS but versions prior to 0.010 will cause this module to fail";
42             }
43             }
44              
45             #########1 Dispatch Tables and Module Variables 5#########6#########7#########8#########9
46              
47             our $epochdt = DateTime->new(
48             year => 1970,
49             month => 1,
50             day => 1
51             );
52              
53             our $excel_type = 'win_excel';
54             our $european_first = 0;
55              
56             my $epochformtr = DateTime::Format::Epoch->new(
57             epoch => $epochdt,
58             unit => 'seconds',
59             type => 'int',
60             skip_leap_seconds => 1,
61             start_at => 0,
62             local_epoch => undef,
63             );
64             my $excelformter =
65             my $weekdays = {
66             'Monday' => 1,
67             'Tuesday' => 2,
68             'Wednessday' => 3,
69             'Thursday' => 4,
70             'Friday' => 5,
71             'Saturday' => 6,
72             'Sunday' => 7,
73             };
74             #~ my $local_time_zone = DateTime::TimeZone->new( name => 'local' );
75              
76             #########1 Subtypes 3#########4#########5#########6#########7#########8#########9
77              
78             declare WeekDay, as Int,
79             where{ $_ >= 1 and $_ <= 7 },
80             message{
81             ( defined $_ ) ?
82             "-$_- cannot be coerced to a weekday" :
83             'No value passed to the weekday type test'
84             };
85              
86             declare_coercion WeekDayFromStr,
87             to_type WeekDay,
88             from Str,
89             via{
90             my $str = $_;
91             return $str if is_Num( $str );
92             for my $day ( keys %$weekdays ) {
93             if ( $day =~ /^$str/i ) {
94             return $weekdays->{$day};
95             }
96             }
97             return "can't match -$str- to day list";
98             };
99              
100             declare DateTimeDate, as InstanceOf[ 'DateTime' ],
101             message{ $_ };
102              
103             declare_coercion DateTimeDateFromHashRef,
104             to_type DateTimeDate,
105             from HashRef,
106             via{
107             my $dt;
108             my %input = %$_;
109             return(
110             ( eval{ $dt = DateTime->new(%input) } ) ?
111             $dt : "Failed to create a DateTime object from the HashRef\n" . Dump( $_ )
112             );
113             };
114              
115             declare_coercion DateTimeDateFromArrayRef,
116             to_type DateTimeDate,
117             from ArrayRef,
118             via{
119             my $dt;
120             my ( $arg, $type, $time_zone ) = @$_;
121             $type = _deduce_epoch_type( $arg, $type );
122             return _convert_list_to_date_time( $arg, $type, $time_zone );
123             };
124              
125             declare_coercion DateTimeDateFromNum,
126             to_type DateTimeDate,
127             from Num,
128             via{
129             my $dt;
130             my $arg = $_;
131             my $type = _deduce_epoch_type( $arg );
132             return "Could not use the number -|$arg|- as an Excel date or a Nix date" if ! $type or $type eq 'bad_num';
133             return _convert_list_to_date_time( $arg, $type,);
134             };
135              
136             declare_coercion DateTimeDateFromStr,
137             to_type DateTimeDate,
138             from Str,
139             via{
140             my $str = $_;
141             my ( $dt_us, $dt_eu );
142             eval '$dt_us = DateTime::Format::Flexible->parse_datetime( $str )';
143             eval '$dt_eu = DateTime::Format::Flexible->parse_datetime( $str, european => 1, )';
144             if( !$dt_us and !$dt_eu ){# handle double digit years in formats unreadable by ~::Flexible
145             my $current_year = DateTime->now()->truncate( to => 'year' );
146             my $century_prefix = substr( $current_year, 0, 2 );
147             my $century_postfix = substr( $current_year, 2, 2 );
148             my $bump_year = ( $century_postfix + 20 > 99 ) ? ( $century_postfix - 80 ) : undef;# The double digit years are probably less than 21 years in the future of the processing time
149             my $drop_year = ( $century_postfix - 79 < 0 ) ? ( $century_postfix + 21 ) : undef;# The double digit years are probably less than 81 years in the past of the processing time
150             $str =~ /(\d{1,2})\D(\d{1,2})\D(\d{1,2})(\s|T)(\d{1,2})\D(\d{1,2})(\D(\d{1,2}))?/;
151             if( defined $1 and defined $2 and defined $3 ){
152             my $year = $3;
153             $year = (
154             (defined $bump_year and $year <= $bump_year ) ? $century_prefix + 1 :
155             (defined $drop_year and $year >= $drop_year ) ? $century_prefix - 1 : $century_prefix ) . sprintf '%02u', $year;
156             my $us_str = sprintf "%u-%02u-%02uT%02u:%02u:%02u", $year, $1, $2, $5, $6, ($7//'00');
157             my $eu_str = sprintf "%u-%02u-%02uT%02u:%02u:%02u", $year, $2, $1, $5, $6, ($7//'00');
158             eval '$dt_us = DateTime::Format::Flexible->parse_datetime( $us_str )';
159             eval '$dt_eu = DateTime::Format::Flexible->parse_datetime( $eu_str )';# european => 1,
160             }
161             }
162             my $return =
163             ( $DateTimeX::Mashup::Shiras::Types::european_first and $dt_eu )? $dt_eu :
164             ( $dt_us ) ? $dt_us : ( $dt_eu ) ? $dt_eu :
165             "Failed to build a date time from DateTime::Format::Flexible (or any other method) for string -$str-\n";
166             return $return;
167             };
168              
169             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
170              
171             sub _deduce_epoch_type{
172 12     12   17 my ( $num, $type ) = @_;
173 12 50 66     162 $type //=
    100          
    100          
174             ( $num =~ /^(\d{7,11}|60|0|-\d+)$/ ) ? 'epoch' :#choose epoch style
175             ( $num =~ /^(\d{0,6}(.\d*)?|\d{7,}.\d+)$/ ) ? 'excel' :#choose excel style
176             ( $num =~ /^-\d*.\d+$/ ) ? 'bad_num' :#Negative decimals not allowed
177             'bad_num';
178 12 100       27 $type = ( $type eq 'excel' ) ? $excel_type : $type;
179 12         21 return $type;
180             }
181              
182             sub _convert_list_to_date_time{
183 10     10   17 my ( $arg, $type, $time_zone ) = @_;
184 10         16 my ( $formatter, $parser_args );
185 10 100       33 if( $type eq 'epoch' ){
    50          
186 6         37 $formatter = DateTime::Format::Epoch->new(
187             epoch => $epochdt,
188             unit => 'seconds',
189             type => 'int', # or 'float', 'bigint'
190             skip_leap_seconds => 1,
191             start_at => 0,
192             local_epoch => undef,
193             );
194             }elsif( $type =~ /_excel$/ ){
195 4         46 $formatter = DateTimeX::Format::Excel->new(
196             system_type => $type
197             );
198             }else{
199 0         0 return "Unknown type -$type- passed to date conversion";
200             }
201 10         6313 my $dt = $formatter->parse_datetime( $arg );
202 10 100       5316 if( DateTimeDate->check( $dt ) ){
203 8 50       113 $dt->set_time_zone( $time_zone ) if $time_zone;
204 8         161 return $dt;
205             }else{
206 2 50       33 my $return =
    50          
207             ( $type eq 'epoch' ) ?
208             "Attempting to treat -$arg- as a Nix epoch failed in the DateTime conversion" :
209             ( $type =~ /_excel$/ ) ?
210             "Attempting to treat -$arg- as an Excel serialized date failed in the DateTime conversion" :
211             "Failed to build a date time from DateTime::Format::DateManip (or any other method) for string -$arg-" ;
212 2         51 return $return;
213             }
214             }
215              
216              
217             #########1 Phinish 3#########4#########5#########6#########7#########8#########9
218              
219             __PACKAGE__->meta->make_immutable;
220             1;
221              
222             #########1 main pod docs 3#########4#########5#########6#########7#########8#########9
223             __END__
224              
225             =head1 NAME
226              
227             DateTimeX::Mashup::Shiras::Types - Types for DateTimeX::Mashup::Shiras
228              
229             =head1 SYNOPSIS
230              
231             #!perl
232             package MyPackage;
233              
234             use Moose;
235             use DateTimeX::Mashup::Shiras::Types qw(
236             WeekDay
237             WeekDayFromStr
238             );
239              
240             has 'attribute_1' => (
241             is => 'ro',
242             isa => WeekDay->plus_coercions( WeekDayFromStr ),
243             );
244              
245             =head1 DESCRIPTION
246              
247             L<Shiras|http://en.wikipedia.org/wiki/Moose#Subspecies> - A small subspecies of
248             Moose found in the western United States (of America).
249              
250             This is the custom type class that ships with the L<DateTimeX::Mashup::Shiras>
251             package. Wherever possible coersion failures are passed back to the type so
252             type errors will be explained. The types are implemented using L<Type::Tiny>.
253              
254             =head2 L<Caveat utilitor|http://en.wiktionary.org/wiki/Appendix:List_of_Latin_phrases_(A%E2%80%93E)#C>
255              
256             All type tests included with this package are considered to be the fixed definition of
257             the types. Any definition not included in the testing is considered flexible.
258              
259             This module uses L<Type::Tiny> which can, in the background, use L<Type::Tiny::XS>.
260             While in general this is a good thing you will need to make sure that
261             Type::Tiny::XS is version 0.010 or newer since the older ones didn't support the
262             'Optional' method.
263              
264             =head2 Types
265              
266             There are no included coercions with these types. Any coercion usage should be
267             with -E<gt>plus_coercions from the L<list|/Coercions> below.
268              
269             =head3 WeekDay
270              
271             =over
272              
273             B<Definition: >integers ( 1 .. 7 )
274              
275             B<Coercions: >from a string. The type will try to qr//i match the passed string
276             to an english name of the week.
277              
278             =back
279              
280             =head3 DateTimeDate
281              
282             =over
283              
284             B<Definition: >a L<DateTime> instance
285              
286             =back
287              
288             =head2 Coercions
289              
290             These are named coercions available for export by this module. For the
291             coercions to work with the Type they must be added to the type via
292             -E<gt>plus_coercions. To test the type and coercions together use the
293             -E<gt>coerce or -E<gt>assert_coerce functions.
294              
295             =head3 WeekDayFromStr
296              
297             =over
298              
299             B<Definition: >Takes a string that matches the full or any portion of
300             the initial letters in an english weekday name and converts it to an integer
301             (1..7) where 1 = Monday. The match is case independant (qr/$_/i).
302              
303             =back
304              
305             =head3 DateTimeDateFromHashRef
306              
307             =over
308              
309             B<Definition: >This will take a HashRef and attempt to treat is as %$args for
310             the function Datetime->new( %$args )
311              
312             =back
313              
314             =head3 DateTimeDateFromArrayRef
315              
316             =over
317              
318             B<Definition: > this will take an ArrayRef and use up to the first three positions
319             in the array as; [ $arg, $type, $time_zone ]. This is only used for passing numbers
320             coded as excel or unix epochs to be converted to DateTime objects. The elements are
321             used as follows.
322              
323             =over
324              
325             B<$arg:> this is expected to be a number that falls either in the L<Unix|DateTime::Format::Epoch>
326             range or in the L<Microsoft Excel|DateTimeX::Format::Excel> range.
327              
328             B<$type:> this is a way to force the interpretation of the number. The four
329             possibilites are; excel, win_excel, apple_excel, or epoch. If epoch is called then
330             the number is interpreted by L<DateTime::Format::Epoch> and the global variable
331             L</$DateTimeX::Mashup::Shiras::Types::epochdt> will be used. A $type eq 'excel'
332             setting will convert to the global variable L</$DateTimeX::Mashup::Shiras::Types::excel_type>.
333             Then the value will be passed to L<DateTimeX::Format::Excel> as the 'system_type'
334             for interpretation by that program.
335              
336             B<$time_zone:> if a value is entered then after $arg is converted to a DateTime object
337             the instance will have $dt-E<gt>set_time_zone( $time_zone ) called on it.
338              
339             =back
340              
341             =back
342              
343             =head3 DateTimeDateFromNum
344              
345             =over
346              
347             B<Definition: > This will check the number for 0 or 60 (microsoft issues),
348             negative integers, and positive integers with more than 7 digits and read them
349             as epoch (Nixy) dates. It will turn any positive integer or decimial with
350             less than 7 leading digits into an excel date using L<DateTime::Format::Excel>.
351             All positive decimals with 7 or more digits will also be treated as excel dates.
352             Negative decimals will fail.This will take a number and guess what type it is.
353             The data is then handled the same as L<an ArrayRef|/DateTimeDateFromArrayRef>.
354              
355             =back
356              
357             =head3 DateTimeDateFromStr
358              
359             =over
360              
361             B<Definition: > This should be the final fall back check and it attempts to
362             turn any String into a DateTime object with L<DateTime::Format::Flexible>.
363              
364             =back
365              
366             =head1 GLOBAL VARIABLES
367              
368             =head2 $ENV{Smart_Comments}
369              
370             The module uses L<Smart::Comments> if the '-ENV' option is set. The 'use' is
371             encapsulated in an if block triggered by an environmental variable to comfort
372             non-believers. Setting the variable $ENV{Smart_Comments} in a BEGIN block will
373             load and turn on smart comment reporting. There are three levels of 'Smartness'
374             available in this module '###', '####', and '#####'.
375              
376             =head2 $DateTimeX::Mashup::Shiras::Types::epochdt
377              
378             This variable holds a L<DateTime> object set to; year => 1970, month => 1,
379             day => 1. To be used by L<DateTime::Format::Epoch> as the Epoch start. If you
380             wish to change the epoch start change this variable. All changes are permanent
381             until the next change.
382              
383             =head2 $DateTimeX::Mashup::Shiras::Types::excel_type
384              
385             This variable holds the default excel type for L<DateTimeX::Format::Excel>.
386             The default is 'win_excel'.
387              
388             =head2 $DateTimeX::Mashup::Shiras::Types::european_first
389              
390             When date strings are parsed it checks D-M-Y prior to M-D-Y. This is default off.
391              
392             B<range:> 1|0
393              
394             =head1 SUPPORT
395              
396             L<github DateTimeX-Mashup-Shiras/issues|https://github.com/jandrew/DateTimeX-Mashup-Shiras/issues>
397              
398             =head1 TODO
399              
400             =over
401              
402             B<1.> Add L<Log::Shiras|https://github.com/jandrew/Log-Shiras> debugging in exchange for
403             L<Smart::Comments>
404              
405             =over
406              
407             * Get Log::Shiras CPAN ready first! (Some horrible deep recursion happens so far)
408              
409             =back
410              
411             =back
412              
413             =head1 AUTHOR
414              
415             =over
416              
417             Jed Lund
418              
419             jandrew@cpan.org
420              
421             =back
422              
423             =head1 COPYRIGHT
424              
425             This program is free software; you can redistribute
426             it and/or modify it under the same terms as Perl itself.
427              
428             The full text of the license can be found in the
429             LICENSE file included with this module.
430              
431             This software is copyrighted (c) 2013, 2014 by Jed Lund.
432              
433             =head1 DEPENDANCIES
434              
435             =over
436              
437             B<5.010> - (L<perl>)
438              
439             L<version>
440              
441             L<Type::Tiny>
442              
443             L<DateTime>
444              
445             L<DateTime::Format::Epoch> - 0.013
446              
447             L<DateTimeX::Format::Excel>
448              
449             L<DateTime::Format::Flexible>
450              
451             =back
452              
453             =head1 SEE ALSO
454              
455             =over
456              
457             L<Time::Piece>
458              
459             L<DateTime::Format::Excel>
460              
461             L<MooseX::Types>
462              
463             L<Date::Parse>
464              
465             L<Date::Manip::Date>
466              
467             L<DateTimeX::Format>
468              
469             =back
470              
471             =cut
472              
473             #########1 Main POD ends 3#########4#########5#########6#########7#########8#########9