File Coverage

blib/lib/Lingua/JA/FindDates.pm
Criterion Covered Total %
statement 186 226 82.3
branch 115 148 77.7
condition 28 57 49.1
subroutine 14 17 82.3
pod 9 11 81.8
total 352 459 76.6


line stmt bran cond sub pod time code
1             package Lingua::JA::FindDates;
2 3     3   132772 use warnings;
  3         21  
  3         97  
3 3     3   16 use strict;
  3         5  
  3         71  
4 3     3   16 use Carp qw/carp croak cluck/;
  3         5  
  3         269  
5 3     3   1179 use utf8;
  3         31  
  3         15  
6 3     3   148 use 5.010000;
  3         13  
7              
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our @EXPORT_OK= qw/subsjdate kanji2number seireki_to_nengo nengo_to_seireki
11             regjnums @jdatere/;
12             our %EXPORT_TAGS = (
13             all => \@EXPORT_OK,
14             );
15              
16             our $VERSION = '0.027';
17              
18             # Kanji number conversion table.
19              
20             my %kanjinums =
21             (
22             〇 => 0,
23             一 => 1,
24             二 => 2,
25             三 => 3,
26             四 => 4,
27             五 => 5,
28             六 => 6,
29             七 => 7,
30             八 => 8,
31             九 => 9,
32             十 => 10,
33             百 => 100,
34             # Dates shouldn't get any bigger than the following times a digit.
35             千 => 1000,
36             );
37              
38             # The kanji digits.
39              
40             my $kanjidigits = join ('', keys %kanjinums);
41              
42             sub kanji2number
43             {
44 51     51 1 1667 my ($knum) = @_;
45 51 100       129 return 1 if $knum eq '元';
46 50         127 my @kanjis = split '', $knum;
47 50         76 my $value = 0;
48 50         67 my $keta = 1;
49 50         75 while (1) {
50 120         189 my $k = pop @kanjis;
51 120 100       346 return $value if ! defined $k;
52 75         137 my $val = $kanjinums{$k};
53             # Make sure this kanji number is one we know how to handle.
54 75 50       136 if (! defined $val) {
55 0         0 warn "can't cope with '$k' of input '$knum'";
56 0         0 return 0;
57             }
58             # If the value of the individual kanji is more than 10.
59 75 100       132 if ($val >= 10) {
60 25         37 $keta = $val;
61 25         41 my $knext = pop @kanjis;
62 25 100       51 if (!$knext) {
63 4         15 return $value + $val;
64             }
65 21         35 my $val_next = $kanjinums{$knext};
66 21 100       43 if (!defined $val_next) {
67 1         13 warn "can't cope with '$knext' of input '$knum'.\n";
68 1         12 return 0;
69             }
70 20 50       35 if ($val_next > 10) {
71 0         0 push @kanjis, $knext;
72 0         0 $value += $val;
73             }
74             else {
75 20         35 $value += $val_next * $val;
76             }
77             }
78             else {
79             # $k is a kanji digit from 0 to 9, and $val is its value.
80 50         77 $value += $val * $keta;
81 50         82 $keta *= 10;
82             }
83             }
84             }
85              
86             # ____
87             # | _ \ ___ __ _ _____ _____ ___
88             # | |_) / _ \/ _` |/ _ \ \/ / _ \/ __|
89             # | _ < __/ (_| | __/> < __/\__ \
90             # |_| \_\___|\__, |\___/_/\_\___||___/
91             # |___/
92              
93             my $jdigit = qr/[0-90-9]/;
94              
95             # A regular expression to match Japanese numbers
96              
97             my $jnumber = qr/($jdigit+|[$kanjidigits]+)/x;
98              
99             # A regular expression to match a Western year
100              
101             my $wyear = qr/
102             (
103             $jdigit{4}
104             |
105             [$kanjidigits]?千[$kanjidigits]*
106             |
107             [\']$jdigit{2}
108             )
109             \s*年
110             /x;
111              
112             my $alpha_era = qr/
113             # If the H, S, T, or M is part of a longer
114             # string of romaji, do not match it.
115             (?
116             (?:
117             [H|H|S|S|T|T|M|M]
118             )
119             /x;
120              
121             # The recent era names (Heisei, Showa, Taisho, Meiji). These eras are
122             # sometimes written using the letters H, S, T, and M.
123              
124             my $jera = qr/($alpha_era|平|昭|大|明|平成|昭和|大正|明治|㍻|㍼|㍽|㍾)/;
125              
126             # A map of Japanese eras to Western dates. These are the starting year
127             # of the period minus one, to allow for that the first year is "heisei
128             # one" rather than "heisei zero".
129              
130             my %jera2w = (
131             H => 1988,
132             H => 1988,
133             平成 => 1988,
134             平 => 1988,
135             '㍻' => 1988,
136             S => 1925,
137             S => 1925,
138             昭和 => 1925,
139             昭 => 1925,
140             '㍼' => 1925,
141             T => 1911,
142             T => 1911,
143             大正 => 1911,
144             大 => 1911,
145             '㍽' => 1911,
146             M => 1867,
147             M => 1867,
148             明治 => 1867,
149             明 => 1867,
150             '㍾' => 1867,
151             );
152              
153             # Japanese year, with era like "Heisei" at the beginning.
154              
155             my $jyear = qr/
156             $jera
157             \h*
158             # Only match up to one or two of these digits, to
159             # prevent unlikely matches.
160             (
161             $jdigit{1,2}
162             |
163             [$kanjidigits]{1,2}
164             |
165             # The first year of an era, something like
166             # "昭和元年" (1926, the first year of the Showa era).
167            
168             )
169             \h*
170            
171             /x;
172              
173             # The "jun" or approximately ten day periods (thirds of a month)
174              
175             my %jun = qw/初 1 上 1 中 2 下 3/;
176              
177             # The translations of the "jun" above into English.
178              
179             my @jun2english = (
180             'invalid',
181             'early ',
182             'mid-',
183             'late ',
184             );
185              
186             # Japanese days of the week, from Monday to Sunday.
187              
188             my $weekdays = '月火水木金土日';
189             my @weekdays = split '',$weekdays;
190              
191             # Match a string for a weekday, like 月曜日 or (日)
192             # The long part (?=\W) is to stop it from accidentally matching a
193             # kanji which is part of a different word, like the following:
194             #平成二十年七月一日
195             # 日本論・日本人論は非常に面白いものだ。
196              
197             my $match_weekday =
198             qr/[\((]?
199             ([$weekdays])
200             (?:(?:(?:曜日|曜)[)\)])|[)\)]|(?=\W))
201             /x;
202              
203             # Match a day of the month, like 10日
204              
205             my $match_dom = qr/$jnumber\h*日/;
206              
207             # Match a month
208              
209             my $match_month = qr/$jnumber\h*月/;
210              
211             # Match a "jun" (a third of a month).
212              
213             my $jun_keys = join ('', keys %jun);
214              
215             my $match_jun = qr/([$jun_keys])\h*旬/;
216              
217             # Match a month+jun
218              
219             my $match_month_jun = qr/$match_month\h*$match_jun/;
220              
221             # Match a month and day of month pair
222              
223             my $match_month_day = qr/$match_month\h*$match_dom/;
224              
225             # Match a Japanese year, month, day string
226              
227             my $matchymd = qr/
228             $jyear
229             \h*
230             $match_month_day
231             /x;
232              
233             # Match a Western year, month, day string
234              
235             my $matchwymd = qr/$wyear\h*$match_month_day/;
236              
237             # Match a Japanese year and month only
238              
239             my $match_jyear_month = qr/$jyear\h*$match_month/;
240              
241             # Match a Western year and month only
242              
243             my $match_wyear_month = qr/$wyear\h*$match_month/;
244              
245             # Match a month, day, weekday.
246              
247             my $match_month_day_weekday = qr/$match_month_day\h*$match_weekday/;
248              
249             # Separators used in date strings
250             # Microsoft Word uses Unicode 0xFF5E, the "fullwidth tilde", for nyoro symbol.
251              
252             my $separators = qr/\h*[〜−~]\h*/;
253            
254             # _ _ _ __
255             # | | (_)___| |_ ___ / _| _ __ ___ __ _ _____ _____ ___
256             # | | | / __| __| / _ \| |_ | '__/ _ \/ _` |/ _ \ \/ / _ \/ __|
257             # | |___| \__ \ |_ | (_) | _| | | | __/ (_| | __/> < __/\__ \
258             # |_____|_|___/\__| \___/|_| |_| \___|\__, |\___/_/\_\___||___/
259             # |___/
260              
261             # This a list of date regular expressions.
262              
263             our @jdatere = (
264              
265             # Match an empty string like 平成 月 日 as found on a form etc.
266              
267             [qr/
268             $jyear
269             (\h+)
270            
271             \h+
272            
273             /x,
274             "ejx"],
275              
276             # Add match for dummy strings here!
277              
278             # Match a Japanese era, year, 2 x (month day weekday) combination
279              
280             [qr/
281             $matchymd
282             \h*$match_weekday
283             $separators
284             $matchymd
285             \h*$match_weekday
286             /x,
287             "e1j1m1d1w1e2j2m2d2w2"],
288              
289             # Match 2 x (era, year, month, day) combination
290              
291             [qr/
292             $matchymd
293             $separators
294             $matchymd
295             /x,
296             "e1j1m1d1e2j2m2d2"],
297              
298             # Match a Japanese era, year, month 2 x (day, weekday) combination
299              
300             [qr/
301             $matchymd
302             $match_weekday
303             $separators
304             $match_dom
305             \h*
306             $match_weekday
307             /x,
308             "ejmd1w1d2w2"],
309              
310             # Match a Japanese era, year, month 2 x day combination
311              
312             [qr/
313             $matchymd
314             $separators
315             $match_dom
316             \h*
317             $match_weekday
318             /x,
319             "ejmd1d2"],
320              
321             # Match 2x(Western year, month, day, weekday) combination
322              
323             [qr/
324             $matchwymd
325             \h*
326             $match_weekday
327             $separators
328             $matchwymd
329             $match_weekday
330             /x,
331             "y1m1d1w1y2m2d2w2"],
332              
333             # Match a Western year, 2x(month, day, weekday) combination
334              
335             [qr/
336             $matchwymd
337             \h*
338             $match_weekday
339             $separators
340             $match_month_day_weekday
341             /x,
342             "ym1d1w1m2d2w2"],
343              
344             # Match a Western year, month, 2x(day, weekday) combination
345              
346             [qr/
347             $matchwymd
348             \h*
349             $match_weekday
350             $separators
351             $match_dom
352             \h*
353             $match_weekday
354             /x,
355             "ymd1w1d2w2"],
356              
357             # Match a Western year, month, 2x(day) combination
358              
359             [qr/
360             $matchwymd
361             $separators
362             $match_dom
363             /x,
364             "ymd1d2"],
365              
366             # Match a Japanese era, year, month1 day1 - month 2 day2 combination
367              
368             [qr/
369             $matchymd
370             $separators
371             $match_month_day
372             /x,
373             "ejm1d1m2d2"],
374              
375             # Match 2 x ( Japanese era, year, month) combination
376              
377             [qr/
378             $jyear
379             \h*
380             $jnumber
381             \h*月?
382             $separators
383             $jyear
384             \h*
385             $match_month
386             /x, "e1j1m1e2j2m2"],
387              
388             # Match a Japanese era, year, month1 - month 2 combination
389              
390             [qr/
391             $jyear
392             \h*
393             $jnumber
394             \h*月?
395             $separators
396             $match_month
397             /x, "ejm1m2"],
398              
399             # Match a Japanese era, year, month, day1 - day2 combination
400              
401             [qr/
402             $match_jyear_month
403             \h*
404             $jnumber
405             \h*日?
406             $separators
407             $match_dom
408             /x,
409             "ejmd1d2"],
410              
411             # Match a Japanese era, year, month, day, weekday combination
412              
413             [qr/
414             $matchymd
415             \h*
416             $match_weekday
417             /x,
418             "ejmdw"],
419              
420             # Match a Japanese era, year, month, day
421              
422             [qr/$matchymd/,
423             "ejmd"],
424              
425             # Match a Japanese era, year, month, jun
426              
427             [qr/
428             $match_jyear_month
429             \h*
430             $match_jun
431             /x,
432             "ejmz"],
433              
434             # Match a Western year, month, day, weekday combination
435              
436             [qr/
437             $matchwymd
438             \h*
439             $match_weekday
440             /x,
441             "ymdw"],
442              
443             # Match a Western year, month, day combination
444              
445             [qr/$matchwymd/,
446             "ymd"],
447              
448             # Match a Western year, month, jun combination
449              
450             [qr/
451             $match_wyear_month
452             \h*
453             $match_jun
454             /x,
455             "ymz"],
456              
457             # Match a Japanese era, year, month
458              
459             [qr/
460             $jyear
461             \h*
462             $jnumber
463             \h*
464            
465             /x,
466             "ejm"],
467              
468             # Match a Western year, month
469              
470             [qr/$match_wyear_month/,
471             "ym"],
472              
473             # Match 2 x (month, day, weekday)
474              
475             [qr/
476             $match_month_day_weekday
477             $separators
478             $match_month_day_weekday
479             /x,
480             "m1d1w1m2d2w2"],
481              
482             # Match month, 2 x (day, weekday)
483              
484             [qr/
485             $match_month_day_weekday
486             $separators
487             $match_dom
488             \h*
489             $match_weekday
490             /x,
491             "md1w1d2w2"],
492              
493             # Match month, 2 x (day, weekday)
494              
495             [qr/
496             $match_month_day
497             $separators
498             $match_dom
499             /x,
500             "md1d2"],
501              
502             # Match a month, day, weekday
503              
504             [qr/$match_month_day_weekday/,
505             "mdw"],
506              
507             # Match a month, day
508              
509             [qr/$match_month_day/,
510             "md"],
511              
512             # Match a fiscal year (年度, nendo in Japanese). These usually don't
513             # have months combined with them, so there is nothing to match a
514             # fiscal year with a month.
515              
516             [qr/
517             $jyear
518            
519             /x,
520             "en"],
521              
522             # Match a fiscal year (年度, nendo in Japanese). These usually don't
523             # have months combined with them, so there is nothing to match a
524             # fiscal year with a month.
525              
526             [qr/
527             $wyear
528            
529             /x,
530             "n"],
531              
532             # Match a Japanese era, year
533              
534             [qr/$jyear/,
535             "ej"],
536              
537             # Match a Western year
538              
539             [qr/$wyear/,
540             "y"],
541              
542             # Match a month with a jun
543              
544             [
545             qr/
546             $match_month
547             \h*
548             $match_jun
549             /x,
550             "mz"
551             ],
552              
553             # Match a month
554              
555             [
556             qr/$match_month/,
557             "m"
558             ],
559             );
560              
561             my @months = qw/Invalid
562             January
563             February
564             March
565             April
566             May
567             June
568             July
569             August
570             September
571             October
572             November
573             December
574             MM/;
575              
576             my @days = qw/Invalid
577             Monday
578             Tuesday
579             Wednesday
580             Thursday
581             Friday
582             Saturday
583             Sunday/;
584              
585             # This is a translation table from the Japanese weekday names to the
586             # English ones.
587              
588             my %j2eweekday;
589              
590             @j2eweekday{@weekdays} = (1..7);
591              
592             # This is the default routine for turning a Japanese date into a
593             # foreign-style one.
594              
595             sub make_date
596             {
597 0     0 1 0 goto & default_make_date;
598             }
599              
600             sub make_date_interval
601             {
602 0     0 1 0 goto & default_make_date_interval;
603             }
604              
605             sub default_make_date
606             {
607 46     46 1 89 my ($datehash) = @_;
608             my ($year, $month, $date, $wday, $jun) =
609 46         74 @{$datehash}{qw/year month date wday jun/};
  46         140  
610 46 0 66     120 if (!$year && !$month && !$date && !$jun) {
      33        
      0        
611 0         0 carp "No valid inputs\n";
612 0         0 return;
613             }
614 46         74 my $edate = '';
615 46 100       100 $edate = $days[$wday].", " if $wday;
616 46 100       84 if ($month) {
617 34         61 $month = int ($month); # In case it is 07 etc.
618 34         67 $edate .= $months[$month];
619 34 100       68 if ($jun) {
620 1         4 $edate = $jun2english[$jun] . $edate;
621             }
622             }
623 46 100       90 if ($date) {
    100          
624 29 50       67 $edate .= " " if length ($edate);
625 29         45 $date = int ($date); # In case it is 07 etc.
626 29 100       56 $date = "DD" if $date == 32;
627 29 100       50 if ($year) {
628 25         60 $edate .= "$date, $year";
629             }
630             else {
631 4         13 $edate .= "$date";
632             }
633             }
634             elsif ($year) {
635 14 100       36 if (length ($edate) > 0) {
636 2         3 $edate .= " ";
637             }
638 14         30 $edate .= $year;
639             }
640 46         101 return $edate;
641             }
642              
643             our $date_sep = '-';
644              
645             # This is the default routine for turning a date interval into a
646             # foreign-style one, which is then substituted into the text.
647              
648             sub default_make_date_interval
649             {
650 17     17 1 605 my ($date1, $date2) = @_;
651 17         27 my $einterval = '';
652 17         22 my $usecomma;
653             # The case of an interval with different years doesn't need to be
654             # considered, because each date in that case can be considered a
655             # single date.
656              
657 17 100       40 if ($date2->{month}) {
658 9 50       22 if (!$date1->{month}) {
659 0         0 carp "end month but no starting month";
660 0         0 return;
661             }
662             }
663 17 50       34 if ($date1->{month}) {
664 17 100 66     72 if ($date1->{wday} && $date2->{wday}) {
    100 66        
665 6 50 33     24 if (! $date1->{date} || ! $date2->{date}) {
666 0         0 carp "malformed date has weekdays but not days of month";
667 0         0 return;
668             }
669 6         21 $usecomma = 1;
670             $einterval = $days[$date1->{wday}] . " " . $date1->{date} .
671             ($date2->{month} ? ' '.$months[int ($date1->{month})] : ''). $date_sep .
672             $days[$date2->{wday}] . " " . $date2->{date} . " " .
673 6 100       52 ($date2->{month} ? $months[int ($date2->{month})] : $months[int ($date1->{month})]);
    100          
674             }
675             elsif ($date1->{date} && $date2->{date}) {
676 7         10 $usecomma = 1;
677 7 50 33     27 if ($date1->{wday} || $date2->{wday}) {
678 0         0 carp "malformed date interval: ",
679             "has weekday for one date $date1->{wday} but not the other one $date2->{wday} .";
680 0         0 return;
681             }
682             $einterval = $months[int ($date1->{month})] . ' ' .
683             $date1->{date} . $date_sep .
684             ($date2->{month} ?
685             $months[int ($date2->{month})] . ' ' : '') .
686 7 100       43 $date2->{date};
687             }
688             else { # no dates or weekdays
689 4 50 33     17 if ($date1->{date} || $date2->{date}) {
690 0         0 cluck "malformed date interval: only one day of month";
691 0         0 return;
692             }
693 4 50       8 if (!$date2->{month}) {
694 0         0 carp "start month but no end month or date";
695 0         0 return;
696             }
697             $einterval = $months[int($date1->{month})] . $date_sep .
698 4         38 $months[int($date2->{month})] .
699             $einterval;
700             }
701             }
702             else { # weekday - day / weekday - day case.
703 0 0 0     0 if ($date1->{wday} && $date2->{wday}) {
704 0 0 0     0 if (! $date1->{date} || ! $date2->{date}) {
705 0         0 carp "malformed date has weekdays but not days of month";
706 0         0 return;
707             }
708             $einterval = $date1->{wday} . " " . $date1->{date} . $date_sep .
709 0         0 $date2->{wday} . " " . $date2->{date};
710             }
711             }
712 17 100       47 if ($date1->{year}) {
713 16 100       47 my $year1 = ($usecomma ? ', ': ' ').$date1->{year};
714 16 100 66     48 if (! $date2->{year} || $date2->{year} == $date1->{year}) {
715 14         30 $einterval .= $year1;
716             }
717             else {
718 2         22 $einterval =~ s/\Q$date_sep/$year1$date_sep/;
719 2 50       11 my $year2 = ($usecomma ? ', ': ' ').$date2->{year};
720 2         5 $einterval .= $year2;
721             }
722             }
723 17         46 return $einterval;
724             }
725              
726             our $verbose = 0;
727              
728             sub subsjdate
729             {
730             # $text is the text to substitute. It needs to be in Perl's
731             # internal encoding.
732             # $replace_callback is a routine to call back if we find valid dates.
733             # $data is arbitrary data to pass to the callback routine.
734 54     54 1 10638 my ($text, $c) = @_;
735             # Save doing existence tests.
736 54 100       148 if (! $c) {
737 47         91 $c = {};
738             }
739 54 50       120 if (! $text) {
740 0         0 return $text;
741             }
742             # Loop through all the possible regular expressions.
743 54         110 for my $datere (@jdatere) {
744 1728         6971 my $regex = $datere->[0];
745 1728         9652 my @process = split (/(?=[a-z][12]?)/, $datere->[1]);
746 1728 50       3506 if ($verbose) {
747 0         0 print "Looking for $datere->[1]\n";
748             }
749 1728         164163 while ($text =~ /($regex)/g) {
750 71         2592 my $date1;
751             my $date2;
752             # The matching string is in the following variable.
753 71         211 my $orig = $1;
754 71         356 my @matches = ($2,$3,$4,$5,$6,$7,$8,$9);
755 71 50       170 if ($verbose) {
756 0         0 print "Found '$orig': ";
757             }
758 71         197 for (0..$#matches) {
759 331         565 my $arg = $matches[$_];
760              
761 331 100       624 last if !$arg;
762 3     3   35 $arg =~ tr/0-9/0-9/;
  3         7  
  3         44  
  261         713  
763 261         1284 $arg =~ s/([$kanjidigits]+|元)/kanji2number($1)/ge;
  47         112  
764 261 50       569 if ($verbose) {
765 0         0 print "Arg $_: $arg ";
766             }
767 261         420 my $argdo = $process[$_];
768 261 100 100     1664 if ($argdo eq 'e1') { # Era name in Japanese
    100 100        
    50 100        
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
769 1         5 $date1->{year} = $jera2w{$arg};
770             }
771             elsif ($argdo eq 'j1') { # Japanese year
772 1         4 $date1->{year} += $arg;
773             }
774             elsif ($argdo eq 'y1') {
775 0         0 $date1->{year} = $arg;
776             }
777             elsif ($argdo eq 'e2') { # Era name in Japanese
778 1         3 $date2->{year} = $jera2w{$arg};
779             }
780             elsif ($argdo eq 'j2') { # Japanese year
781 1         3 $date2->{year} += $arg;
782             }
783             elsif ($argdo eq 'y2') {
784 0         0 $date2->{year} = $arg;
785             }
786             elsif ($argdo eq 'e') { # Era name in Japanese
787 43         174 $date1->{year} = $jera2w{$arg};
788             }
789             elsif ($argdo eq 'j') { # Japanese year
790 42         143 $date1->{year} += $arg;
791             }
792             elsif ($argdo eq 'y') {
793 18         65 $date1->{year} = $arg;
794             }
795             elsif ($argdo eq 'n') {
796 2         8 $date1->{year} += $arg;
797 2         8 $date1->{year} = "fiscal ".$date1->{year};
798             }
799             elsif ($argdo eq 'm' || $argdo eq 'm1') {
800 58         165 $date1->{month} = $arg;
801             }
802             elsif ($argdo eq 'd' || $argdo eq 'd1') {
803 49         147 $date1->{date} = $arg;
804             }
805             elsif ($argdo eq 'm2') {
806 8         25 $date2->{month} = $arg;
807             }
808             elsif ($argdo eq 'd2') {
809 12         35 $date2->{date} = $arg;
810             }
811             elsif ($argdo eq 'w' || $argdo eq 'w1') {
812 18         55 $date1->{wday} = $j2eweekday{$arg};
813             }
814             elsif ($argdo eq 'w2') {
815 5         14 $date2->{wday} = $j2eweekday{$arg};
816             }
817             elsif ($argdo eq 'z') {
818 1         6 $date1->{jun} = $jun{$arg};
819             }
820             elsif ($argdo eq 'x') {
821 1 50       4 if ($verbose) {
822 0         0 print "Dummy date '$orig'.\n";
823             }
824 1         3 $date1->{date} = 32;
825 1         4 $date1->{month} = 13;
826             }
827             }
828 71         118 my $edate;
829 71 100       125 if ($date2) {
830             # Date interval
831 16 50       33 if ($c->{make_date_interval}) {
832 0         0 $edate = &{$c->{make_date_interval}} ($c->{data}, $orig,
  0         0  
833             $date1, $date2);
834             }
835             else {
836 16         42 $edate = default_make_date_interval ($date1, $date2);
837             }
838             }
839             else {
840             # Single date
841 55 100       106 if ($c->{make_date}) {
842 9         21 $edate = &{$c->{make_date}}($c->{data}, $orig, $date1);
  9         29  
843             }
844             else {
845 46         98 $edate = default_make_date ($date1);
846             }
847             }
848 71 50       340 if ($verbose) {
849 0         0 print "-> '$edate'\n";
850             }
851 71         1026 $text =~ s/\Q$orig\E/$edate/g;
852 71 100       705 if ($c->{replace}) {
853 13         37 &{$c->{replace}} ($c->{data}, $orig, $edate);
  13         39  
854             }
855             }
856             }
857 54         678 return $text;
858             }
859              
860             sub nengo_to_seireki
861             {
862 1     1 1 12121 my ($text) = @_;
863 1         3 my %data;
864 1         2 $data{count} = 0;
865              
866 1         7 my $out_text = subsjdate (
867             $text, {
868             make_date => \& nengo_to_seireki_make_date,
869             data => \%data,
870             }
871             );
872 1         20 $out_text =~ s/#REPLACEME(\d+)REPLACEME#/$data{$1}/g;
873 1         8 return $out_text;
874             }
875              
876             sub nengo_to_seireki_make_date
877             {
878 4     4 0 10 my ($data, $original, $date) = @_;
879 4 50       9 if ($date->{year}) {
880 4         30 $original =~ s/.*年/$date->{year}年/;
881 4         10 my $count = $data->{count};
882 4         10 $data->{$count} = $original;
883 4         5 $data->{count}++;
884 4         16 return "#REPLACEME${count}REPLACEME#";
885             }
886             else {
887 0         0 return $original;
888             }
889             }
890              
891             sub seireki_to_nengo
892             {
893 2     2 1 12690 my ($text) = @_;
894 2         4 my %data;
895 2         4 $data{count} = 0;
896              
897 2         12 my $out_text = subsjdate (
898             $text, {
899             make_date => \& seireki_to_nengo_make_date,
900             data => \%data,
901             }
902             );
903 2         25 $out_text =~ s/#REPLACEME(\d+)REPLACEME#/$data{$1}/g;
904 2         19 return $out_text;
905             }
906              
907             sub seireki_to_nengo_make_date
908             {
909 2     2 0 7 my ($data, $original, $date) = @_;
910 2         4 my $year = $date->{year};
911 2         10 my @eras = (
912             ['平成', 1989, 1, 8],
913             ['昭和', 1926, 12, 25],
914             ['大正', 1912, 7, 30],
915             ['明治', 1868, 1, 25],
916             );
917 2 50       6 if (defined $year) {
918 2         4 for my $era (@eras) {
919 3         6 my $ename = $era->[0];
920 3         4 my $eyear = $era->[1];
921 3         5 my $emonth = $era->[2];
922 3         5 my $eday = $era->[3];
923 3         6 my $month = $date->{month};
924 3         5 my $date = $date->{date};
925              
926             # This is a flag which says whether to perform a
927             # substitution of the year or not.
928              
929 3         4 my $subs;
930              
931             # If the year is greater than the era year, or if the year
932             # is the same as the era year and we do not know the
933             # month, just replace.
934              
935 3 100 33     21 if ($year > $eyear ||
    50 66        
      33        
936             ($year == $eyear && ! defined ($month))) {
937 1         4 $subs = 1;
938             }
939              
940             # If the year is the same, and there is a month
941              
942             elsif ($year == $eyear && defined ($month)) {
943              
944             # If there is a day of the month, then only substitute
945             # if the month is greater than the changeover month,
946             # or the month is the same, and the day of the month
947             # is greater than or equal to the changeover day of
948             # the month.
949              
950 2 50       6 if (defined ($date)) {
    0          
951 2 100 33     12 if ($month > $emonth ||
      66        
952             ($month == $emonth && $date >= $eday)) {
953 1         5 $subs = 1;
954             }
955             }
956              
957             # If we don't know the day of the month, substitute if
958             # the month is greater than or equal to the changeover
959             # month.
960              
961             elsif ($month >= $emonth) {
962 0         0 $subs = 1;
963             }
964             }
965 3 100       9 if ($subs) {
966              
967             # Only substitute if we need to.
968              
969 2 50       26 if ($original !~ /$ename/) {
970              
971             # The year counting starts from 1, so we add 1 to
972             # the difference.
973              
974 2         6 my $hyear = $year - $eyear + 1;
975 2         20 $original =~ s/\d+年/$ename${hyear}年/;
976             }
977              
978             # Don't replace again, stop the loop.
979              
980 2         7 last;
981             }
982             }
983             }
984 2         3 my $count = $data->{count};
985 2         6 $data->{$count} = $original;
986 2         3 $data->{count}++;
987              
988             # This is a tag for substituting with.
989              
990 2         10 return "#REPLACEME${count}REPLACEME#";
991             }
992              
993             # Regularize any small integer Japanese numbers in a piece of text.
994              
995             sub regjnums
996             {
997 0     0 1   my ($input) = @_;
998 0           $input =~ tr/0-9/0-9/;
999 0           $input =~ s/([$kanjidigits]+)/kanji2number($1)/ge;
  0            
1000 0           return $input;
1001             }
1002              
1003             1;
1004