File Coverage

blib/lib/DateTime/Format/Japanese/Common.pm
Criterion Covered Total %
statement 37 39 94.8
branch n/a
condition n/a
subroutine 13 13 100.0
pod n/a
total 50 52 96.1


line stmt bran cond sub pod time code
1             # $Id: /mirror/datetime/DateTime-Format-Japanese/trunk/lib/DateTime/Format/Japanese/Common.pm 69499 2008-08-24T16:17:57.045540Z lestrrat $
2              
3             package DateTime::Format::Japanese::Common;
4 4     4   25 use strict;
  4         8  
  4         160  
5 4     4   23 use warnings;
  4         8  
  4         120  
6 4     4   6778 use utf8;
  4         9265  
  4         115  
7 4     4   311 use Exporter;
  4         8  
  4         254  
8 4     4   26 use vars qw(@ISA %EXPORT_TAGS);
  4         9  
  4         289  
9 4     4   24 use constant FORMAT_KANJI_WITH_UNIT => 'FORMAT_KANJI_WITH_UNIT';
  4         9  
  4         303  
10 4     4   23 use constant FORMAT_KANJI => 'FORMAT_KANJI';
  4         7  
  4         221  
11 4     4   19 use constant FORMAT_ZENKAKU => 'FORMAT_ZENKAKU';
  4         7  
  4         212  
12 4     4   21 use constant FORMAT_ROMAN => 'FORMAT_ROMAN';
  4         6  
  4         178  
13 4     4   21 use constant FORMAT_ERA => 'FORMAT_ERA';
  4         7  
  4         192  
14 4     4   23 use constant FORMAT_GREGORIAN => 'FORMAT_GREGORIAN';
  4         8  
  4         430  
15             BEGIN
16             {
17 4     4   87 @ISA = qw(Exporter);
18 4         42 %EXPORT_TAGS = (
19             constants => [ qw(
20             FORMAT_KANJI_WITH_UNIT FORMAT_KANJI FORMAT_ZENKAKU
21             FORMAT_ROMAN FORMAT_ERA FORMAT_GREGORIAN) ]
22             );
23 4         3561 Exporter::export_ok_tags('constants');
24             }
25 4     4   2510 use DateTime::Calendar::Japanese::Era;
  0            
  0            
26             use Encode ();
27             use Encode::Guess ();
28              
29             BEGIN
30             {
31             my($euc2utf8_sub, $normalize_utf8_sub);
32              
33             $normalize_utf8_sub = sub {
34             my %args = @_;
35              
36             my $self = $args{self};
37             if (ref($self) && $self->{input_encoding} ne 'Guess') {
38             return Encode::decode($self->{input_encoding}, $args{input});
39             } else {
40             if (Encode::is_utf8($args{input})) {
41             return $args{input};
42             } else {
43             my $enc = Encode::Guess::guess_encoding(
44             $args{input}, qw(euc-jp shiftjis 7bit-jis)) or
45             die "Could not guess encoding for input!";
46             return Encode::decode($enc->name, $args{input});
47             }
48             }
49             };
50              
51             {
52             no strict 'refs';
53             *_normalize_utf8 = $normalize_utf8_sub;
54             }
55             }
56              
57             sub _make_utf8_re_str
58             {
59             my $u = shift;
60             my $l = length($u);
61             return sprintf( '\x{%04X}' x $l, unpack('U ' x $l, $u));
62             }
63              
64             sub _make_utf8_re
65             {
66             _make_re(_make_utf8_re_str(@_));
67             }
68              
69             sub _make_re
70             {
71             my $re = shift;
72             return qr($re);
73             }
74              
75             # Declare a bunch of variables
76             use vars qw(
77             @DAY_OF_WEEKS
78             @ZENKAKU_NUMBERS @KANJI_NUMBERS %ZENKAKU2ASCII %KANJI2ASCII %JP2ASCII
79             %AMPM
80             $KANJI_TEN
81             $KANJI_ZERO
82             $BC_MARKER
83             $GREGORIAN_MARKER
84             $YEAR_MARKER
85             $MONTH_MARKER
86             $DAY_MARKER
87             $DAY_MARKER
88             $HOUR_MARKER
89             $MINUTE_MARKER
90             $SECOND_MARKER
91             $AM_MARKER
92             $PM_MARKER
93             $DAY_OF_WEEK_SHORT_MARKER
94             $DAY_OF_WEEK_MARKER
95             $TRADITIONAL_MARKER
96             $RE_KANJI_TEN
97             $RE_KANJI_ZERO
98             $RE_BC_MARKER
99             $RE_GREGORIAN_MARKER
100             $RE_YEAR_MARKER
101             $RE_MONTH_MARKER
102             $RE_DAY_MARKER
103             $RE_DAY_MARKER
104             $RE_HOUR_MARKER
105             $RE_MINUTE_MARKER
106             $RE_SECOND_MARKER
107             $RE_AM_MARKER
108             $RE_PM_MARKER
109             $RE_TRADITIONAL_MARKER
110             $RE_ZENKAKU_NUM
111             $RE_KANJI_NUM
112             $RE_ZENKAKU_NUM
113             $RE_JP_OR_ASCII_NUM
114             $RE_GREGORIAN_YEAR
115             $RE_ERA_YEAR_SPECIAL
116             $RE_ERA_YEAR
117             $RE_ERA_NAME
118             $RE_TWO_DIGITS
119             $RE_AM_PM_MARKER
120             $RE_DAY_OF_WEEKS
121             );
122              
123             { # XXX - eh, not need to put this in different scope, but _makes this stand out
124             $KANJI_TEN = '十';
125             $KANJI_ZERO = '零';
126             $BC_MARKER = '紀元前';
127             $GREGORIAN_MARKER = '西暦';
128             $YEAR_MARKER = '年';
129             $MONTH_MARKER = '月';
130             $DAY_MARKER = '日';
131             $HOUR_MARKER = '時';
132             $MINUTE_MARKER = '分';
133             $SECOND_MARKER = '秒';
134             $AM_MARKER = '午前';
135             $PM_MARKER = '午後';
136             $TRADITIONAL_MARKER = '旧暦';
137             $DAY_OF_WEEK_SHORT_MARKER = '曜';
138             $DAY_OF_WEEK_MARKER = $DAY_OF_WEEK_SHORT_MARKER . $DAY_MARKER;
139              
140             @ZENKAKU_NUMBERS = qw(0 1 2 3 4 5 6 7 8 9);
141             @KANJI_NUMBERS = qw(〇 一 二 三 四 五 六 七 八 九);
142             %ZENKAKU2ASCII = map { ($ZENKAKU_NUMBERS[$_] => $_) } 0..$#ZENKAKU_NUMBERS;
143             %KANJI2ASCII = map { ($KANJI_NUMBERS[$_] => $_) } 0.. $#KANJI_NUMBERS;
144             $KANJI2ASCII{ $KANJI_ZERO } = 0;
145             %JP2ASCII = (%ZENKAKU2ASCII, %KANJI2ASCII);
146              
147             @DAY_OF_WEEKS = qw( 月 火 水 木 金 土 日 );
148              
149             %AMPM = (
150             $AM_MARKER => 0,
151             $PM_MARKER => 1
152             );
153              
154             $RE_DAY_OF_WEEKS = _make_re(
155             '(?:' . join( '|', map { _make_utf8_re_str($_) } @DAY_OF_WEEKS ) . ')' .
156             _make_utf8_re_str($DAY_OF_WEEK_SHORT_MARKER) .
157             '(?:' . _make_utf8_re_str($DAY_MARKER) . ')?');
158              
159             $RE_ZENKAKU_NUM = _make_re( sprintf( '[%s]',
160             _make_utf8_re_str( join('', @ZENKAKU_NUMBERS) ) ) );
161              
162             $RE_KANJI_NUM = _make_re( sprintf( '[%s]',
163             _make_utf8_re_str( join('', @KANJI_NUMBERS) ) ) );
164             $RE_ZENKAKU_NUM = _make_re( sprintf( '[%s]',
165             _make_utf8_re_str( join('', @ZENKAKU_NUMBERS, @KANJI_NUMBERS) ) ) );
166             $RE_JP_OR_ASCII_NUM = qr([0-9]|$RE_ZENKAKU_NUM);
167             $RE_BC_MARKER = _make_utf8_re($BC_MARKER);
168             $RE_GREGORIAN_MARKER = _make_utf8_re($GREGORIAN_MARKER);
169             $RE_TRADITIONAL_MARKER = _make_utf8_re($TRADITIONAL_MARKER);
170             $RE_AM_PM_MARKER = _make_re( join( '|',
171             _make_utf8_re_str($AM_MARKER), _make_utf8_re_str($PM_MARKER), '') );
172             $RE_YEAR_MARKER = _make_utf8_re($YEAR_MARKER);
173             $RE_MONTH_MARKER = _make_utf8_re($MONTH_MARKER);
174             $RE_DAY_MARKER = _make_utf8_re($DAY_MARKER);
175             $RE_HOUR_MARKER = _make_utf8_re($HOUR_MARKER);
176             $RE_MINUTE_MARKER = _make_utf8_re($MINUTE_MARKER);
177             $RE_SECOND_MARKER = _make_utf8_re($SECOND_MARKER);
178             $RE_KANJI_TEN = _make_utf8_re($KANJI_TEN);
179             $RE_KANJI_ZERO = _make_utf8_re($KANJI_ZERO);
180              
181             $RE_TWO_DIGITS = qr(
182             ${RE_KANJI_NUM}?${RE_KANJI_TEN}?${RE_KANJI_NUM} |
183             ${RE_ZENKAKU_NUM}?${RE_ZENKAKU_NUM} |
184             [0-9]?[0-9]
185             )x;
186            
187             $RE_GREGORIAN_YEAR = qr(-?$RE_JP_OR_ASCII_NUM+);
188             $RE_ERA_YEAR_SPECIAL = _make_utf8_re('元');
189             $RE_ERA_YEAR = qr($RE_ERA_YEAR_SPECIAL|$RE_TWO_DIGITS);
190             $RE_ERA_NAME = _make_re(join( "|",
191             map { $_->name } DateTime::Calendar::Japanese::Era->registered) );
192             }
193              
194             my %valid_number_format = (
195             FORMAT_KANJI_WITH_UNIT() => 1,
196             FORMAT_KANJI() => 1,
197             FORMAT_ZENKAKU() => 1,
198             FORMAT_ROMAN() => 1,
199             );
200              
201             sub _valid_number_format { exists $valid_number_format{$_[0]} }
202              
203             my %valid_year_format = (
204             FORMAT_ERA() => 1,
205             FORMAT_GREGORIAN() => 1
206             );
207              
208             sub _valid_year_format { exists $valid_year_format{$_[0]} }
209              
210             # Era year 1 can be written as "元年"
211             sub _fix_era_year
212             {
213             my %args = @_;
214             if ($args{parsed}->{era_year} =~ /$RE_ERA_YEAR_SPECIAL/) {
215             $args{parsed}->{era_year} = 1;
216             }
217             return 1;
218             }
219              
220             sub _normalize_numbers
221             {
222             my %args = @_;
223             foreach my $key qw(year month day era_year hour minute second) {
224             if (defined $args{parsed}->{$key}) {
225             $args{parsed}->{$key} =~ s/^$RE_KANJI_TEN/1/;
226             $args{parsed}->{$key} =~ s/$RE_KANJI_TEN//;
227             }
228              
229             # check for definedness here so that we don't get use uninitialized
230             # ... warnings in the substitution, plus so that DateTime doesn't
231             # complain + it uses the appropriate default value
232             if (!defined $args{parsed}->{$key}) {
233             delete $args{parsed}->{$key};
234             }
235              
236             if (exists $args{parsed}->{$key} && defined($args{parsed}->{$key})) {
237             $args{parsed}->{$key} =~ s/($RE_KANJI_NUM|$RE_ZENKAKU_NUM)/$JP2ASCII{$1}/ge;
238             }
239             }
240              
241             return 1;
242             }
243              
244             sub _fix_am_pm
245             {
246             my %args = @_;
247             if (my $am_pm = delete $args{parsed}->{am_pm}) {
248             if (!exists $AMPM{ $am_pm }) {
249             return 0;
250             }
251              
252             my $is_pm = $AMPM{ $am_pm };
253              
254             if (!$is_pm && $args{parsed}->{hour} >= 12) {
255             return 0;
256             }
257              
258             if ($is_pm && $args{parsed}->{hour} < 12) {
259             $args{parsed}->{hour} += 12;
260             }
261             }
262             return 1;
263             }
264              
265             sub _format_number
266             {
267             my($number, $number_format) = @_;
268              
269             if($number_format eq FORMAT_KANJI_WITH_UNIT()) {
270             if ($number > 99) {
271             Carp::croak("format_number doesn't support formatting numbers that are greater than 99");
272             }
273              
274             if ($number < 10) {
275             $number = $KANJI_NUMBERS[$number];
276             } else {
277             my $tens = int($number / 10);
278             my $ones = $number % 10;
279             if ($tens > 1) {
280             $number = $KANJI_NUMBERS[$tens] . $KANJI_TEN . $KANJI_NUMBERS[$ones];
281             } else {
282             $number = $KANJI_TEN . $KANJI_NUMBERS[$ones];
283             }
284             }
285             } elsif ($number_format eq FORMAT_ZENKAKU()) {
286             $number =~ s/(\d)/$ZENKAKU_NUMBERS[$1]/ge;
287             } elsif ($number_format eq FORMAT_KANJI()) {
288             $number =~ s/(\d)/$KANJI_NUMBERS[$1]/ge;
289             }
290              
291             return $number;
292             }
293              
294             sub _format_era
295             {
296             my($dt, $number_format) = @_;
297              
298             my $era = DateTime::Calendar::Japanese::Era->lookup_by_date(
299             datetime => $dt);
300             if (!$era) {
301             Carp::croak("No era defined for specified date");
302             }
303              
304             my $era_year = ($dt->year - $era->start->year) + 1;
305             my $era_name = Encode::decode_utf8($era->name);
306              
307             return $era_name .
308             _format_number($era_year, $number_format) .
309             $YEAR_MARKER;
310             }
311              
312             sub _format_common_with_marker
313             {
314             my($marker, $number, $number_format) = @_;
315             return _format_number($number, $number_format) . $marker;
316             }
317              
318             1;
319              
320             __END__