File Coverage

blib/lib/DateTime/Format/EraLegis.pm
Criterion Covered Total %
statement 146 157 92.9
branch 46 78 58.9
condition 12 24 50.0
subroutine 37 40 92.5
pod n/a
total 241 299 80.6


line stmt bran cond sub pod time code
1             package DateTime::Format::EraLegis;
2             $DateTime::Format::EraLegis::VERSION = '0.009';
3             # ABSTRACT: DateTime formatter for Era Legis (http://oto-usa.org/calendar.html)
4              
5 1     1   786865 use 5.010;
  1         4  
6 1     1   472 use Any::Moose;
  1         32275  
  1         5  
7 1     1   1337 use Method::Signatures;
  1         31485  
  1         7  
8              
9             has 'ephem' => (
10             is => 'ro',
11             isa => 'DateTime::Format::EraLegis::Ephem',
12             lazy_build => 1,
13             );
14              
15             has 'style' => (
16             is => 'ro',
17             isa => 'DateTime::Format::EraLegis::Style',
18             lazy_build => 1,
19             );
20              
21 1 0   1   1472 method _build_ephem {
  0     0   0  
  0         0  
22 0         0 return DateTime::Format::EraLegis::Ephem::DBI->new;
23             }
24              
25 1 0   1   754 method _build_style {
  0     0   0  
  0         0  
26 0         0 return DateTime::Format::EraLegis::Style->new;
27             }
28              
29              
30 1 50 66 1   113055 method format_datetime(DateTime $dt, Str $format = 'plain') {
  13 50 66 13   18354  
  13 100       41  
  13 50       25  
  13 50       116  
  13         848  
  13         69  
  13         65  
31 13         42 $dt = $dt->clone;
32              
33             ### Day of week should match existing time zone
34 13         186 my $dow = $dt->day_of_week;
35              
36             ### But pull ephemeris data based on UTC
37 13         79 $dt->set_time_zone('UTC');
38              
39 13         2690 my %tdate = (
40             evdate => $dt->ymd . ' ' . $dt->hms,
41             dow => $dow,
42             );
43              
44 13         350 for ( qw(sol luna) ) {
45 26         110 my $deg = $self->ephem->lookup( $_, $dt );
46 26         114 $tdate{$_}{sign} = int($deg / 30);
47 26         80 $tdate{$_}{deg} = int($deg % 30);
48             }
49              
50             my $years = $dt->year -
51 13 100 100     45 (($dt->month <= 3 && $tdate{sol}{sign} > 0) ? 1905 : 1904);
52 13         190 $tdate{year} = [ int( $years/22 ), int( $years%22 ) ];
53              
54 13         58 $tdate{plain} = $self->style->express( \%tdate );
55              
56 13 100       108 return ($format eq 'raw') ? \%tdate : $tdate{plain};
57             }
58              
59              
60             __PACKAGE__->meta->make_immutable;
61 1     1   483 no Any::Moose;
  1         3  
  1         13  
62              
63             ######################################################
64             package DateTime::Format::EraLegis::Ephem;
65             $DateTime::Format::EraLegis::Ephem::VERSION = '0.009';
66 1     1   341 use Any::Moose qw(Role);
  1         12  
  1         6  
67              
68             requires 'lookup';
69              
70 1     1   2363 no Any::Moose;
  1         2  
  1         5  
71              
72             ######################################################
73             package DateTime::Format::EraLegis::Ephem::DBI;
74             $DateTime::Format::EraLegis::Ephem::DBI::VERSION = '0.009';
75 1     1   292 use 5.010;
  1         3  
76 1     1   6 use Any::Moose;
  1         2  
  1         5  
77 1     1   655 use Carp;
  1         3  
  1         77  
78 1     1   7 use DBI;
  1         2  
  1         52  
79 1     1   6 use Method::Signatures;
  1         3  
  1         9  
80              
81             with 'DateTime::Format::EraLegis::Ephem';
82              
83             has 'ephem_db' => (
84             is => 'ro',
85             isa => 'Str',
86             lazy_build => 1,
87             );
88              
89             has 'dbh' => (
90             is => 'ro',
91             isa => 'DBI::db',
92             lazy_build => 1,
93             );
94              
95 1 0   1   1178 method _build_ephem_db {
  0     0   0  
  0         0  
96             return $ENV{ERALEGIS_EPHEMDB}
97 0   0     0 // croak 'No ephemeris database defined';
98             }
99              
100 1 50   1   727 method _build_dbh {
  1     1   3  
  1         5  
101 1         15 return DBI->connect( 'dbi:SQLite:dbname='.$self->ephem_db );
102             }
103              
104 1 50 33 1   3949 method lookup(Str $body, DateTime $dt) {
  26 50 33 26   49  
  26 50       70  
  26 50       44  
  26 50       182  
  26         69  
  26         53  
  26         130  
  26         64  
105 26         74 my $time = $dt->ymd . ' ' . $dt->hms;
106 26 50       525 croak 'Date is before era legis' if $time lt '1904-03-20';
107 26         160 my $rows = $self->dbh->selectcol_arrayref(
108             q{SELECT degree FROM ephem
109             WHERE body = ? AND time < ?
110             ORDER BY time DESC LIMIT 1},
111             undef, $body, $time );
112 26 50       7822 croak "Cannot find date entry for $time." unless $rows;
113              
114 26         83 return $rows->[0];
115             }
116              
117             __PACKAGE__->meta->make_immutable;
118 1     1   311 no Any::Moose;
  1         3  
  1         6  
119              
120             ######################################################
121              
122             package DateTime::Format::EraLegis::Style;
123             $DateTime::Format::EraLegis::Style::VERSION = '0.009';
124 1     1   280 use 5.010;
  1         3  
125 1     1   4 use Any::Moose;
  1         3  
  1         3  
126 1     1   597 use utf8;
  1         2  
  1         8  
127 1     1   495 use Roman::Unicode qw(to_roman);
  1         34860  
  1         68  
128 1     1   8 use Method::Signatures;
  1         3  
  1         9  
129              
130             has 'lang' => (
131             is => 'ro',
132             isa => 'Str',
133             default => 'latin',
134             required => 1,
135             );
136              
137             has 'dow' => (
138             is => 'ro',
139             isa => 'ArrayRef',
140             lazy_build => 1,
141             );
142              
143             has 'signs' => (
144             is => 'ro',
145             isa => 'ArrayRef',
146             lazy_build => 1,
147             );
148              
149             has 'years' => (
150             is => 'ro',
151             isa => 'ArrayRef',
152             lazy_build => 1,
153             );
154              
155             has 'show_terse' => (
156             is => 'ro',
157             isa => 'Bool',
158             default => 0,
159             );
160              
161             has [ qw( show_deg show_dow show_year roman_year ) ] => (
162             is => 'ro',
163             isa => 'Bool',
164             default => 1,
165             );
166              
167             has 'template' => (
168             is => 'ro',
169             isa => 'Str',
170             lazy_build => 1,
171             );
172              
173 1 50   1   1307 method _build_dow {
  2     2   5  
  2         8  
174             return
175 2 50       27 ($self->lang eq 'symbol')
    100          
176             ? [qw( ☉︎ ☽︎ ♂︎ ☿︎ ♃︎ ♀︎ ♄︎ ☉︎ )]
177             : ($self->lang eq 'english')
178             ? [qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday)]
179             : [qw(Solis Lunae Martis Mercurii Iovis Veneris Saturni Solis)];
180             }
181              
182 1 50   1   759 method _build_signs {
  4     4   1737  
  4         15  
183 4 100       41 return [qw( ♈︎ ♉︎ ♊︎ ♋︎ ♌︎ ♍︎ ♎︎ ♏︎ ♐︎ ♑︎ ♒︎ ♓︎ )]
184             if $self->lang eq 'symbol';
185              
186 2 50       7 return [qw(Aries Taurus Gemini Cancer Leo Virgo Libra Scorpio Sagittarius Capricorn Aquarius Pisces)]
187             if $self->lang eq 'english';
188              
189 2 50       8 return [qw(Aries Taurus Gemini Cancer Leo Virgo Libra Scorpio Sagittarius Capricorn Aquarius Pisces)]
190             if $self->lang eq 'poor-latin';
191              
192 2 50 33     30 return [qw(Arietis Tauri Geminorum Cancri Leonis Virginis Librae Scorpii Sagittarii Capricorni Aquarii Piscis)]
193             if $self->lang eq 'latin' && $self->show_deg;
194              
195 0         0 return [qw(Ariete Tauro Geminis Cancro Leone Virginie Libra Scorpio Sagittario Capricorno Aquario Pisci)];
196             }
197              
198 1 50   1   806 method _build_years {
  3     3   7  
  3         9  
199             return
200             ($self->roman_year)
201 3 50       16 ? [ 0, map { to_roman($_) } 1..21 ]
  63         7127  
202             : [ 0..21 ];
203             }
204              
205 1 50   1   774 method _build_template {
  4     4   9  
  4         47  
206 4         7 my $template = '';
207 4 50       15 if ($self->show_deg) {
208 4         14 $template = '☉︎ in {sdeg}° {ssign} : ☽︎ in {ldeg}° {lsign}';
209             }
210             else {
211 0         0 $template = '☉︎ in {ssign} : ☽︎ in {lsign}';
212             }
213 4 100       18 if ($self->show_terse) {
214 2         18 $template =~ s/ in / /g;
215             }
216 4 100       16 if ($self->show_dow) {
217 2         6 $template .= ' : ';
218 2 100       13 $template .= ($self->lang eq 'latin')
219             ? 'dies '
220             : '';
221 2         5 $template .= '{dow}';
222             }
223 4 100       13 if ($self->show_year) {
224 3         9 $template .= ' : ';
225 3 50       20 $template .= ($self->lang eq 'symbol')
    100          
226             ? '{year1}{year2}'
227             : ($self->lang eq 'english')
228             ? 'Year {year1}.{year2} of the New Aeon'
229             : 'Anno {year1}{year2} æræ legis';
230             }
231              
232 4         22 return $template;
233             }
234              
235 1 50 66 1   3562 method express( HashRef $tdate ) {
  13 50   13   26  
  13 50       31  
  13         20  
  13         92  
  13         70  
236 13         59 my $datestr = $self->template;
237              
238 13         75 $datestr =~ s/{sdeg}/$tdate->{sol}{deg}/ge;
  13         66  
239 13         49 $datestr =~ s/{ssign}/$self->signs->[$tdate->{sol}{sign}]/ge;
  13         61  
240 13         50 $datestr =~ s/{ldeg}/$tdate->{luna}{deg}/ge;
  13         41  
241 13         38 $datestr =~ s/{lsign}/$self->signs->[$tdate->{luna}{sign}]/ge;
  13         75  
242 13         39 $datestr =~ s/{dow}/$self->dow->[$tdate->{dow}]/ge;
  2         15  
243 13         37 $datestr =~ s/{year1}/$self->years->[$tdate->{year}[0]]/ge;
  12         52  
244 13         429 $datestr =~ s/{year2}/lc($self->years->[$tdate->{year}[1]])/ge;
  12         85  
245              
246 13         5895 return $datestr;
247             }
248              
249             __PACKAGE__->meta->make_immutable;
250 1     1   476 no Any::Moose;
  1         3  
  1         9  
251             1;
252              
253             __END__
254              
255             =head1 NAME
256              
257             DateTime::Format::EraLegis - DateTime converter for Era Legis
258             DateTime::Format::EraLegis::Ephem - planetary ephemeris role
259             DateTime::Format::EraLegis::Ephem::DBI - default ephemeris getter
260             DateTime::Format::EraLegis::Style - customize output styles
261              
262             =head1 SYNOPSIS
263              
264             use DateTime::Format::EraLegis;
265              
266             my $ephem = DateTime::Format::EraLegis::Ephem::DBI->new(
267             ephem_db => 'db.sqlite3');
268             my $style = DateTime::Format::EraLegis::Style->new(
269             show_terse => 1, lang => 'symbol');
270             my $dtf = DateTime::Format::EraLegis->new(
271             ephem => $ephem, style => $style);
272              
273             my $dt->set_formatter($dtf);
274              
275             =head1 DESCRIPTION
276              
277             These three modules combined enable DateTime objects to emit date strings
278             formatted according to the Thelemic calendar. The ephemeris provides access
279             to the planetary location of the Sun and Moon keyed by UTC timestamp. The
280             style dictates the specific expression of the of datetime value using a
281             template into which one can place tokens which can be converted into the
282             sign/degree coordinates for the given date. A default style exists and is
283             permutable by boolean attributes.
284              
285             All three classes are built with Moose and behave accordingly. Method
286             arguments are typechecked and will die on failure. Defaults exist for
287             all attributes. All attributes are read-only and must be assigned at
288             the time of instantiation.
289              
290             =head1 ATTRIBUTES AND METHODS
291              
292             =over
293              
294             =item *
295              
296             DateTime::Format::EraLegis
297              
298             =over
299              
300             =item *
301              
302             ephem: DT::F::EL::Ephem object. Creates a new DBI one by default.
303              
304             =item *
305              
306             style: DT::F::EL::Style object. Creates a new one by default.
307              
308             =item *
309              
310             format_datetime(DateTime $dt, Str $format): Standard interface for a
311             DateTime::Format package. $format is one of 'plain' or 'raw'.
312             Defaults to 'plain'.
313              
314             =back
315              
316             =item *
317              
318             DateTime::Format::EraLegis::Ephem (Role)
319              
320             =over
321              
322             =item *
323              
324             lookup(Str $body, DateTime $dt): Required by any role consumer. $body
325             is one of "sol" or "luna". $dt is the date in question (in UTC!).
326             Returns the number of degrees away from 0 degrees Aries. Divide by
327             thirty to get the sign. Modulo by thirty to get the degrees of that
328             sign.
329              
330             =back
331              
332             =item *
333              
334             DateTime::Format::EraLegis::Ephem::DBI
335              
336             =over
337              
338             =item *
339              
340             Consumes DT::F::EL::Ephem role.
341              
342             =item *
343              
344             ephem_db: Filename of the sqlite3 ephemeris database. Defaults to the value
345             of $ENV{ERALEGIS_EPHEMDB}.
346              
347             =item *
348              
349             dbh: DBI handle for ephemeris database. Defaults to creating a new one pointing
350             to the ephem_db database.
351              
352             =back
353              
354             =item *
355              
356             DateTime::Format::EraLegis::Style
357              
358             =over
359              
360             =item *
361              
362             template: Assign a custom template value. Variables (enclosed in '{}')
363             include 'ssign' and 'sdeg' for Sol sign and degree, 'lsign' and 'ldeg'
364             for Luna sign and degree, 'dow' for day of the week, and 'year1' and
365             'year2' for the two docosades. Example:
366              
367             "Sol in {sdeg} degrees {ssign}, anno {year1}{year2} era legis"
368              
369             Interpolated values get assigned based on the setting of 'lang'.
370              
371             =item *
372              
373             lang: Set the output language, one of latin, english, symbol, poor-latin.
374             Defaults to 'latin'.
375              
376             =item *
377              
378             show_terse, show_deg, show_dow, show_year, roman_year: Flags to direct
379             the style to alter the default template.
380              
381             =back
382              
383             =back
384              
385             =head1 DATABASE SCHEMA
386              
387             The schema for the DBI ephemeris table is very simple and the querying
388             SQL very generic. Most DBI backends should work without issue, though
389             SQLite3 is the only one tested. The schema is:
390              
391             CREATE TABLE ephem (
392             body TEXT, -- one of 'sol' or 'luna'
393             time DATETIME, -- UTC timestamp of shift into degree
394             degree INTEGER NOT NULL, -- degrees from 0 degrees Aries
395             PRIMARY KEY (body, time)
396             );
397              
398             =head1 BUGS
399              
400             Please report bugs as issues at:
401             https://gitlab.com/clayfouts/datetime-format-eralegis
402              
403             =head1 AUTHOR
404              
405             Clay Fouts <cfouts@khephera.net>
406              
407             =head1 COPYRIGHT & LICENSE
408              
409             Copyright (c) 2012 Clay Fouts
410              
411             This is free software; you can redistribute it and/or modify it under
412             the same terms as the Perl 5 programming language system itself.
413              
414             =cut