今月からJava SE 6における国際化の新機能を紹介していきます。

その第1弾は和暦です。今週は和暦を表すカレンダクラスを紹介し、来週和暦のフォーマットとパースについて紹介します。

Javaで日付を表すには、みなさんご存知の通り、java.uti.Calendarクラスを使います。Calendarクラスはアブストラクトクラスで、staticなgetInstanceメソッドでCalendarオブジェクトを取得して使用します。

一般に使われるグレゴリオ暦はjava.util.GregorianCalendarクラスで表されます。GregorianCalendarクラスはGregorianという名前がついてますが、グレゴリオ暦だけでなく、グレゴリオ暦が使われる前に使われていたユリウス暦も扱うことができます。

さて、日本ではもう一つの暦、和暦が使われています。この和暦を表すのがJava SE 6で導入されたJapaneseImperialCalendarクラスです。

ところが、Java SE 6のJavadocにはこのJapaneseImperialCalendarクラスはのっていません。というのも、JapaneseImperialCalendarクラスはパッケージプライベートなクラスだからなのです。

では、どのように使えばいいのでしょうか。

答えはロケールにあります。

ロケールja_JP_JPを引数としてCalendar.getInstanceメソッドをコールすると、JapaneseImperialCalendarオブジェクトを取得できます。

サンプルのソース ImperialCalendarSample1.java

このサンプルはデフォルトロケールとロケールja_JP_JPを用いてカレンダを取得しているだけです。日本語環境で実行すると、デフォルトロケールはja_JPになります。

    public ImperialCalendarSample1() {
        // デフォルトロケールを使用してカレンダ取得
        Calendar cal = Calendar.getInstance();
        System.out.println("デフォルトロケール ja_JP");
        System.out.println(cal);

        // ロケール ja_JP_JP を使用してカレンダ取得
        Locale locale = new Locale("ja", "JP", "JP");
        cal = Calendar.getInstance(locale);
        System.out.println("ロケール ja_JP_JP");
        System.out.println(cal);
    }

実行結果を次に示します。

C:\temp>java ImperialCalendarSample1
デフォルトロケール ja_JP
java.util.GregorianCalendar[time=1193542836750,areFieldsSet=true,
areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo
[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,t
ransitions=10,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstW
eek=1,ERA=1,YEAR=2007,MONTH=9,WEEK_OF_YEAR=44,WEEK_OF_MONTH=5,DAY
_OF_MONTH=28,DAY_OF_YEAR=301,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=4
,AM_PM=1,HOUR=0,HOUR_OF_DAY=12,MINUTE=40,SECOND=36,MILLISECOND=75
0,ZONE_OFFSET=32400000,DS
T_OFFSET=0]
ロケール ja_JP_JP
java.util.JapaneseImperialCalendar[time=1193542836765,areFieldsSe
t=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.Z
oneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=
false,transitions=10,lastRule=null],firstDayOfWeek=1,minimalDaysI
nFirstWeek=1,ERA=4,YEAR=19,MONTH=9,WEEK_OF_YEAR=44,WEEK_OF_MONTH=
5,DAY_OF_MONTH=28,DAY_OF_YEAR=301,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MO
NTH=4,AM_PM=1,HOUR=0,HOUR_OF_DAY=12,MINUTE=40,SECOND=36,MILLISECO
ND=765,ZONE_OFFSET=32400000,DST_OFFSET=0]

CalenarクラスをtoStringメソッドで文字列表現にした場合、クラス名がはじめに表示されます。デフォルトロケールではGregorianCalendarクラス、ロケールja_JP_JPではJapaneseImperialCalendarになっていることが確認できます。

その他の部分で注目していただきたいのはYEARとERAです。

デフォルトロケールではYEARが2007になっているのに対し、ロケールja_JP_JPではYEARが19になっています。つまり、前者が西暦2007年を表しており、後者が平成19年を表していることが分かります。

GregorianCalendarクラスを使っている場合、ERAはほとんど意識することはありません。というのも、ERAは紀元前(BC)、紀元(AD)を表すために使うためです。紀元前の日時を使用することはほとんどないですよね。

デフォルトロケールではERAは1になっています。この値はADを表しています。ところが、ロケールがja_JP_JPの場合、ERAが4になっています。これはどういうことなのでしょう?

JapaneseImperialCalendarクラスではERAは紀元前、紀元を表しているのではなく、元号を表しています。

元号とERAの値の対応を表1に示します。

表1 ERAと元号の対応
ERA 元号
0 明治以前
1 明治
2 大正
3 昭和
4 平成

表1からERAが4は平成を表していることが分かります。

実際には、JapaneseImperialCalednarクラスではMEIJIやHEISEIといった定数が定義してあります。しかし、これらの定数を使うことはできません。もちろん、JapaneseImperialCalendarクラスがパッケージプライベートなクラスのためです。

GregorianCalendarクラスには紀元前、紀元を表すBC、ADという定数が定義されています。もちろん、GregorianCalendarクラスはパブリックなクラスなので、これらの定数を使うことができます。

しかし、パッケージプライベートなJapaneseImperialCalendarクラスはCalendarクラスとしてしか扱うことができないため、定数を定義してあってもそれを使用することはできません。

こうやって見ていると、パッケージプライベートにする利点がほとんどないような気もするのですが、なぜパッケージプライベートになったんでしょうね?

太陽暦と太陰暦

JapaneseImperialCalendarクラスのERAは明治以前はすべて0ということになっています。つまり、慶安とか享保などの明治以前の元号はあつかえないのです。

これはなぜなのでしょう?

答えはすぐにお分かりですね。そう、江戸時代まで使われていた暦は、現在使われている太陽暦ではなく、太陰暦(正確には太陰太陽暦) だからです。

太陰暦は月の満ち欠けが基準になっていますが、地球の公転の周期と合わせるために、3年に一度閏月が設けられるなど、とても複雑になっています。江戸時代の人々は、逆にこの複雑な暦でさえ遊びの要素を取りいれ、絵暦や大小暦など様々なカレンダが作られていたようです。

それはさておき、このような暦をコンピュータで扱うのはなかなか大変です。また、元号のはじめと終わりをすべて保持する必要もあります。

このような理由から、JapaneseImperialCalendarでは明治以後の太陽暦だけを扱うようになったようです。

ところで、実際に太陰暦から太陽暦に変更されたのがいつかご存じですか?

答えは明治5年です。

太陰暦の明治5年12月3日を太陽暦の明治6年1月1日と定めたのです。

実をいうと、JapaneseImperialCalendarクラスはこの頃の暦を正確に表していません。つまり、明治はすべて太陽暦として表されています。

試しに明治元年1月1日を西暦で表示してみましょう。

サンプルのソース ImperialCalendarSample2.java

このサンプルでは、JapaneseImperialCalendarクラスを使って、ERAを1、つまり明治にセットしています。その後、元年1月1日にセットします。

そして、それをjava.text.SimpleDateFormatクラスを使用して、西暦で年月日を表示しています。

    public ImperialCalendarSample2() {
        Locale locale = new Locale("ja", "JP", "JP");
        Calendar imperialCalendar = Calendar.getInstance(locale);
 
        // 明治元年1月1日にセットする
        imperialCalendar.set(Calendar.ERA, 1);
        imperialCalendar.set(1, Calendar.JANUARY, 1);
 
        // 西暦で表示
        Date date = imperialCalendar.getTime();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(format.format(date));
    }

これを実行した結果を次に示します。

C:\temp>java ImperialCalendarSample2
1868-01-01

西暦1868年1月1日が明治元年1月1日と示されてしまいました。

ところが、実際には明治元年1月1日は、西暦1968年1月25日に決められています。つまり、JapaneseImperialCalendarクラスは太陽暦しか扱えないため、明治の太陰暦を使用していた期間は実際の月日とは異なります。

JapaneseImperialCalendarで明治の日時を扱う場合は注意が必要ですね。

 

実際に改元したのは慶応4年9月8日(西暦1968年10月23日)ですが、法律的には1月1日に遡って適用されています。

 

【コラム】昭和はいつから始まったのか

元号は元号法で開始から終了まで厳密に決められています。ところが、昭和の始まりだけはあいまいなようです。

大正天皇が崩御なされたのは1926年12月25日ですが、昭和への改元は崩御なされた12月25日からという場合と、次の日である12月26日からという場合の両方が使用されています。12月25日を改元日とする場合が一般的なようですが、12月26日でも間違いではないのだそうです。

どちらも間違いではないというところが、コンピュータで扱いにくいところですね。

ちなみにSunのJava SE 6u2からは1926年12月25日が昭和の改元日になっています。

 

著者紹介 櫻庭祐一

横河電機 ネットワーク開発センタ所属。Java in the Box 主筆

今月の櫻庭

早いもので、Java SE 6完全攻略も50回を過ぎてしまいました。昨年の10月から続けているので、すでに1年以上Java SE 6の紹介をしているわけです。

ところが、まだまだ紹介していない機能がいっぱいあります。いったいいつになったら完全攻略が終わるんでしょうね? 筆者にも分かりません(笑)。

終わったときには、すでにJava SE 7がリリースしていたなんてことだけは避けようと思っていますが、さてどうなることやら。

なお、今月の記事はサン・マイクロシステムズ奥津正義氏および神谷結花氏に多大なるご協力をいただきました。この場を借りてお礼を述べさせていただきます。