File Coverage

blib/lib/Bank/Holidays.pm
Criterion Covered Total %
statement 57 73 78.0
branch 15 34 44.1
condition 3 6 50.0
subroutine 9 9 100.0
pod 0 3 0.0
total 84 125 67.2


line stmt bran cond sub pod time code
1             package Bank::Holidays;
2              
3 3     3   48266 use 5.006001;
  3         7  
4 3     3   9 use strict;
  3         4  
  3         46  
5 3     3   16 use warnings;
  3         3  
  3         67  
6 3     3   1724 use HTML::TableExtract;
  3         32443  
  3         21  
7 3     3   2395 use LWP::UserAgent;
  3         116740  
  3         90  
8 3     3   2410 use DateTime;
  3         939227  
  3         1802  
9              
10             our $VERSION = '0.84';
11              
12             sub new {
13 8     8 0 4072 my ( $package, %params ) = @_;
14              
15 8         9 my $param;
16             $param->{dt} =
17             $params{dt}
18             ? $params{dt}
19             : $params{date}
20             ? $params{date}
21 8 50       36 : DateTime->now;
    100          
22 8         283 $param->{holidays} = reserve_holidays();
23 8         39 bless $param, $package;
24             }
25              
26             sub reserve_holidays() {
27 8     8 0 45 my $te = HTML::TableExtract->new();
28              
29 8         717 my $ua = LWP::UserAgent->new();
30              
31 8         5217 $ua->timeout(120);
32              
33 8   33     85 my $home = $ENV{HOME} || $ENV{LOCALAPPDATA};
34              
35 8 100       172 unless ( -d $home . "/.bankholidays" ) {
36 1         111 mkdir( $home . "/.bankholidays" );
37             }
38              
39 8         16 my $cache = $home . "/.bankholidays/frbholidays.html";
40              
41             # Cache the content from the FRB since holdays are unlikely to
42             # change from day to day (or year to year)
43              
44 8         8 my $content;
45              
46 8 100 66     146 if ( -f $cache && ( time() - ( stat($cache) )[9] ) < 86400 ) {
47 7 50       179 open my $fh, "<", $cache or die $!;
48 7         7 $content = do { local $/ = <$fh> };
  7         141  
49 7         50 close $fh;
50             }
51             else {
52 1         2 my $url = 'http://www.federalreserve.gov/aboutthefed/k8.htm';
53              
54 1         8 my $request = HTTP::Request->new( 'GET', $url );
55              
56 1         5381 my $response = $ua->request($request);
57              
58 1         322273 $content = $response->content();
59              
60 1 50       911957 open my $fh, ">", $cache or die $!;
61 1         5 print {$fh} $content;
  1         31  
62 1         128 close $fh;
63             }
64              
65 8         42 $te->parse($content);
66              
67 8         299 my $months = {
68             'January' => 1,
69             'February' => 2,
70             'March' => 3,
71             'April' => 4,
72             'May' => 5,
73             'June' => 6,
74             'July' => 7,
75             'August' => 8,
76             'September' => 9,
77             'October' => 10,
78             'November' => 11,
79             'December' => 12
80             };
81              
82 8         8 my $holidays;
83              
84 8         23 foreach my $ts ( $te->tables ) {
85 0 0       0 next if ( $ts->coords ) != 2;
86 0         0 my @colyears;
87 0         0 foreach my $row ( $ts->rows ) {
88              
89 0 0       0 next unless @$row;
90 0 0       0 map { s/\r|\n//g if $_ } @$row;
  0         0  
91 0         0 my $colcount = 0;
92 0         0 foreach my $col (@$row) {
93 0 0       0 if ($col) {
94 0 0       0 if ( $col =~ /(\d{4})/ ) {
    0          
95 0         0 $colyears[$colcount] = $1;
96             }
97             elsif ( $col =~ /(\w+)\s(\d{1,2})(\*?)/ ) {
98 0         0 push @{ $holidays->{ $colyears[$colcount] }->{ $months->{$1} } },
  0         0  
99             {
100             day => $2,
101             satflag => $3
102             };
103              
104             }
105             }
106 0         0 $colcount++;
107             }
108             }
109             }
110 8         235 return $holidays;
111             }
112              
113             sub is_holiday {
114 12     12 0 1048 my ( $param, %opts ) = @_;
115              
116 12 50       33 if ( $opts{date} ) {
117 0         0 $param->{dt} = $opts{date};
118             }
119              
120 12 100       32 if ( $opts{Tomorrow} ) {
    100          
121 4         17 $param->{dt}->add( days => 1 );
122             }
123             elsif ( $opts{Yesterday} ) {
124 4         16 $param->{dt}->subtract( days => 1 );
125             }
126 12 50       5603 return 1 if $param->{dt}->dow == 7;
127 12         44 foreach my $holiday ( @{ $param->{holidays}->{ $param->{dt}->year }->{ int( $param->{dt}->month ) } } ) {
  12         34  
128 0 0       0 return 1 if int( $param->{dt}->day ) == $holiday->{day};
129             }
130 12         127 return undef;
131             }
132              
133             # Preloaded methods go here.
134              
135             1;
136             __END__
137             # Below is stub documentation for your module. You'd better edit it!
138              
139             =head1 NAME
140              
141             Bank::Holidays - Perl extension for determining Federal Reserve holidays. 2015 - 2019
142              
143             =head1 VERSION
144              
145             0.84
146              
147             =head1 SYNOPSIS
148              
149             use Bank::Holidays;
150              
151             # Using the date => reference you can specify any date you like.
152             my $bank = Bank::Holidays->new( date => DateTime->now ); # or any datetime object
153              
154             # Check yesterday to see if it was a holiday
155             print "Yesterday ";
156             $bank->is_holiday( Yesterday => 1 ) ? print "is " : print "is not";
157             print " a holiday";
158              
159             # Check to see if today is a holiday;
160             print "Today ";
161             $bank->is_holiday ? print "is" : print "is not";
162             print " a holiday\n";
163              
164             # Check to see if tomorrow is a holiday.
165             print "Tomorrow ";
166             $bank->is_holiday( Tomorrow => 1 ) ? print "is" : print "is not";
167             print " a holiday\n";
168              
169             =head1 EXPORTER
170              
171             As of version 0.82, no functions are exported, and EXPORTER has been removed. It's
172             unclear if exported functions worked, as both (`is_holiday' and `reserve_holidays')
173             required an existing Bank::Holidays object to have been created by the caller.
174              
175             =head1 DESCRIPTION
176              
177             Bank::Holidays reads a page from the Federal Reserve's website that contains
178             holidays until 2019. However should the FR's site change this code may not work.
179             This code is very useful for determining days that a valid banking transaction
180             can occur, remembering that Sunday is never a banking day.
181              
182             =head2 methods
183              
184             new( [ date => dt->object ] ) Defaults to today if undefines.
185              
186             is_holiday( [ Yesterday|Tomorrow => 1 ] ) To determine what day to check default is current date in date object.
187              
188             =head1 AUTHOR
189              
190             Tyler Hardison, E<lt>thardison@seraph-net.netE<gt>
191              
192             =head1 THANKS TO
193              
194             Alex White E<lt>wu@geekfarm.orgE<gt> - For providing a patch for 2010 changes to the fed's site.
195              
196             Robert Leap E<lt>robertleap@gmail.comE<gt> - For providing a patch for the 2012-2016 holday period.
197              
198             =head1 COPYRIGHT AND LICENSE
199              
200             Copyright (C) 2013 by Tyler Hardison
201              
202             This library is free software; you can redistribute it and/or modify
203             it under the same terms as Perl itself, either Perl version 5.8.3 or,
204             at your option, any later version of Perl 5 you may have available.
205              
206              
207             =cut