File Coverage

blib/lib/Date/HolidayParser/iCalendar.pm
Criterion Covered Total %
statement 75 76 98.6
branch 17 20 85.0
condition 7 15 46.6
subroutine 18 18 100.0
pod 2 13 15.3
total 119 142 83.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             # Date::HolidayParser
3             # A parser of ~/.holiday-style files.
4             # The format is based off of the holiday files found bundled
5             # with the plan program, not any official spec. This because no
6             # official spec could be found.
7             # Copyright (C) Eskild Hustvedt 2006, 2007, 2008, 2010
8             #
9             # This program is free software; you can redistribute it and/or modify it
10             # under the same terms as Perl itself. There is NO warranty;
11             # not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12             #
13             # This is the iCalendar component, which emulates a DP::iCalendar-like interface
14             # in order to make it easier to use for users familiar with iCalendar, and
15             # make it compatible with DP::iCalendar::Manager.
16              
17             package Date::HolidayParser::iCalendar;
18              
19 2     2   47857 use Any::Moose;
  2         95660  
  2         15  
20 2     2   2942 use Date::HolidayParser;
  2         10  
  2         83  
21 2     2   23 use constant { true => 1, false => undef };
  2         4  
  2         2588  
22              
23             our $VERSION = 0.41;
24              
25             extends 'Date::HolidayParser';
26              
27             has '_UID_List' => (
28             is => 'rw',
29             isa => 'HashRef',
30             default => sub { {} },
31             );
32             has '_iCal_cache' => (
33             is => 'rw',
34             isa => 'HashRef',
35             default => sub { {} },
36             );
37              
38             # -- Public methods --
39              
40             # Purpose: Get an iCalendar hash with holiday info matching the supplied UID
41             # Usage: get_info(UID);
42             sub get_info
43             {
44 72     72 1 110 my $self = shift;
45 72         95 my $UID = shift;
46 72 100       545 return($self->_UID_List->{$UID}) if $self->_UID_List->{$UID};
47 3         14 return(false);
48             }
49              
50             # Purpose: List events in said year, on said month and day
51             # Usage: obj->list_events(year?,month?,day?);
52             # year is required, others are optional.
53             #
54             # This is the primary API for this module. It does only wrap the other
55             # methods, but provides a cleaner interface for new code.
56             sub list_events
57             {
58 24     24 1 8114 my ($self,$Year,$Month,$Day) = @_;
59 24 50       79 if(not defined $Year)
60             {
61 0         0 croak('Requried parameter "Year" not supplied');
62             }
63 24 100       64 if(defined $Day)
    100          
64             {
65 9         26 return $self->get_timeinfo($Year,$Month,$Day,'DAY');
66             }
67             elsif(defined $Month)
68             {
69 12         35 return $self->get_monthinfo($Year,$Month);
70             }
71             else
72             {
73 3         10 return $self->get_months($Year);
74             }
75             }
76              
77             # Purpose: Get information for the supplied month (list of days there are events)
78             # Usage: my $TimeRef = $object->get_monthinfo(YEAR,MONTH,DAY);
79             sub get_monthinfo
80             {
81 24     24 0 8803 my($self, $Year, $Month) = @_; # TODO: verify that they are set
82 24         80 $self->get($Year);
83 24         33 my @Array;
84 24 50 33     184 if(defined($self->_iCal_cache->{$Year}) and defined($self->_iCal_cache->{$Year}{$Month})){
85 24         33 @Array = sort keys(%{$self->_iCal_cache->{$Year}{$Month}});
  24         160  
86             }
87 24         144 return(\@Array);
88             }
89              
90             # Purpose: Get information for the supplied date (list of times in the day there are events)
91             # Usage: my $TimeRef = $object->get_dateinfo(YEAR,MONTH,DAY);
92             sub get_dateinfo
93             {
94 6     6 0 1358 my($self, $Year, $Month, $Day) = @_; # TODO: verify that they are set
95 6         21 $self->get($Year);
96 6         9 my @Array;
97 6 100 33     80 if(defined($self->_iCal_cache->{$Year}) and defined($self->_iCal_cache->{$Year}{$Month}) and defined($self->_iCal_cache->{$Year}{$Month}{$Day})) {
      66        
98 3         6 @Array = sort keys(%{$self->_iCal_cache->{$Year}{$Month}{$Day}});
  3         20  
99             }
100 6         34 return(\@Array);
101             }
102              
103             # Purpose: Return an empty array, unsupported.
104             # Usage: my $UIDRef = $object->get_timeinfo(YEAR,MONTH,DAY,TIME);
105             sub get_timeinfo
106             {
107 18     18 0 35 my($self, $Year, $Month, $Day,$Time) = @_;
108              
109 18 50       43 return(undef) if not $Time eq 'DAY';
110              
111 18         58 $self->get($Year);
112              
113 18 100 33     209 if( defined($self->_iCal_cache->{$Year}) and
      66        
114             defined($self->_iCal_cache->{$Year}{$Month}) and
115             defined($self->_iCal_cache->{$Year}{$Month}{$Day})
116             )
117             {
118 12         89 return($self->_iCal_cache->{$Year}{$Month}{$Day}{$Time});
119             }
120 6         27 return([]);
121             }
122              
123             # Purpose: Get a list of months which have events (those with *only* recurring not counted)
124             # Usage: my $ArrayRef = $object->get_months(YEAR);
125             sub get_months
126             {
127 6     6 0 12 my ($self, $Year) = @_;
128 6         33 $self->get($Year);
129 6         7 my @Array = sort keys(%{$self->_iCal_cache->{$Year}});
  6         54  
130 6         43 return(\@Array);
131             }
132              
133             # Purpose: Check if there is an holiday event with the supplied UID
134             # Usage: $bool = $object->exists($UID);
135             sub exists
136             {
137 36     36 0 47068 my $self = shift;
138 36         62 my $UID = shift;
139 36 100       284 return(true) if defined($self->_UID_List->{$UID});
140 3         12 return(false);
141             }
142              
143             # -- Unsupported or dummy methods, here for compatibility --
144              
145             # Purpose: Return an empty array, unsupported.
146             # Usage: my $ArrayRef = $object->get_years();
147             sub get_years
148             {
149 3     3 0 4293 return([]);
150             }
151              
152             # -- DP::iCalendar compatibility code --
153              
154             # Used by DP::iCalendar::Manager to set the prodid in output iCalendar files.
155             # We can't output iCalendar files, so we just ignore calls to it.
156 3     3 0 6 sub set_prodid { }
157              
158             # Purpose: Return manager information
159             # Usage: get_manager_version();
160             sub get_manager_version
161             {
162 6     6 0 2214 my $self = shift;
163 6         29 return('01_capable');
164             }
165              
166             # Purpose: Return manager capability information
167             # Usage: get_manager_capabilities
168             sub get_manager_capabilities
169             {
170             # All capabilites as of 01_capable
171 6     6 0 35 return(['LIST_DPI',])
172             }
173              
174              
175             # -- Private methods --
176              
177             # Purpose: Wraps _addParsedEvent in Date::HolidayParser so that an iCalendar version
178             # is also created at the same time.
179             around '_addParsedEvent' => sub
180             {
181             my $orig = shift;
182             my $self = shift;
183              
184             my($FinalParsing,$final_mon,$final_mday,$HolidayName,$holidayType,$FinalYDay,$PosixYear) = @_;
185              
186             my $UID = $self->_event_to_iCalendar($FinalYDay,$PosixYear,$HolidayName);
187             my $Year = $PosixYear+1900;
188              
189             if(not $self->_iCal_cache->{$Year}->{$final_mon}{$final_mday}{'DAY'})
190             {
191             $self->_iCal_cache->{$Year}->{$final_mon}{$final_mday}{'DAY'} = [];
192             }
193             push(@{$self->_iCal_cache->{$Year}->{$final_mon}{$final_mday}{'DAY'}}, $UID);
194              
195             return $self->$orig(@_);
196             };
197              
198             # Purpose: Generate an iCalendar entry
199             # Usage: this->_event_to_iCalendar(UNIXTIME, NAME);
200             sub _event_to_iCalendar
201             {
202 44     44   56 my $self = shift;
203 44         56 my $FinalYDay = shift;
204 44         57 my $PosixYear = shift;
205 44         54 my $name = shift;
206 44         252 $name =~ s/\s/-/g;
207              
208 44         492 my $unixtime = POSIX::mktime(0, 0, 0, $FinalYDay, 0, $PosixYear);
209              
210             # Generate the UID of the event, this is simply a
211 44         132 my $sum = unpack('%32C*', $name);
212             # This should be unique enough for our needs.
213             # We don't want it to be random, because if someone copies the events to their
214             # own calendar, we want DP::iCalendar::Manager to fetch the information from
215             # the changed calendar, instead of from the HolidayParser object.
216 44         123 my $UID = 'D-HP-ICS-'.$FinalYDay.'-'.$PosixYear.'-'.$sum;
217            
218 44         85 $self->_UID_List->{$UID} = {
219             UID => $UID,
220             DTSTART => iCal_ConvertFromUnixTime($unixtime),
221             DTEND => iCal_ConvertFromUnixTime($unixtime+86390), # Yes, this is purposefully not 86400
222             SUMMARY => $name,
223             };
224 44         138 return($UID);
225             }
226              
227             # The following three functions are originally from DP::iCalendar
228              
229             # Purpose: Generate an iCalendar date-time from multiple values
230             # Usage: my $iCalDateTime = iCal_GenDateTime(YEAR, MONTH, DAY, TIME);
231             sub iCal_GenDateTime {
232             # NOTE: This version ignores $Time because it isn't used in HolidayParser
233 88     88 0 152 my ($Year, $Month, $Day, $Time) = @_;
234             # Fix the month and day
235 88         150 my $iCalMonth = _PrefixZero($Month);
236 88         157 my $iCalDay = _PrefixZero($Day);
237 88         552 return("$Year$iCalMonth$iCalDay");
238             }
239              
240             # Purpose: Generate an iCalendar date-time string from a UNIX time string
241             # Usage: my $iCalDateTime = iCal_ConvertFromUnixTime(UNIX TIME);
242             sub iCal_ConvertFromUnixTime {
243 88     88 0 115 my $UnixTime = shift;
244 88         605 my ($realsec,$realmin,$realhour,$realmday,$realmonth,$realyear,$realwday,$realyday,$realisdst) = localtime($UnixTime);
245 88         155 $realyear += 1900; # Fix the year
246 88         98 $realmonth++; # Fix the month
247             # Return data from iCal_GenDateTime
248 88         397 return(iCal_GenDateTime($realyear,$realmonth,$realmday,"$realhour:$realmin"));
249             }
250              
251             # Purpose: Prefix a "0" to a number if it is only one digit.
252             # Usage: my $NewNumber = PrefixZero(NUMBER);
253             sub _PrefixZero {
254 176 100   176   559 if ($_[0] =~ /^\d$/) {
255 88         221 return("0$_[0]");
256             }
257 88         180 return($_[0]);
258             }
259              
260             # End of Date::HolidayParser::iCalendar
261             1;
262              
263             __END__