File Coverage

blib/lib/DateTime/Format/Japanese.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             # $Id: /mirror/datetime/DateTime-Format-Japanese/trunk/lib/DateTime/Format/Japanese.pm 69499 2008-08-24T16:17:57.045540Z lestrrat $
2              
3             package DateTime::Format::Japanese;
4 4     4   188412 use strict;
  4         10  
  4         164  
5 4     4   22 use warnings;
  4         10  
  4         147  
6              
7 4     4   12797 use Params::Validate qw( validate validate_pos SCALAR BOOLEAN );
  4         127762  
  4         436  
8 4     4   4078 use Encode();
  4         64860  
  4         119  
9 4     4   37 use Exporter;
  4         8  
  4         246  
10 4     4   23 use vars qw(@ISA $VERSION %EXPORT_TAGS);
  4         7  
  4         329  
11 4     4   3147 use DateTime::Format::Japanese::Common qw(:constants);
  0            
  0            
12             use DateTime::Calendar::Japanese::Era;
13             BEGIN
14             {
15             $VERSION = '0.04000';
16             @ISA = qw(Exporter);
17             %EXPORT_TAGS = (
18             constants => [ qw(
19             FORMAT_KANJI_WITH_UNIT FORMAT_KANJI FORMAT_ZENKAKU
20             FORMAT_ROMAN FORMAT_ERA FORMAT_GREGORIAN) ]
21             );
22             Exporter::export_ok_tags('constants');
23             }
24              
25             # XXX - OBJECT DEFINITION
26              
27             my %NewValidate = (
28             output_encoding => { default => 'utf8' },
29             input_encoding => { default => 'utf8' },
30             number_format => {
31             type => SCALAR,
32             default => FORMAT_KANJI
33             },
34             year_format => {
35             type => SCALAR,
36             default => FORMAT_ERA
37             },
38             with_gregorian_marker => {
39             type => BOOLEAN,
40             default => 0
41             },
42             with_bc_marker => {
43             type => BOOLEAN,
44             default => 0
45             },
46             with_ampm_marker => {
47             type => BOOLEAN,
48             default => 0
49             },
50             with_day_of_week => {
51             type => BOOLEAN,
52             default => 0
53             }
54             );
55              
56             sub new
57             {
58             my $class = shift;
59             my %hash = validate(@_, \%NewValidate);
60             my $self = bless \%hash, $class;
61             }
62              
63             sub input_encoding
64             {
65             my $self = shift;
66             my $ret = $self->{input_encoding};
67             if (@_) {
68             $self->{input_encoding} = shift;
69             }
70             return $ret;
71             }
72              
73             sub output_encoding
74             {
75             my $self = shift;
76             my $ret = $self->{output_encoding};
77             if (@_) {
78             $self->{output_encoding} = shift;
79             }
80             return $ret;
81             }
82              
83             sub number_format
84             {
85             my $self = shift;
86             my $current = $self->{number_format};
87             if (@_) {
88             my($val) = validate_pos(@_, {
89             type => SCALAR,
90             callbacks => {
91             'is valid number_format' => \&DateTime::Format::Japanese::Common::_valid_number_format
92             }
93             });
94             $self->{number_format} = $val;
95             }
96             return $current;
97             }
98              
99             sub year_format
100             {
101             my $self = shift;
102             my $current = $self->{year_format};
103             if (@_) {
104             my($val) = validate_pos(@_, {
105             type => SCALAR,
106             callbacks => {
107             'is valid year_format' => \&DateTime::Format::Japanese::Common::_valid_year_format
108             }
109             });
110             $self->{year_format} = $val;
111             }
112             return $current;
113             }
114              
115             sub with_gregorian_marker
116             {
117             my $self = shift;
118             my $current = $self->{with_gregorian_marker};
119             if (@_) {
120             my($val) = validate_pos(@_, { type => BOOLEAN });
121             $self->{with_gregorian_marker} = $val;
122             }
123             return $current;
124             }
125              
126             sub with_bc_marker
127             {
128             my $self = shift;
129             my $current = $self->{with_bc_marker};
130             if (@_) {
131             my($val) = validate_pos(@_, { type => BOOLEAN });
132             $self->{with_bc_marker} = $val;
133             }
134             return $current;
135             }
136              
137             sub with_ampm_marker
138             {
139             my $self = shift;
140             my $current = $self->{with_ampm_marker};
141             if (@_) {
142             my($val) = validate_pos(@_, { type => BOOLEAN });
143             $self->{with_ampm_marker} = $val;
144             }
145             return $current;
146             }
147              
148             sub with_day_of_week
149             {
150             my $self = shift;
151             my $current = $self->{with_day_of_week};
152             if (@_) {
153             my($val) = validate_pos(@_, { type => BOOLEAN });
154             $self->{with_day_of_week} = $val;
155             }
156             return $current;
157             }
158              
159             # XXX - FORMATTING RELATED STUFF
160              
161             my @FmtBasicValidate = (
162             { isa => 'DateTime' },
163             );
164              
165             sub format_year
166             {
167             my $self = shift;
168             my ($dt) = validate_pos(@_, @FmtBasicValidate);
169              
170             my $year_section = '';
171              
172             if ($self->year_format eq DateTime::Format::Japanese::FORMAT_ERA()) {
173             $year_section =
174             DateTime::Format::Japanese::Common::_format_era($dt, $self->number_format);
175             } else {
176             my $year = $dt->year;
177             if ($year < 0 && $self->with_bc_marker) {
178             $year *= -1;
179             $year_section .= $DateTime::Format::Japanese::Common::BC_MARKER;
180             }
181              
182             if ($self->with_gregorian_marker) {
183             $year_section .= $DateTime::Format::Japanese::Common::GREGORIAN_MARKER;
184             }
185              
186             my $restore = undef;
187             if ($self->number_format eq FORMAT_KANJI_WITH_UNIT) {
188             $restore = $self->number_format(FORMAT_KANJI);
189             }
190              
191             $year_section .=
192             DateTime::Format::Japanese::Common::_format_number($year, $self->number_format);
193             $year_section .= $DateTime::Format::Japanese::Common::YEAR_MARKER;
194              
195             if ($restore) {
196             $self->number_format($restore);
197             }
198             }
199              
200             return Encode::encode($self->{output_encoding}, $year_section);
201             }
202              
203             sub format_month
204             {
205             my $self = shift;
206             my ($dt) = validate_pos(@_, @FmtBasicValidate);
207              
208             return Encode::encode($self->{output_encoding},
209             DateTime::Format::Japanese::Common::_format_common_with_marker(
210             $DateTime::Format::Japanese::Common::MONTH_MARKER,
211             $dt->month,
212             $self->number_format));
213             }
214              
215             sub format_day
216             {
217             my $self = shift;
218             my ($dt) = validate_pos(@_, @FmtBasicValidate);
219              
220             return Encode::encode($self->{output_encoding},
221             DateTime::Format::Japanese::Common::_format_common_with_marker(
222             $DateTime::Format::Japanese::Common::DAY_MARKER,
223             $dt->day,
224             $self->number_format));
225             }
226              
227             sub format_hour
228             {
229             my $self = shift;
230             my ($dt) = validate_pos(@_, @FmtBasicValidate);
231              
232             my $hour = $dt->hour;
233             my $ampm = '';
234              
235             if ($self->with_ampm_marker) {
236             $hour = $dt->hour <= 12 ? $dt->hour : $dt->hour - 12;
237             $ampm = $dt->hour < 12 ?
238             $DateTime::Format::Japanese::Common::AM_MARKER :
239             $DateTime::Format::Japanese::Common::PM_MARKER;
240             }
241              
242             return Encode::encode($self->{output_encoding},
243             $ampm .
244             DateTime::Format::Japanese::Common::_format_common_with_marker(
245             $DateTime::Format::Japanese::Common::HOUR_MARKER,
246             $hour,
247             $self->number_format));
248             }
249              
250             sub format_minute
251             {
252             my $self = shift;
253             my ($dt) = validate_pos(@_, @FmtBasicValidate);
254              
255             return Encode::encode($self->{output_encoding},
256             DateTime::Format::Japanese::Common::_format_common_with_marker(
257             $DateTime::Format::Japanese::Common::MINUTE_MARKER,
258             $dt->minute,
259             $self->number_format));
260             }
261              
262             sub format_second
263             {
264             my $self = shift;
265             my ($dt) = validate_pos(@_, @FmtBasicValidate);
266              
267             return Encode::encode($self->{output_encoding},
268             DateTime::Format::Japanese::Common::_format_common_with_marker(
269             $DateTime::Format::Japanese::Common::SECOND_MARKER,
270             $dt->second,
271             $self->number_format));
272             }
273              
274             sub format_ymd
275             {
276             my $self = shift;
277             my ($dt) = validate_pos(@_, @FmtBasicValidate);
278              
279             # format_year, format_month, format_day already takes care of
280             # encoding, so don't re-encode
281             return
282             $self->format_year($dt) .
283             $self->format_month($dt) .
284             $self->format_day($dt);
285             }
286              
287             sub format_time
288             {
289             my $self = shift;
290             my ($dt) = validate_pos(@_, @FmtBasicValidate);
291              
292             # format_hour, format_minute, format_second already takes care of
293             # encoding, so don't re-encode
294             return
295             $self->format_hour($dt) .
296             $self->format_minute($dt) .
297             $self->format_second($dt);
298             }
299              
300             sub format_day_of_week
301             {
302             my $self = shift;
303             my ($dt) = validate_pos(@_, @FmtBasicValidate);
304              
305             return Encode::encode($self->{output_encoding},
306             @DateTime::Format::Japanese::Common::DAY_OF_WEEKS[ $dt->day_of_week - 1 ] .
307             $DateTime::Format::Japanese::Common::DAY_OF_WEEK_MARKER);
308             }
309              
310             sub format_datetime
311             {
312             my $self = shift;
313             my ($dt) = validate_pos(@_, @FmtBasicValidate);
314              
315             my $rv = $self->format_ymd($dt) .
316             $self->format_time($dt);
317             if ($self->with_day_of_week) {
318             $rv .= $self->format_day_of_week($dt);
319             }
320              
321             # format_ymd, format_time, format_day_of_week have already
322             # fixed our encoding, so don't touch.
323             return $rv;
324             }
325              
326             # XXX - PARSING RELATED STUFF
327              
328             my $RE_MODERN_TIME_COMPONENTS = qr(
329             (?:
330             ($DateTime::Format::Japanese::Common::RE_AM_PM_MARKER)
331             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
332             $DateTime::Format::Japanese::Common::RE_HOUR_MARKER
333             (?:
334             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
335             $DateTime::Format::Japanese::Common::RE_MINUTE_MARKER
336             (?:
337             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
338             $DateTime::Format::Japanese::Common::RE_SECOND_MARKER
339             )?
340             )?
341             )?
342             )x;
343            
344              
345             my $parse_gregorian = {
346             regex => qr<
347             ^
348             $DateTime::Format::Japanese::Common::RE_GREGORIAN_MARKER?
349             ($DateTime::Format::Japanese::Common::RE_GREGORIAN_YEAR)
350             $DateTime::Format::Japanese::Common::RE_YEAR_MARKER
351             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
352             $DateTime::Format::Japanese::Common::RE_MONTH_MARKER
353             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
354             $DateTime::Format::Japanese::Common::RE_DAY_MARKER
355             $RE_MODERN_TIME_COMPONENTS
356             $DateTime::Format::Japanese::Common::RE_DAY_OF_WEEKS?
357             $
358             >x,
359             params => [ qw(year month day am_pm hour minute second) ],
360             preprocess => [
361             \&DateTime::Format::Japanese::Common::_normalize_utf8, ],
362             postprocess => [
363             \&DateTime::Format::Japanese::Common::_normalize_numbers,
364             \&DateTime::Format::Japanese::Common::_fix_am_pm,
365             \&_fix_year ]
366             };
367              
368             my $parse_gregorian_bc = {
369             regex => qr<
370             ^
371             ($DateTime::Format::Japanese::Common::RE_BC_MARKER|\-)
372             $DateTime::Format::Japanese::Common::RE_GREGORIAN_MARKER?
373             ($DateTime::Format::Japanese::Common::RE_GREGORIAN_YEAR)
374             $DateTime::Format::Japanese::Common::RE_YEAR_MARKER
375             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
376             $DateTime::Format::Japanese::Common::RE_MONTH_MARKER
377             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
378             $DateTime::Format::Japanese::Common::RE_DAY_MARKER
379             $RE_MODERN_TIME_COMPONENTS
380             $DateTime::Format::Japanese::Common::RE_DAY_OF_WEEKS?
381             $
382             >x,
383             params => [ qw(is_bc year month day am_pm hour minute second) ],
384             preprocess => [
385             \&DateTime::Format::Japanese::Common::_normalize_utf8, ],
386             postprocess => [
387             \&DateTime::Format::Japanese::Common::_normalize_numbers,
388             \&DateTime::Format::Japanese::Common::_fix_am_pm,
389             \&_fix_year ]
390             };
391              
392             my $parse_with_era = {
393             regex => qr|
394             ^
395             ($DateTime::Format::Japanese::Common::RE_ERA_NAME)
396             ($DateTime::Format::Japanese::Common::RE_ERA_YEAR)
397             $DateTime::Format::Japanese::Common::RE_YEAR_MARKER
398             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
399             $DateTime::Format::Japanese::Common::RE_MONTH_MARKER
400             ($DateTime::Format::Japanese::Common::RE_TWO_DIGITS)
401             $DateTime::Format::Japanese::Common::RE_DAY_MARKER
402             $RE_MODERN_TIME_COMPONENTS
403             $DateTime::Format::Japanese::Common::RE_DAY_OF_WEEKS?
404             $
405             |x,
406             params => [ qw(era_name era_year month day am_pm hour minute second) ],
407             preprocess => [
408             \&DateTime::Format::Japanese::Common::_normalize_utf8, ],
409             postprocess => [
410             \&DateTime::Format::Japanese::Common::_fix_era_year,
411             \&DateTime::Format::Japanese::Common::_normalize_numbers,
412             \&DateTime::Format::Japanese::Common::_fix_am_pm,
413             \&_era2year_modern ]
414             };
415              
416             sub _fix_year
417             {
418             my %args = @_;
419             if (delete $args{parsed}->{is_bc}) {
420             $args{parsed}->{year} *= -1;
421             }
422             1;
423             }
424              
425             sub _era2year_modern
426             {
427             my %args = @_;
428              
429             my $era_name = delete $args{parsed}->{era_name} ||
430             return 0;
431             my $era_year = delete $args{parsed}->{era_year};
432             if ($era_year <= 0) {
433             return 0;
434             }
435              
436             my $era = DateTime::Calendar::Japanese::Era->lookup_by_name(name => $era_name);
437              
438             my $g_year = $era->start->year + $era_year - 1;
439              
440             if ($g_year == 1) {
441             if ($era->start->month > $args{parsed}->{month}) {
442             Carp::croak("Invalid input format: Month " .
443             $era->id .
444             " is before the beginning of era '$era_name'");
445             } elsif ($era->start->day > $args{parsed}->{day}) {
446             Carp::croak("Invalid input format: Day " .
447             $era->id .
448             " is before the beginning of era '$era_name'");
449             }
450             }
451              
452             if ($era->end->is_finite() && $era->end->year < $g_year) {
453             Carp::croak("Invalid input format: Year $g_year is after the end of era " . $era->id);
454             } elsif ($g_year == $era->end->year) {
455             if ($era->start->month < $args{parsed}->{month}) {
456             Carp::croak("Invalid input format: Month " .
457             $era->id .
458             " is after the end of era '$era_name'");
459             } elsif ($era->start->day >= $args{parsed}->{day}) {
460             Carp::croak("Invalid input format: Day " .
461             $era->id .
462             " is after the end of era '$era_name'");
463             }
464             }
465              
466             $args{parsed}->{year} = $g_year;
467              
468             1;
469             }
470              
471             require DateTime::Format::Builder;
472             DateTime::Format::Builder->create_class(
473             parsers => {
474             parse_datetime => [
475             $parse_with_era, $parse_gregorian, $parse_gregorian_bc
476             ]
477             }
478             );
479              
480             1;
481              
482             __END__