File Coverage

blib/lib/DateTime/Format/Roman.pm
Criterion Covered Total %
statement 66 66 100.0
branch 30 32 93.7
condition 9 15 60.0
subroutine 10 10 100.0
pod 2 3 66.6
total 117 126 92.8


line stmt bran cond sub pod time code
1             #
2             # Perl extension to display date with the Roman style
3             # Copyright (C) 2003, 2004, 2018, 2019 Eugene van der Pijll, Dave Rolsky and Jean Forget
4             #
5             # See the license in the embedded documentation below.
6             #
7             package DateTime::Format::Roman;
8              
9 4     4   2084604 use strict;
  4         32  
  4         131  
10 4     4   30 use warnings;
  4         10  
  4         218  
11              
12             our $VERSION = 0.04;
13              
14 4     4   1080 use DateTime 0.22;
  4         485837  
  4         127  
15              
16 4     4   1986 use Roman;
  4         3059  
  4         301  
17 4     4   2020 use Params::Validate qw/validate SCALAR ARRAYREF/;
  4         11370  
  4         4135  
18              
19             sub new {
20 3     3 1 1253 my $class = shift;
21 3         87 my %p = validate( @_,
22             { pattern => {type => SCALAR | ARRAYREF,
23             default => '%Od %2f %B %Oy' },
24             } );
25              
26 3 100       29 $p{pattern} = [$p{pattern}] unless ref $p{pattern};
27              
28 3         10 my $self = bless \%p, $class;
29 3         14 return $self;
30             }
31              
32             my @fixed_days_names = (
33             { Kal => 'Kal' , Non => 'Non' , Id => 'Id' },
34             { Kal => 'K' , Non => 'N' , Id => 'Id' },
35             { Kal => 'Kalends', Non => 'Nones', Id => 'Ides' },
36             );
37              
38             my %dt_elem;
39             my %formats;
40             %formats =
41             ( 'b' => sub { (shift->locale->month_format_abbreviated)->[$dt_elem{month}-1] },
42             'B' => sub { (shift->locale->month_format_wide) ->[$dt_elem{month}-1] },
43             'd' => sub { $dt_elem{day} },
44             'D' => sub { ($dt_elem{day} ne 1 && $dt_elem{day}.' ') . $formats{f}->(@_) },
45             'f' => sub { $fixed_days_names[$_[1]||0]{$dt_elem{fixed_day}} },
46             'm' => sub { $dt_elem{month} },
47             'y' => sub { $dt_elem{year} },
48             );
49              
50             my $default_formatter;
51              
52             sub format_datetime {
53 20     20 1 17924 my ($self, $dt) = @_;
54              
55 20 100       61 unless (ref $self) {
56             # Called as a class method
57 4   66     16 $default_formatter ||= $self->new();
58 4         7 $self = $default_formatter;
59             }
60              
61 20         57 %dt_elem = DateTime::Format::Roman->date_elements($dt);
62              
63 20         41 my @return;
64 20         31 for (@{$self->{pattern}}) {
  20         61  
65 56         75 my $pat = $_;
66 56         261 $pat =~ s/%([Oo]?)(\d*)([a-zA-Z])/
67             $formats{$3} ?
68 104 50       339 _romanize($formats{$3}->($dt, $2),$1)
69             : "$1$2$3" /ge;
70 56 100       162 return $pat unless wantarray;
71 48         115 push @return, $pat;
72             }
73 12         43 return @return;
74             }
75              
76             sub _romanize {
77 104     104   352 my ($str, $extra) = @_;
78 104 100       267 if ($extra eq 'O') {
    100          
79 16 100       62 $str =~ s/(\d+)(\w?)/Roman($1) . ($2?" $2":'')/ge;
  15         48  
80             } elsif ($extra eq 'o') {
81 4 100       17 $str =~ s/(\d+)(\w?)/roman($1) . ($2?" $2":'')/ge;
  4         12  
82             }
83 104         1143 return $str;
84             }
85              
86             sub date_elements {
87 20     20 0 51 my ($self, $dt) = @_;
88              
89 20         55 my ($d, $m, $y) = ($dt->day, $dt->month, $dt->year);
90 20         281 my $nones = _nones($m);
91 20         32 my $ides = $nones + 8;
92              
93 20         38 my %retval;
94              
95 20 100       77 if ($d == 1) {
    100          
    100          
96 3         22 @retval{'day', 'fixed_day'} = (1, 'Kal');
97             } elsif ($d <= $nones) {
98 5         16 @retval{'day', 'fixed_day'} = ($nones + 1 - $d, 'Non');
99             } elsif ($d <= $ides) {
100 4         17 @retval{'day', 'fixed_day'} = ($ides + 1 - $d, 'Id');
101             } else {
102 8         40 my $days_in_month = (ref $dt)->last_day_of_month(
103             year => $y, month => $m )->day;
104 8         2445 my $day = $days_in_month + 2 - $d;
105              
106             # In leap years, 6 Kal March is doubled (24&25 Feb)
107 8 100 66     30 if ($dt->is_leap_year && $m == 2) {
108 5 100       73 if ($day > 7) {
    100          
109 1         3 $day --;
110             } elsif ($day == 7) {
111 3         9 $day = '6bis';
112             }
113             }
114 8         48 @retval{'day', 'fixed_day'} = ($day, 'Kal');
115 8         14 $m++;
116 8 100       22 if ($m > 12) {
117 1         5 $m -= 12;
118 1         2 $y++;
119             }
120             }
121              
122 20         49 @retval{'month', 'year'} = ($m, $y);
123 20         99 return %retval;
124             }
125              
126             sub _nones {
127 20     20   35 my $m = shift;
128 20 50 66     109 return 7 if $m == 3 or $m == 5 or $m == 7 or $m == 10;
      66        
      33        
129 6         16 return 5;
130             }
131              
132             # Instead of using a boring "1" ending value:
133             'Ils sont fous, ces romains !';
134              
135             __END__
136              
137             =head1 NAME
138              
139             DateTime::Format::Roman - Roman day numbering for DateTime objects
140              
141             =head1 SYNOPSIS
142              
143             use DateTime::Format::Roman;
144              
145             my $formatter = DateTime::Format::Roman->new(
146             pattern => '%d %f %b %y' );
147              
148             my $dt = DateTime->new( year => 2003, month => 5, day => 28 );
149              
150             $formatter->format_datetime($dt);
151             # '5 Kal Jun 2003'
152              
153             =head1 DESCRIPTION
154              
155             This module formats dates in the Roman style.
156              
157             The Romans expressed their dates in relation to three fixed dates per
158             month. For example: the Ides of March was the 15th of that month; 14
159             March was called "2 Ides", 13 March was called "3 Ides", etcetera. The
160             days in the second half of the month were named after the first day of
161             the next month, the "Kalends"; e.g. 16 March was called "17 Kalends of
162             April".
163              
164             =head1 METHODS
165              
166             =over 4
167              
168             =item * new( pattern => $string )
169              
170             Creates a new formatter object. The optional formatting pattern defines
171             the format of the output of format_datetime(). If no formatting pattern
172             is given, a reasonable default is used.
173              
174             =item * format_datetime($datetime)
175              
176             Retruns the formatted string. This method can be called on a formatter
177             object (created by new()), or it can be called as a class method. In the
178             latter case, the default pattern is used.
179              
180             =back
181              
182             =head2 PATTERN SPECIFIERS
183              
184             The following specifiers are allowed in the format strings given to the
185             new() method:
186              
187             =over 4
188              
189             =item * %b
190              
191             The abbreviated month name.
192              
193             =item * %B
194              
195             The full month name.
196              
197             =item * %d
198              
199             The day of the month as a decimal number (including '1' for the fixed
200             days).
201              
202             =item * %D
203              
204             The day of the month, written as a number plus the corresponding fixed
205             day.
206              
207             =item * %f
208              
209             The 'fixed day' part of the date.
210              
211             =item * %m
212              
213             The month as a decimal number (range 1 to 12).
214              
215             =item * %y
216              
217             The year as a decimal number.
218              
219             =back
220              
221             If a specifier is preceded by 'O' or 'o', numbers will be written in
222             uppercase and lowercase Roman numerals, respectively.
223              
224             The %f specifier accepts an additional argument of 1 digit, specifying
225             the length of the output:
226              
227             %0f : abbreviated name (e.g. "Kal")
228             %1f : full name (e.g. "Kalends")
229             %2f : one-letter abbreviation (e.g. "K")
230              
231             =head1 SUPPORT
232              
233             Support for this module is provided via the datetime@perl.org email
234             list. See L<https://lists.perl.org/> for more details.
235              
236             Note that this is a beta release. The interface *will* change,
237             especially the format specifiers, and the way the "fixed days" are
238             returned.
239              
240             =head1 AUTHOR
241              
242             First author: Eugene van der Pijll <pijll@gmx.net>
243              
244             First co-maintainer: Dave Rolsky <autarch@urth.org>
245              
246             Second co-maintainer: Jean Forget <JFORGET@cpan.org>
247              
248             =head1 COPYRIGHT
249              
250             Copyright (c) 2003, 2004, 2018, 2019 Eugene van der Pijll, Dave Rolsky
251             and Jean Forget. All rights reserved. This program is free software;
252             you can redistribute it and/or modify it under the same terms as Perl
253             itself.
254              
255             This program is distributed under the same terms as Perl 5.28.0: GNU
256             Public License version 1 or later and Perl Artistic License
257              
258             You can find the text of the licenses in the F<LICENSE> file or at
259             L<https://dev.perl.org/licenses/artistic.html> and
260             L<https://www.gnu.org/licenses/gpl-1.0.html>.
261              
262             Here is the summary of GPL:
263              
264             This program is free software; you can redistribute it and/or modify
265             it under the terms of the GNU General Public License as published by
266             the Free Software Foundation; either version 1, or (at your option)
267             any later version.
268              
269             This program is distributed in the hope that it will be useful, but
270             WITHOUT ANY WARRANTY; without even the implied warranty of
271             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
272             General Public License for more details.
273              
274             You should have received a copy of the GNU General Public License
275             along with this program; if not, see <https://www.gnu.org/licenses/>
276             or write to the Free Software Foundation, Inc., L<https://fsf.org>.
277              
278             =head1 SEE ALSO
279              
280             L<DateTime>
281              
282             datetime@perl.org mailing list
283              
284             =cut