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   2360 use strict;
  3         14  
  3         73  
4 3     3   12 use warnings;
  3         4  
  3         93  
5              
6 3         354 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   1760 };
  3         166695  
21 3     3   1471 use Time::Local;
  3         5795  
  3         3718  
22              
23             our $ERROR;
24             our $VERSION = '1.905';
25              
26             sub new {
27 2563     2563 1 1307098 my ( $class, $date, %arg ) = @_;
28 2563         4409 my $self = {};
29 2563         3772 $ERROR = '';
30 2563         4016 bless($self, $class);
31 2563 100       8075 $self->set_date($date) if defined($date);
32 2563         8594 $self->set_accented( $arg{accented} );
33 2563         7182 $self->set_traditional( $arg{traditional} );
34 2563         6300 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 268 my ( $class, %arg ) = @_;
52 1         3 my $accented = delete $arg{accented};
53 1         3 my $traditional = delete $arg{traditional};
54 1         3 my $self = $class->new()->set_shire( %arg );
55 1         3 $self->set_accented( $accented );
56 1         2 $self->set_traditional( $traditional );
57 1         4 return $self;
58             }
59              
60             sub set_date {
61 2562     2562 1 3486 my ( $self, $date ) = @_;
62 2562         3014 $ERROR = '';
63              
64 2562 50       4092 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         3750 my $ref = ref $date;
70              
71 2562 100       6653 if ( __PACKAGE__ eq $ref ) {
    50          
72              
73             # Shallow clone
74 1         3 %{ $self } = %{ $date };
  1         4  
  1         3  
75              
76             } elsif ( ! $ref ) {
77              
78             # TODO this will throw warnings if the date is not a number.
79 2561         37923 my ( $greg_year, $greg_day_of_year ) = ( localtime $date )[5,7];
80              
81 2561         12029 my $greg_rata_die = __year_day_to_rata_die(
82             $greg_year + 1900,
83             $greg_day_of_year + 1,
84             );
85              
86 2561         108207 $self->set_rata_die( $greg_rata_die );
87              
88             } else {
89 0         0 $ERROR .= 'The date you gave is invalid';
90             }
91 2562         3330 return $self;
92             }
93              
94             sub set_rata_die {
95 2561     2561 1 3757 my ( $self, $greg_rata_die ) = @_;
96              
97 2561         3341 my $shire_rata_die = $greg_rata_die + GREGORIAN_RATA_DIE_TO_SHIRE;
98              
99 2561         4738 my ( $shire_year, $shire_day_of_year ) = __rata_die_to_year_day(
100             $shire_rata_die );
101              
102 2561         148893 my ( $shire_month, $shire_day ) = __day_of_year_to_date(
103             $shire_year,
104             $shire_day_of_year,
105             );
106              
107 2561         155164 $self->{year} = $shire_year;
108 2561         3626 $self->{month} = $shire_month;
109 2561 100       4335 if ( $shire_month ) {
110 2523         3739 $self->{holiday} = 0;
111 2523         3531 $self->{monthday} = $shire_day;
112             } else {
113 38         63 $self->{holiday} = $shire_day;
114 38         66 $self->{monthday} = 0;
115             }
116 2561         4778 $self->{weekday} = __day_of_week( $shire_month, $shire_day );
117              
118 2561         74440 return $self;
119             }
120              
121             {
122             my %legal = map { $_ => 1 } qw{ year month day holiday };
123              
124             sub set_shire {
125 1     1 1 4 my ( $self, %arg ) = @_;
126              
127 1         4 foreach my $key ( keys %arg ) {
128 3 50       8 $legal{$key}
129             or return _error_out( $self,
130             "No such argument as '$key'" );
131 3 50       12 $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       4 or return _error_out( $self, 'Year must be specified' );
138              
139 1 50       3 if ( $arg{month} ) {
140             $arg{holiday}
141 1 50       3 and return _error_out( $self,
142             'Month and holiday must not both be specified' );
143 1 50       3 if ( $arg{month} ) {
144 1         2 $arg{holiday} = 0;
145 1   50     6 $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         2 $ERROR = '';
156             $arg{weekday} = __day_of_week( $arg{month}, $arg{day} ||
157 1   33     5 $arg{holiday} );
158 1         58 $arg{monthday} = delete $arg{day};
159 1         5 %{ $self } = %arg;
  1         5  
160 1         3 return $self;
161             }
162             }
163              
164             sub set_accented {
165 2564     2564 1 5914 my ( $self, $value ) = @_;
166 2564         3993 $self->{accented} = $value;
167 2564         3355 return $self;
168             }
169              
170             sub set_traditional {
171 2564     2564 1 4279 my ( $self, $value ) = @_;
172 2564         3817 $self->{traditional} = $value;
173 2564         2896 return $self;
174             }
175              
176             sub time_in_seconds {
177 2561     2561 1 72133 my ( $self ) = @_;
178              
179 2561 50       3385 $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     6905 );
187              
188             my $shire_rata_die = __year_day_to_rata_die(
189             $self->{year},
190 2561         150241 $shire_day_of_year,
191             );
192              
193 2561         80638 my $greg_rata_die = $shire_rata_die - GREGORIAN_RATA_DIE_TO_SHIRE;
194              
195 2561         4464 my ( $greg_year, $greg_day_of_year ) = __rata_die_to_year_day(
196             $greg_rata_die );
197              
198 2561         146213 my @monthlen = ( 31, 28 + __is_leap_year( $greg_year ),
199             31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
200              
201 2561         46451 my $greg_day = $greg_day_of_year;
202 2561         5202 for ( my $greg_month = 0; $greg_month < @monthlen; $greg_month++ ) {
203 16726 100       25811 $greg_day <= $monthlen[$greg_month]
204             and return timelocal(
205             0, 0, 0, $greg_day, $greg_month, $greg_year );
206 14165         20082 $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       3 $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       3 $self->_has_date()
239             or return 0;
240              
241 1         6 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         4 return __month_name( $self->{month} );
251             }
252              
253             sub day {
254 1     1 1 4 my ( $self ) = @_;
255              
256 1 50       2 $self->_has_date()
257             or return 0;
258              
259 1         5 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 122928 my ( $self ) = @_;
273              
274 2562 50       3959 $self->_has_date()
275             or return 0;
276              
277 2562         4593 return $self->{year};
278             }
279              
280             use overload
281 3         24 '<=>' => \&_space_ship,
282             'cmp' => \&_space_ship,
283             '""' => \&as_string,
284 3     3   1965 ;
  3         1646  
285              
286             #All the other operators come automatically once this one is defined
287              
288             sub _space_ship {
289 2     2   290 my ($date1, $date2) = @_;
290 2         7 my $time1 = $date1->time_in_seconds();
291 2 50       113 $ERROR .= " on left operand" if $ERROR;
292 2         6 my $time2 = $date2->time_in_seconds();
293 2 50       105 $ERROR .= " on right operand" if $ERROR;
294 2         32 return $time1 <=> $time2;
295             } #end sub _space_ship
296              
297 4     4 1 18 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 10092 splice @_, 1, $#_, '%Ex';
303 2557         6646 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 433 splice @_, 1, $#_, '%Ex%n%En%Ed';
310 1         3 goto &strftime;
311             }
312              
313             sub strftime {
314 2561     2561 1 4651 my ( $self, @fmt ) = @_;
315              
316 2561 50       4469 $self->_has_date()
317             or return 0;
318              
319             return wantarray ?
320 2561 50       6449 ( map { __format( $self, $_ ) } @fmt ) :
  0         0  
321             __format( $self, $fmt[0] );
322             }
323              
324 2552     2552 1 243794 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   455766 my ( $self ) = @_;
332              
333 7688 50       10843 $self->_has_date()
334             or return 0;
335              
336 7688         14643 return $self->{month};
337             }
338              
339             sub __fmt_shire_day {
340 5126     5126   163280 my ( $self ) = @_;
341              
342 5126 50       7878 $self->_has_date()
343             or return 0;
344              
345 5126   66     13456 return $self->{monthday} || $self->{holiday};
346             }
347              
348             sub __fmt_shire_day_of_week {
349 2553     2553   6879 my ( $self ) = @_;
350              
351 2553 50       3697 $self->_has_date()
352             or return 0;
353              
354 2553         5551 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   29256 my ( $self ) = @_;
379 23055 50       29416 if ( grep { ! defined $self->{$_} }
  115275         183899  
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         28683 $ERROR = '';
385 23055         43543 return 1;
386             }
387             }
388              
389             1;
390              
391             __END__