File Coverage

blib/lib/Date/Tolkien/Shire.pm
Criterion Covered Total %
statement 132 162 81.4
branch 30 56 53.5
condition 6 13 46.1
subroutine 28 36 77.7
pod 22 22 100.0
total 218 289 75.4


line stmt bran cond sub pod time code
1             package Date::Tolkien::Shire;
2              
3 3     3   2774 use strict;
  3         18  
  3         87  
4 3     3   16 use warnings;
  3         6  
  3         110  
5              
6 3         429 use Date::Tolkien::Shire::Data qw{
7             __date_to_day_of_year
8             __day_of_week
9             __day_of_year_to_date
10             __format
11             __holiday_name
12             __is_leap_year
13             __month_name
14             __on_date
15             __rata_die_to_year_day
16             __trad_weekday_name
17             __weekday_name
18             __year_day_to_rata_die
19             GREGORIAN_RATA_DIE_TO_SHIRE
20 3     3   2112 };
  3         202846  
21 3     3   1725 use Time::Local;
  3         6911  
  3         4514  
22              
23             our $ERROR;
24             our $VERSION = '1.904';
25              
26             sub new {
27 2563     2563 1 1606780 my ( $class, $date, %arg ) = @_;
28 2563         5188 my $self = {};
29 2563         4801 $ERROR = '';
30 2563         4481 bless($self, $class);
31 2563 100       10381 $self->set_date($date) if defined($date);
32 2563         10387 $self->set_accented( $arg{accented} );
33 2563         9133 $self->set_traditional( $arg{traditional} );
34 2563         8284 return $self;
35             }
36              
37             sub error {
38 0     0 1 0 return $ERROR;
39             }
40              
41             sub today {
42 0     0 1 0 my ( $class, %arg ) = @_;
43             # TODO If I ever do time-of-day support, this will have to change.
44 0         0 my $self = $class->new( time );
45 0         0 $self->set_accented( $arg{accented} );
46 0         0 $self->set_traditional( $arg{traditional} );
47 0         0 return $self;
48             }
49              
50             sub from_shire {
51 1     1 1 338 my ( $class, %arg ) = @_;
52 1         4 my $accented = delete $arg{accented};
53 1         4 my $traditional = delete $arg{traditional};
54 1         4 my $self = $class->new()->set_shire( %arg );
55 1         4 $self->set_accented( $accented );
56 1         3 $self->set_traditional( $traditional );
57 1         4 return $self;
58             }
59              
60             sub set_date {
61 2562     2562 1 4695 my ( $self, $date ) = @_;
62 2562         3900 $ERROR = '';
63              
64 2562 50       4968 if ( ! defined $date ) {
65 0         0 $ERROR = 'You must pass in a date to set me equal to';
66 0         0 return $self;
67             }
68              
69 2562         4258 my $ref = ref $date;
70              
71 2562 100       7400 if ( __PACKAGE__ eq $ref ) {
    50          
72              
73             # Shallow clone
74 1         3 %{ $self } = %{ $date };
  1         5  
  1         5  
75              
76             } elsif ( ! $ref ) {
77              
78             # TODO this will throw warnings if the date is not a number.
79 2561         44484 my ( $greg_year, $greg_day_of_year ) = ( localtime $date )[5,7];
80              
81 2561         13645 my $greg_rata_die = __year_day_to_rata_die(
82             $greg_year + 1900,
83             $greg_day_of_year + 1,
84             );
85              
86 2561         128011 $self->set_rata_die( $greg_rata_die );
87              
88             } else {
89 0         0 $ERROR .= 'The date you gave is invalid';
90             }
91 2562         4359 return $self;
92             }
93              
94             sub set_rata_die {
95 2561     2561 1 4840 my ( $self, $greg_rata_die ) = @_;
96              
97 2561         4049 my $shire_rata_die = $greg_rata_die + GREGORIAN_RATA_DIE_TO_SHIRE;
98              
99 2561         6388 my ( $shire_year, $shire_day_of_year ) = __rata_die_to_year_day(
100             $shire_rata_die );
101              
102 2561         183307 my ( $shire_month, $shire_day ) = __day_of_year_to_date(
103             $shire_year,
104             $shire_day_of_year,
105             );
106              
107 2561         191615 $self->{year} = $shire_year;
108 2561         4632 $self->{month} = $shire_month;
109 2561 100       5633 if ( $shire_month ) {
110 2523         4594 $self->{holiday} = 0;
111 2523         4398 $self->{monthday} = $shire_day;
112             } else {
113 38         70 $self->{holiday} = $shire_day;
114 38         90 $self->{monthday} = 0;
115             }
116 2561         6337 $self->{weekday} = __day_of_week( $shire_month, $shire_day );
117              
118 2561         90262 return $self;
119             }
120              
121             {
122             my %legal = map { $_ => 1 } qw{ year month day holiday };
123              
124             sub set_shire {
125 1     1 1 5 my ( $self, %arg ) = @_;
126              
127 1         4 foreach my $key ( keys %arg ) {
128 3 50       9 $legal{$key}
129             or return _error_out( $self,
130             "No such argument as '$key'" );
131 3 50       17 $arg{$key} =~ m/ \A [0-9]+ \z /smx
132             or return _error_out( $self,
133             "Argument '$key' must be an unsigned integer" );
134             }
135              
136             defined $arg{year}
137 1 50       5 or return _error_out( $self, 'Year must be specified' );
138              
139 1 50       4 if ( $arg{month} ) {
140             $arg{holiday}
141 1 50       4 and return _error_out( $self,
142             'Month and holiday must not both be specified' );
143 1 50       6 if ( $arg{month} ) {
144 1         3 $arg{holiday} = 0;
145 1   50     18 $arg{day} ||= 1;
146             } else {
147 0         0 $arg{holiday} = $arg{day};
148 0         0 $arg{day} = 0;
149             }
150             } else {
151 0   0     0 $arg{holiday} ||= 1;
152 0         0 $arg{month} = $arg{day} = 0;
153             }
154              
155 1         5 $ERROR = '';
156             $arg{weekday} = __day_of_week( $arg{month}, $arg{day} ||
157 1   33     8 $arg{holiday} );
158 1         61 $arg{monthday} = delete $arg{day};
159 1         7 %{ $self } = %arg;
  1         5  
160 1         4 return $self;
161             }
162             }
163              
164             sub set_accented {
165 2564     2564 1 7708 my ( $self, $value ) = @_;
166 2564         5351 $self->{accented} = $value;
167 2564         3977 return $self;
168             }
169              
170             sub set_traditional {
171 2564     2564 1 5290 my ( $self, $value ) = @_;
172 2564         4854 $self->{traditional} = $value;
173 2564         3689 return $self;
174             }
175              
176             sub time_in_seconds {
177 2561     2561 1 90687 my ( $self ) = @_;
178              
179 2561 50       4580 $self->_has_date()
180             or return 0;
181              
182             my $shire_day_of_year = __date_to_day_of_year(
183             $self->{year},
184             $self->{month},
185             $self->{monthday} || $self->{holiday},
186 2561   66     9033 );
187              
188             my $shire_rata_die = __year_day_to_rata_die(
189             $self->{year},
190 2561         184321 $shire_day_of_year,
191             );
192              
193 2561         98012 my $greg_rata_die = $shire_rata_die - GREGORIAN_RATA_DIE_TO_SHIRE;
194              
195 2561         5752 my ( $greg_year, $greg_day_of_year ) = __rata_die_to_year_day(
196             $greg_rata_die );
197              
198 2561         181025 my @monthlen = ( 31, 28 + __is_leap_year( $greg_year ),
199             31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
200              
201 2561         57358 my $greg_day = $greg_day_of_year;
202 2561         6880 for ( my $greg_month = 0; $greg_month < @monthlen; $greg_month++ ) {
203 16726 100       31805 $greg_day <= $monthlen[$greg_month]
204             and return timelocal(
205             0, 0, 0, $greg_day, $greg_month, $greg_year );
206 14165         25149 $greg_day -= $monthlen[$greg_month];
207             }
208              
209 0         0 $ERROR = "Programming error: computed day $greg_day_of_year in Gregorian year $greg_year";
210 0         0 return 0;
211             }
212              
213             # TODO if I do time of day, epoch() will return it, whereas
214             # time_in_seconds will not.
215             *epoch = \&time_in_seconds; # sub epoch;
216              
217             sub weekday {
218 1     1 1 3 my ( $self ) = @_;
219              
220 1 50       4 $self->_has_date()
221             or return 0;
222              
223 1         5 return __weekday_name( $self->{weekday} );
224             }
225              
226             sub weekday_number {
227 0     0 1 0 my ( $self ) = @_;
228              
229 0 0       0 $self->_has_date()
230             or return 0;
231              
232 0         0 return $self->{weekday};
233             }
234              
235             sub trad_weekday {
236 1     1 1 3 my ( $self ) = @_;
237              
238 1 50       4 $self->_has_date()
239             or return 0;
240              
241 1         7 return __trad_weekday_name( $self->{weekday} );
242             }
243              
244             sub month {
245 1     1 1 3 my ( $self ) = @_;
246              
247 1 50       4 $self->_has_date()
248             or return 0;
249              
250 1         6 return __month_name( $self->{month} );
251             }
252              
253             sub day {
254 1     1 1 3 my ( $self ) = @_;
255              
256 1 50       4 $self->_has_date()
257             or return 0;
258              
259 1         10 return $self->{monthday};
260             }
261              
262             sub holiday {
263 0     0 1 0 my ( $self ) = @_;
264              
265 0 0       0 $self->_has_date()
266             or return 0;
267              
268 0         0 return __holiday_name( $self->{holiday} );
269             }
270              
271             sub year {
272 2562     2562 1 151185 my ( $self ) = @_;
273              
274 2562 50       5367 $self->_has_date()
275             or return 0;
276              
277 2562         5796 return $self->{year};
278             }
279              
280             use overload
281 3         28 '<=>' => \&_space_ship,
282             'cmp' => \&_space_ship,
283             '""' => \&as_string,
284 3     3   2462 ;
  3         1945  
285              
286             #All the other operators come automatically once this one is defined
287              
288             sub _space_ship {
289 2     2   350 my ($date1, $date2) = @_;
290 2         7 my $time1 = $date1->time_in_seconds();
291 2 50       149 $ERROR .= " on left operand" if $ERROR;
292 2         8 my $time2 = $date2->time_in_seconds();
293 2 50       122 $ERROR .= " on right operand" if $ERROR;
294 2         37 return $time1 <=> $time2;
295             } #end sub _space_ship
296              
297 4     4 1 23 sub accented { return $_[0]->{accented} }
298              
299             sub as_string {
300             # I can not just assign to $_[1] because it is an alias for the
301             # argument, thus the possibility of spooky action at a distance.
302 2557     2557 1 12315 splice @_, 1, $#_, '%Ex';
303 2557         8419 goto &strftime;
304             }
305              
306             sub on_date {
307             # I can not just assign to $_[1] because it is an alias for the
308             # argument, thus the possibility of spooky action at a distance.
309 1     1 1 533 splice @_, 1, $#_, '%Ex%n%En%Ed';
310 1         4 goto &strftime;
311             }
312              
313             sub strftime {
314 2561     2561 1 5695 my ( $self, @fmt ) = @_;
315              
316 2561 50       5879 $self->_has_date()
317             or return 0;
318              
319             return wantarray ?
320 2561 50       8393 ( map { __format( $self, $_ ) } @fmt ) :
  0         0  
321             __format( $self, $fmt[0] );
322             }
323              
324 2552     2552 1 304985 sub traditional { return $_[0]->{traditional} }
325              
326             # Date::Tolkien::Shire::Data::__format() date object interface
327              
328             *__fmt_shire_year = \&year; # sub __fmt_shire_year;
329              
330             sub __fmt_shire_month {
331 7688     7688   568044 my ( $self ) = @_;
332              
333 7688 50       14030 $self->_has_date()
334             or return 0;
335              
336 7688         18862 return $self->{month};
337             }
338              
339             sub __fmt_shire_day {
340 5126     5126   202457 my ( $self ) = @_;
341              
342 5126 50       9090 $self->_has_date()
343             or return 0;
344              
345 5126   66     16725 return $self->{monthday} || $self->{holiday};
346             }
347              
348             sub __fmt_shire_day_of_week {
349 2553     2553   8370 my ( $self ) = @_;
350              
351 2553 50       4639 $self->_has_date()
352             or return 0;
353              
354 2553         6944 return $self->{weekday};
355             }
356              
357             # sub __fmt_shire_hour; sub __fmt_shire_minute; sub __fmt_shire_second;
358             # sub __fmt_shire_nanosecond;
359             *__fmt_shire_hour = *__fmt_shire_minute = *__fmt_shire_second =
360 0     0   0 *__fmt_shire_nanosecond = sub { 0 };
361              
362             # The interface definition requires this to return undef, since the zone
363             # is undefined. See Date::Tolkien::Shire::Data.
364 0     0   0 sub __fmt_shire_zone_offset { return undef } ## no critic (ProhibitExplicitReturnUndef)
365 0     0   0 sub __fmt_shire_zone_name { return '' }
366              
367             *__fmt_shire_epoch = \&epoch; # sub __fmt_shire_epoch;
368             *__fmt_shire_accented = \&accented; # sub __fmt_shire_accented;
369             *__fmt_shire_traditional = \&traditional; # sub __fmt_shire_traditional;
370              
371             sub _error_out {
372 0     0   0 my ( $return, @msg ) = @_;
373 0         0 $ERROR = join ' ', @msg;
374 0         0 return $return;
375             }
376              
377             sub _has_date {
378 23055     23055   33233 my ( $self ) = @_;
379 23055 50       37527 if ( grep { ! defined $self->{$_} }
  115275         228025  
380             qw{ holiday month monthday weekday year } ) {
381 0         0 $ERROR = 'You must set a date first';
382 0         0 return 0;
383             } else {
384 23055         35806 $ERROR = '';
385 23055         51662 return 1;
386             }
387             }
388              
389             1;
390              
391             __END__