File Coverage

blib/lib/DateTime/Calendar/Mayan.pm
Criterion Covered Total %
statement 217 221 98.1
branch 60 64 93.7
condition 15 23 65.2
subroutine 38 40 95.0
pod 23 23 100.0
total 353 371 95.1


line stmt bran cond sub pod time code
1             package DateTime::Calendar::Mayan;
2              
3 10     10   2352385 use strict;
  10         28  
  10         573  
4              
5 10     10   63 use vars qw( $VERSION );
  10         19  
  10         819  
6             $VERSION = '0.0601';
7              
8 10     10   10941 use DateTime;
  10         973381  
  10         328  
9 10     10   80 use Params::Validate qw( validate SCALAR OBJECT );
  10         19  
  10         968  
10              
11 10     10   58 use constant MAYAN_EPOCH => -1137142;
  10         25  
  10         873  
12 10     10   58 use constant MAYAN_HAAB_EPOCH => MAYAN_EPOCH - 348;
  10         24  
  10         552  
13 10         733 use constant MAYAN_HAAB_MONTH => qw( Pop Uo Zip Zotz Tzec Xul Yaxkin Mol Chen
14 10     10   47 Yax Zac Ceh Mac Kankin Muan Pax Kayab Cumku Uayeb );
  10         17  
15 10     10   48 use constant MAYAN_TZOLKIN_EPOCH => MAYAN_EPOCH - 159;
  10         18  
  10         592  
16 10         33639 use constant MAYAN_TZOLKIN_NAME => qw( Imix Ik Akbal Kan Chicchan Cimi Manik
17 10     10   54 Lamat Muluc Oc Chuen Eb Ben Ix Men Cib Caban Etznab Cauac Ahau );
  10         19  
18              
19             sub new {
20 34     34 1 31421 my( $class ) = shift;
21              
22 34         1260 my %args = validate( @_,
23             {
24             baktun => { type => SCALAR, default => 0 },
25             katun => { type => SCALAR, default => 0 },
26             tun => { type => SCALAR, default => 0 },
27             uinal => { type => SCALAR, default => 0 },
28             kin => { type => SCALAR, default => 0 },
29             epoch => {
30             type => OBJECT,
31             can => 'utc_rd_values',
32             optional => 1,
33             },
34             }
35             );
36              
37 34   33     584 $class = ref( $class ) || $class;
38              
39 34         50 my $alt_epoch;
40 34 100       111 if ( exists $args{ epoch } ) {
41 4         7 my $object = $args{ epoch };
42 4         9 delete $args{ epoch };
43 4 50       27 $object = $object->clone->set_time_zone( 'floating' )
44             if $object->can( 'set_time_zone' );
45              
46 4         106 $alt_epoch = ( $object->utc_rd_values )[ 0 ];
47             }
48              
49 34   100     278 my $self = {
50             epoch => $alt_epoch || MAYAN_EPOCH,
51             rd_secs => 0,
52             rd_nanos => 0,
53             };
54              
55 34         110 $self->{ rd } = _long_count2rd( $self, \%args );
56              
57 34         328 return( bless( $self, $class ) );
58             }
59              
60             sub now {
61 3     3 1 15872 my( $class ) = shift;
62              
63 3   33     45 $class = ref( $class ) || $class;
64              
65 3         28 my $dt = DateTime->now;
66 3         1396 my $dtcm = $class->from_object( object => $dt );
67              
68 3         21 return( $dtcm );
69             }
70              
71             sub today {
72 1     1 1 3 my( $class ) = shift;
73              
74 1   33     5 $class = ref( $class ) || $class;
75              
76 1         5 my $dt = DateTime->today;
77 1         500 my $dtcm = $class->from_object( object => $dt );
78              
79 1         7 return( $dtcm );
80             }
81              
82             # lifted from DateTime
83 8     8 1 15 sub clone { bless { %{ $_[0] } }, ref $_[0] }
  8         51  
84              
85             sub _long_count2rd {
86 95     95   148 my( $self, $lc ) = @_;
87              
88             my $rd = $self->{ epoch }
89             + $lc->{ baktun } * 144000
90             + $lc->{ katun } * 7200
91             + $lc->{ tun } * 360
92             + $lc->{ uinal } * 20
93 95         337 + $lc->{ kin };
94              
95 95         236 return( $rd );
96             }
97              
98             sub _rd2long_count {
99 100     100   146 my( $self ) = shift;
100              
101 100         126 my %lc;
102 100         216 my $long_count = $self->{ rd } - $self->{ epoch };
103 100         464 $lc{ baktun } = _floor( $long_count / 144000 );
104 100         236 my $day_baktun = $long_count % 144000;
105 100         272 $lc{ katun } = _floor( $day_baktun / 7200 );
106 100         144 my $day_katun = $day_baktun % 7200;
107 100         205 $lc{ tun } = _floor( $day_katun / 360 );
108 100         149 my $day_tun = $day_katun % 360;
109 100         194 $lc{ uinal } = _floor( $day_tun / 20 );
110 100         208 $lc{ kin } = _floor( $day_tun % 20 );
111              
112 100         251 return( \%lc );
113             }
114              
115             sub _rd2haab {
116 21     21   25 my( $self ) = shift;
117              
118 21         24 my %haab;
119 21         43 my $count = ( $self->{ rd } - MAYAN_HAAB_EPOCH ) % 365;
120 21         46 $haab{ day } = $count % 20;
121 21         49 $haab{ month } = _floor( $count / 20 ) + 1;
122              
123 21         48 return( \%haab );
124             }
125              
126             sub _haab2rd {
127 0     0   0 my( $month, $day ) = @_;
128            
129 0         0 return( ( $month - 1 ) * 20 + $day );
130             }
131              
132             sub _rd2tzolkin {
133 22     22   33 my( $self ) = shift;
134              
135 22         23 my %tzolkin;
136 22         48 my $count = $self->{ rd } - MAYAN_TZOLKIN_EPOCH + 1;
137 22         57 $tzolkin{ number } = _amod( $count, 13 );
138 22         43 $tzolkin{ name } = _amod( $count, 20 );
139              
140 22         43 return( \%tzolkin );
141             }
142              
143             sub _tzolkin2rd {
144 0     0   0 my( $number, $name ) = shift;
145              
146 0         0 return( ( $number - 1 + 39 x ( $number - $name ) ) % 260 );
147             }
148              
149             sub from_object {
150 22     22 1 19514 my( $class ) = shift;
151 22         465 my %args = validate( @_,
152             {
153             object => {
154             type => OBJECT,
155             can => 'utc_rd_values',
156             },
157             },
158             );
159              
160 22   66     946 $class = ref( $class ) || $class;
161              
162 22         48 my $object = $args{ object };
163 22 100       163 $object = $object->clone->set_time_zone( 'floating' )
164             if $object->can( 'set_time_zone' );
165              
166 22         2119 my( $rd, $rd_secs, $rd_nanos ) = $object->utc_rd_values();
167              
168 22 100       384 my $dtcm_epoch = $object->mayan_epoch
169             if $object->can( 'mayan_epoch' );
170              
171             my $self = {
172             rd => $rd,
173             rd_secs => $rd_secs,
174             rd_nanos => $rd_nanos || 0,
175 22   100     240 epoch => $dtcm_epoch->{ rd } || MAYAN_EPOCH,
      100        
176             };
177              
178 22         2638 return( bless( $self, $class ) );
179             }
180              
181             sub utc_rd_values {
182 28     28 1 868 my( $self ) = shift;
183              
184             # days utc, seconds utc,
185 28   100     194 return( $self->{ rd }, $self->{ rd_secs }, $self->{ rd_nanos } || 0 );
186             }
187              
188             sub from_epoch {
189 2     2 1 784 my( $class ) = shift;
190 2         36 my %args = validate( @_,
191             {
192             epoch => { type => SCALAR },
193             }
194             );
195              
196 2   66     18 $class = ref( $class ) || $class;
197              
198 2         11 my $dt = DateTime->from_epoch( epoch => $args{ epoch } );
199              
200 2         398 my $self = $class->from_object( object => $dt );
201              
202 2         13 return( $self );
203             }
204              
205             sub epoch {
206 2     2 1 10 my( $self ) = shift;
207              
208 2         22 my $dt = DateTime->from_object( object => $self );
209              
210 2         912 return( $dt->epoch );
211             }
212              
213             sub set_mayan_epoch {
214 5     5 1 196 my( $self ) = shift;
215              
216 5         76 my %args = validate( @_,
217             {
218             object => {
219             type => OBJECT,
220             can => 'utc_rd_values',
221             },
222             },
223             );
224              
225 5         145 my $object = $args{ object };
226 5 100       32 $object = $object->clone->set_time_zone( 'floating' )
227             if $object->can( 'set_time_zone' );
228              
229             # this can not handle rd values larger then a Mayan year
230             # $self->{ rd } = _long_count2rd( $self, _rd2long_count( $self ) );
231              
232 5         118 $self->{ epoch } = ( $object->utc_rd_values )[ 0 ];
233 5 100       57 if ( $self->{ epoch } > MAYAN_EPOCH ) {
234 4         11 $self->{ rd } += abs( $self->{ epoch } - MAYAN_EPOCH );
235             } else {
236 1         3 $self->{ rd } -= abs( $self->{ epoch } - MAYAN_EPOCH );
237             }
238              
239 5         27 return( $self );
240             }
241              
242             sub mayan_epoch {
243 6     6 1 22 my( $self ) = shift;
244              
245 6         15 my $new_self = $self->clone();
246              
247 6         15 $new_self->{ rd } = $self->{ epoch };
248 6         11 $new_self->{ rd_secs } = 0;
249 6         9 $new_self->{ epoch } = MAYAN_EPOCH;
250              
251             # calling from_object causes a method loop
252              
253 6         10 my $class = ref( $self );
254 6         13 my $dtcm = bless( $new_self, $class );
255              
256 6         19 return( $dtcm );
257             }
258              
259             sub set {
260 3     3 1 17 my( $self ) = shift;
261              
262 3         202 my %args = validate( @_,
263             {
264             baktun => { type => SCALAR, optional => 1 },
265             katun => { type => SCALAR, optional => 1 },
266             tun => { type => SCALAR, optional => 1 },
267             uinal => { type => SCALAR, optional => 1 },
268             kin => { type => SCALAR, optional => 1 },
269             }
270             );
271              
272 3         29 my $lc = _rd2long_count( $self );
273              
274 3 50       17 $lc->{ baktun } = $args{ baktun } if defined $args{ baktun };
275 3 100       13 $lc->{ katun } = $args{ katun } if defined $args{ katun };
276 3 100       13 $lc->{ tun } = $args{ tun } if defined $args{ tun };
277 3 100       13 $lc->{ uinal } = $args{ uinal } if defined $args{ uinal };
278 3 100       12 $lc->{ kin } = $args{ kin } if defined $args{ kin };
279              
280 3         10 $self->{ rd } = _long_count2rd( $self, $lc );
281              
282 3         20 return( $self );
283             }
284              
285             sub add {
286 42     42 1 95 my( $self ) = shift;
287              
288 42         1233 my %args = validate( @_,
289             {
290             baktun => { type => SCALAR, optional => 1 },
291             katun => { type => SCALAR, optional => 1 },
292             tun => { type => SCALAR, optional => 1 },
293             uinal => { type => SCALAR, optional => 1 },
294             kin => { type => SCALAR, optional => 1 },
295             }
296             );
297              
298 42         307 my $lc = _rd2long_count( $self );
299              
300 42 100       116 $lc->{ baktun } += $args{ baktun } if defined $args{ baktun };
301 42 100       98 $lc->{ katun } += $args{ katun } if defined $args{ katun };
302 42 100       349 $lc->{ tun } += $args{ tun } if defined $args{ tun };
303 42 100       103 $lc->{ uinal } += $args{ uinal } if defined $args{ uinal };
304 42 100       122 $lc->{ kin } += $args{ kin } if defined $args{ kin };
305              
306 42         92 $self->{ rd } = _long_count2rd( $self, $lc );
307            
308 42         193 return( $self );
309             }
310              
311             sub subtract {
312 5     5 1 24 my( $self ) = shift;
313              
314 5         152 my %args = validate( @_,
315             {
316             baktun => { type => SCALAR, optional => 1 },
317             katun => { type => SCALAR, optional => 1 },
318             tun => { type => SCALAR, optional => 1 },
319             uinal => { type => SCALAR, optional => 1 },
320             kin => { type => SCALAR, optional => 1 },
321             }
322             );
323              
324 5         45 my $lc = _rd2long_count( $self );
325              
326 5 100       26 $lc->{ baktun } -= $args{ baktun } if defined $args{ baktun };
327 5 100       64 $lc->{ katun } -= $args{ katun } if defined $args{ katun };
328 5 100       20 $lc->{ tun } -= $args{ tun } if defined $args{ tun };
329 5 100       19 $lc->{ uinal } -= $args{ uinal } if defined $args{ uinal };
330 5 100       23 $lc->{ kin } -= $args{ kin } if defined $args{ kin };
331              
332 5         16 $self->{ rd } = _long_count2rd( $self, $lc );
333              
334 5         26 return( $self );
335             }
336              
337             sub add_duration {
338 3     3 1 307 my( $self, $duration ) = @_;
339              
340 3         19 my $dt = DateTime->from_object( object => $self );
341 3         1488 $dt->add_duration( $duration );
342              
343 3         1691 my $new_self = $self->from_object( object => $dt );
344              
345             # if there is an alternate epoch defined don't touch it
346 3         11 $self->{ rd } = $new_self->{ rd };
347 3         10 $self->{ rd_secs } = $new_self->{ rd_secs };
348              
349 3         19 return( $self );
350             }
351              
352             sub subtract_duration {
353 3     3 1 154 my( $self, $duration ) = @_;
354              
355 3         18 my $dt = DateTime->from_object( object => $self );
356 3         1122 $dt->subtract_duration( $duration );
357              
358 3         1735 my $new_self = $self->from_object( object => $dt );
359              
360             # if there is an alternate epoch defined don't touch it
361 3         11 $self->{ rd } = $new_self->{ rd };
362 3         7 $self->{ rd_secs } = $new_self->{ rd_secs };
363              
364 3         34 return( $self );
365             }
366              
367             sub baktun {
368 5     5 1 22 my( $self, $arg ) = @_;
369              
370 5         13 my $lc = _rd2long_count( $self );
371              
372 5 100       19 if ( defined $arg ) {
373 2         5 $lc->{ baktun } = $arg;
374 2         5 $self->{ rd } = _long_count2rd( $self, $lc );
375              
376 2         12 return( $self );
377             }
378              
379             # conversion from Date::Maya
380             # set baktun to [1-13]
381 3         5 $lc->{ baktun } %= 13;
382 3 100       12 $lc->{ baktun } = 13 if $lc->{ baktun } == 0;
383              
384 3         18 return( $lc->{ baktun } );
385             }
386              
387             *set_baktun = \&baktun;
388              
389             sub katun {
390 5     5 1 12 my( $self, $arg ) = @_;
391              
392 5         11 my $lc = _rd2long_count( $self );
393              
394 5 100       17 if ( defined $arg ) {
395 2         5 $lc->{ katun } = $arg;
396 2         12 $self->{ rd } = _long_count2rd( $self, $lc );
397              
398 2         10 return( $self );
399             }
400              
401 3         15 return( $lc->{ katun } );
402             }
403              
404             *set_katun= \&katun;
405              
406             sub tun {
407 5     5 1 12 my( $self, $arg ) = @_;
408              
409 5         13 my $lc = _rd2long_count( $self );
410              
411 5 100       31 if ( defined $arg ) {
412 2         4 $lc->{ tun } = $arg;
413 2         5 $self->{ rd } = _long_count2rd( $self, $lc );
414              
415 2         9 return( $self );
416             }
417              
418 3         18 return( $lc->{ tun } );
419             }
420              
421             *set_tun= \&tun;
422              
423             sub uinal {
424 5     5 1 11 my( $self, $arg ) = @_;
425              
426 5         12 my $lc = _rd2long_count( $self );
427              
428 5 100       19 if ( defined $arg ) {
429 2         4 $lc->{ uinal } = $arg;
430 2         5 $self->{ rd } = _long_count2rd( $self, $lc );
431              
432 2         9 return( $self );
433             }
434              
435 3         14 return( $lc->{ uinal } );
436             }
437              
438             *set_uinal= \&uinal;
439              
440             sub kin {
441 6     6 1 17 my( $self, $arg ) = @_;
442              
443 6         19 my $lc = _rd2long_count( $self );
444              
445 6 100       22 if ( defined $arg ) {
446 3         8 $lc->{ kin } = $arg;
447 3         16 $self->{ rd } = _long_count2rd( $self, $lc );
448              
449 3         10 return( $self );
450             }
451              
452 3         16 return( $lc->{ kin } );
453             }
454              
455             *set_kin= \&kin;
456              
457             sub bktuk {
458 24     24 1 669 my( $self, $sep ) = @_;
459 24 100       79 $sep = '.' unless defined $sep;
460              
461 24         61 my $lc = _rd2long_count( $self );
462              
463 24         146 $lc->{ baktun } %= 13;
464 24 100       72 $lc->{ baktun } = 13 if $lc->{ baktun } == 0;
465              
466             return(
467             $lc->{ baktun } . $sep .
468             $lc->{ katun } . $sep .
469             $lc->{ tun } . $sep .
470             $lc->{ uinal } . $sep .
471             $lc->{ kin }
472 24         228 );
473             }
474              
475             *date = \&bktuk;
476             *long_count = \&bktuk;
477              
478             sub haab {
479 21     21 1 41 my( $self, $sep ) = @_;
480 21 50       49 $sep = ' ' unless defined $sep;
481              
482 21         42 my $haab = _rd2haab( $self );
483              
484 21         157 return( $haab->{ day } . $sep . (MAYAN_HAAB_MONTH)[ $haab->{ month } - 1 ] );
485             }
486              
487             sub tzolkin {
488 22     22 1 43 my( $self, $sep ) = @_;
489 22 50       57 $sep = ' ' unless defined $sep;
490              
491 22         45 my $tzolkin = _rd2tzolkin( $self );
492              
493 22         349 return( $tzolkin->{ number } . $sep . (MAYAN_TZOLKIN_NAME )[ $tzolkin->{ name } - 1 ] );
494             }
495              
496             # lifted from DateTime::Calendar::Julian;
497             sub _floor {
498 521     521   12973 my $x = shift;
499 521         689 my $ix = int $x;
500 521 100       911 if ($ix <= $x) {
501 483         1007 return $ix;
502             } else {
503 38         104 return $ix - 1;
504             }
505             }
506              
507             sub _amod {
508 44     44   53 my( $x, $y ) = @_;
509              
510 44         104 return( $y + $x % ( -$y ) );
511             }
512              
513             1;
514              
515             __END__