じゃじゃ馬ならし

 

官公庁向けでも大丈夫 - 和暦

西暦と和暦

私は普段の生活では和暦を使うことはほとんどなく、ほぼ西暦を使っています。

でも、どうしても和暦を使わざるをえないところがあります。みなさん、よくご存じだと思いますが、官公庁に提出する書類は和暦を使わなくてはいけません。

普段、和暦を使い慣れていないので、「今年は平成何年だったっけ?」なんてことがよくあるわけです。

Java も和暦が求められてきはしたものの、長らく西暦のみの扱いになっていました。

やっと Java SE 6 になって、和暦が採用されたのです。

 

新しいロケール

和暦を表すクラスは JapaneseImperialCalendar クラスです。ところがこのクラスはパッケージプライベートクラスなのです。

じゃ、どうやってオブジェクトを生成すればいいかというと、Calendar#getInstance メソッドを使用します。

ただし、そのまま引数なしの getInstance メソッドをコールしてしまうと、デフォルトロケールが使用されるので、GregorianCalendar オブジェクトが生成されてしまいます。

和暦を扱うためには、それに応じた新しいロケールを使用します。そのロケールが ja_JP_JP です。普通、日本の日本語を表すロケールは ja_JP ですから、そこに新たなバリアント JP が付加された格好です。

サンプルのソースコード ImperialCalendarSample1.java

ロケールを指定して Calendar#getInstance をコールするか、デフォルトロケールに ja_JP_JP をセットして、その後 Calendar#getInstance をコールするのでもどちらでも JapaneseImperialCalendar オブジェクトを取得できます。

        Locale.setDefault(new Locale("ja", "JP", "JP"));
 
        Calendar cal = Calendar.getInstance();
        System.out.println(cal);
 
        DateFormat format = new SimpleDateFormat("GGGGyy年 MMMM d日");
        System.out.println(format.format(new Date()));

ここではデフォルトロケールを設定する方法で記述してみました。こうすると SimpleDateFormat も和暦を扱えるようになります。

さて、ここで問題です。年号は Calendar クラスのフィールドとしては何で表されるでしょうか?

答えは ERA です。

意外ではないですか。GregorianCalendar クラスでは ERA は BC, AD を表します。一方の JapaneseImperialCalendar クラスでは年号を表すわけです。

C:\temp>java ImperialCalendarSample1
java.util.JapaneseImperialCalendar[time=1130850742031,areFieldsSet=true,areAllFi
eldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset
=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayO
fWeek=1,minimalDaysInFirstWeek=1,ERA=4,YEAR=17,MONTH=10,WEEK_OF_YEAR=45,WEEK_OF_
MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=305,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_P
M=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=12,SECOND=22,MILLISECOND=31,ZONE_OFFSET=324000
00,DST_OFFSET=0]
 
平成17年 11月 1日
 
C:\temp>

実行例を見てみると ERA が 4 になっています。ERA と元号の対応は次のようになっています。

元号 ERA の値
明治以前
0
明治
1
大正
2
昭和
3
平成
4

これから ERA が 4 ということは平成だと理解することができます。

注意しなくてはいけないのが、現在の実装だと JapaneseImperialCalendar クラスは明治以後の年号しか扱うことができません。

慶安とかはだめなのです。

とことで、SimpleDateFormat では G が ERA を表すフォーマット記述子です。GGGG だと「平成」と表示されましたが、G だとどうなるでしょう?

答えは 「H」です。明治が M で、大正は T、昭和が S です。

また、これらは DateFormat クラスの getDateInstance/getDateTimeInstance メソッドの引数 style もしくは dateStyle を DateFormat.LONG にしたときと DateFormat.SHORT にも対応しています。

ところで、DateFormat クラスときたら、やっぱり java.util.Formatter クラスでも年号を表示してみたいですよね。

ところが、ないのです。Formatter クラスのフォーマット記述子には ERA に対応する記述子がないのです... orz

それはちょっとないんじゃないかなぁ。Formatter クラスは Tiger から導入されたクラスで新しいんだから、それぐらい忘れないでほしい...

 

西暦と和暦の比較

さて、日時を和暦で表せることは分かったのですが、使いこなすには注意が必要です。

たとえば、ERA の問題。

Calendar オブジェクトとして扱っていると、実際のオブジェクトが Calendar クラスの派生クラスであることを気にしなくなります。オブジェクト指向だから当たり前なのですが、ここに問題が出てきてしまいます。

つまり、派生クラス間で ERA の解釈のしかたが違うということですね。単にセットなどしてしまったら目も当てられません。

つまりは Calendar オブジェクトとして扱っていたとしても、実装クラスが何であるかを意識していなければいけないということです。

ところで、JapaneseImperialCalendar オブジェクトと GregorianCalendar オブジェクトを比較したらどうなるでしょう。同じ時間を表現を変化させて表しているだけなので、時間という本質は変わりません。でも、クラスとしては異なるわけです。

さっそくやってみましょう。

サンプルのソースコード ImperialCalendarSample2.java

このクラスの中では同じ時間を 2 つの Calendar オブジェクトにセットしています。この比較はどうなるでしょう。

        Date date = new Date();
 
        Calendar imperialCal = Calendar.getInstance(new Locale("ja", "JP", "JP"));
        imperialCal.setTime(date);
        System.out.println(imperialCal);
 
        Calendar gregorianCal = Calendar.getInstance();
        gregorianCal.setTime(date);
        System.out.println(gregorianCal);
 
        System.out.println("\n和暦表現 == グレゴリオ歴表現: " + imperialCal.equals(gregorianCal));

実行してみると...

C:\temp>java ImperialCalendarSample2
java.util.JapaneseImperialCalendar[time=1130853849328,areFieldsSet=true,areAllFi
eldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset
=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayO
fWeek=1,minimalDaysInFirstWeek=1,ERA=4,YEAR=17,MONTH=10,WEEK_OF_YEAR=45,WEEK_OF_
MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=305,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_P
M=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=4,SECOND=9,MILLISECOND=328,ZONE_OFFSET=3240000
0,DST_OFFSET=0]
java.util.GregorianCalendar[time=1130853849328,areFieldsSet=true,areAllFieldsSet
=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=324000
00,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayOfWeek=1
,minimalDaysInFirstWeek=1,ERA=1,YEAR=2005,MONTH=10,WEEK_OF_YEAR=45,WEEK_OF_MONTH
=1,DAY_OF_MONTH=1,DAY_OF_YEAR=305,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,H
OUR=11,HOUR_OF_DAY=23,MINUTE=4,SECOND=9,MILLISECOND=328,ZONE_OFFSET=32400000,DST
_OFFSET=0]

和暦表現 == グレゴリオ歴表現: false

2 つの変数 imperialCalendar と gregorianCalendar は、それぞれ JapaneseImperialCalendar オブジェクトと GerogiranCalendar オブジェクトであることは確認できました。

肝心の比較は、やっぱり false になります。equals メソッドの実装を考えればしかたないですね。普通はクラスが違えば、equals で true は返さないですから。

それでは、どうやって比較しましょう?

比較できるものに変換すればいいですね。Calendar クラスの場合は Date クラスか、ミリ秒表記のどちらかが使えると思います。

        System.out.println("ミリ秒換算: "
                           + (imperialCal.getTimeInMillis() == gregorianCal.getTimeInMillis()));
        System.out.println("Date 換算: "
                           + imperialCal.getTime().equals(gregorianCal.getTime()));

こうすれば、比較は true になるはずです。..

ミリ秒換算: true
Date 換算: true

Java SE 6 ではプラガブルロケールなど、今までより簡単にロケールを変更しやすくなっています。ということは Calendar クラスで表されている情報も実際には異なるクラスで現されている可能性があります。

ですから、Calendar オブジェクトの比較や、値の設定などは注意する必要がありそうです。

 

おわりに

和暦を待ち望んでいた人はかなり多いのではないでしょうか。IBM の ICU4J の JapaneseCalendar クラスのように自前で作られている方も多いと思います。

やっと Java SE 6 で和暦が使えるようになったのは、とても喜ばしいことです。

でも、GregorialCalendar クラスとのコンバートができないのがちょっと残念ですけど。

ところで、このクラスを作成されたのは国際化チームの奥津さんです。なぜ分かったかというと、ソースコードに署名がしてあったのです。日本人が JDK のソースコードに署名されているのははじめてみたのでした。

 

(Nov. 2005)