旧日時APIとの相互変換&Java 6/7でDate-Time APIが使えるライブラリThreeTen Backportここが大変だよJava 8 Date-Time API(終)(2/3 ページ)

» 2015年04月07日 05時00分 公開
[長谷川智之株式会社ビーブレイクシステムズ]

Java 8 Date-Time APIと旧日時APIの変換対応表

 一方、Java 8から追加されたDate-Time APIではタイムラインを表すjava.time.Instantクラスがあり、日付や時刻の情報とタイムゾーンの情報を持つjava.time.ZonedDateTimeクラスがあります。そしてDate-Time APIではタイムゾーンを表すクラスとしてjava.time.ZoneIdクラスが用意されていて、さらにZoneIdを継承したUTCからの時差を表すjava.time.ZoneOffsetクラスが用意されています。

 このことからjava.util.Dateクラスとjava.time.Instantクラス、およびCalendarやGregorianCalendarとZonedDateTimeとの変換が基本になります。そして、タイムゾーンの情報そのものを変換したい場合はjava.util.TimeZoneクラスとjava.time.ZoneIdクラスとの変換を行うことになります。また、ZoneOffsetはZoneIdを継承しているのでZoneIdを受け取るものに対しZoneOffsetを渡すことも可能です。

 これらの変換をまとめたものが下記の表になります

旧日時API Date-Time API 概要
java.util.Date java.time.Instant 両方ともエポックからの経過時間(タイムライン)を情報として持っている。そのため、タイムラインを基に変換を行いたい場合は、DateからInstantへの変換も、InstantからDateへの変換も可能
java.util.Calendar java.tie.Instant 両方ともタイムラインを情報として持っている。そのため、タイムラインを基に変換を行いたい場合は、CalendarからInstantへの変換が可能。しかしInstantからCalendarへ変換するメソッドは用意されていない
java.util.GregorianCalendar java.time.ZonedDateTime 両方ともタイムゾーンの情報および年、月、日、時、分、秒、ミリ秒などの情報を持つことが可能。またZonedDateTimeは内部でInstantの情報も持っている。そのため、GregorianCalendarからZonedDateTimeへの変換も、その逆も可能
java.util.TimeZone java.time.ZoneId
(※TimeZoneへの変換はjava.time.ZoneOffsetも可)
両方ともタイムゾーンのIDを情報として持っている。そのため、タイムゾーンのIDの情報を使って変換するメソッドが用意されている。また、ZoneIdを使ってTimeZoneに変換する場合は、ZoneIdを継承しているZoneOffsetも使うことが可能

 それでは旧日時APIとDate-Time APIとの相互変換が行うには実際どうしたらよいのか見ていきましょう。

旧日時APIに追加された変換メソッド

 旧日時APIとDate-Time APIとでお互いを変換できるように、旧日時APIに変換するためのメソッドが追加されています。それでは変換するために使われるメソッドについて見ていきましょう。

タイムラインを基にした変換

static 戻り値 メソッド 概要
java.util.Date
Instant toInstant() java.util.Dateからjava.time.Instantを生成するメソッド。生成されるInstantは呼び出し元のDateが持つタイムライン(エポックからの経過時間)を持つ
Date from(Instant instant) java.time.Instant からjava.util.Dateを生成するメソッド。生成されるDateは引数のinstantが持つタイムラインを持つ
java.util.Calendar
Instant toInstant() java.util.Calendarからjava.time.Instantを生成するメソッド。生成されるInstantは呼び出し元のCalendarが持つタイムライン(エポックからの経過時間)を持つ

 次の例では、DateとInstantとの相互変換とCalendarからInstantへの変換を行っています。

// Date → Instant
System.out.println("Date → Instant");
 
Date date1 = new Date();
Instant instant1 = date1.toInstant();
 
System.out.println("date1=" + date1);
System.out.println("instant1=" + instant1);
System.out.println("date1.getTime()=" + date1.getTime());
System.out.println("instant1.toEpochMilli()=" + instant1.toEpochMilli());
 
// Instant → Date
System.out.println("Instant → Date");
 
Instant instant2 = Instant.now();
Date date2 = Date.from(instant2);
 
System.out.println("instant2=" + instant2);
System.out.println("date2=" + date2);
System.out.println("instant2.toEpochMilli()=" + instant2.toEpochMilli());
System.out.println("date2.getTime()=" + date2.getTime());
 
// Calendar → Instant
System.out.println("Calendar → Instant");
 
Calendar calendar = Calendar.getInstance();
Instant instant3 = calendar.toInstant();
 
System.out.println("calendar=" + calendar);
System.out.println("instant3=" + instant3);
System.out.println("calendar.getTimeInMillis()=" + calendar.getTimeInMillis());
System.out.println("instant3.toEpochMilli()=" + instant3.toEpochMilli());
サンプル
Date → Instant
date1=Sat Dec 27 10:51:11 JST 2014
instant1=2014-12-27T01:51:11.457Z
date1.getTime()=1419645071457
instant1.toEpochMilli()=1419645071457
Instant → Date
instant2=2014-12-27T01:51:11.816Z
date2=Sat Dec 27 10:51:11 JST 2014
instant2.toEpochMilli()=1419645071816
date2.getTime()=1419645071816
Calendar → Instant
calendar=java.util.GregorianCalendar[time=1419645071816,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2014,MONTH=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=4,DAY_OF_MONTH=27,DAY_OF_YEAR=361,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=10,HOUR_OF_DAY=10,MINUTE=51,SECOND=11,MILLISECOND=816,ZONE_OFFSET=32400000,DST_OFFSET=0]
instant3=2014-12-27T01:51:11.816Z
calendar.getTimeInMillis()=1419645071816
instant3.toEpochMilli()=1419645071816
実行結果

 InstantではないDate-Time APIのクラスのインスタンスからDateへの変換を行いたい場合は、対象のクラスからInstantを生成しDateクラスに変換する必要があります。Date-Time APIの日時を表すクラスはInstantを生成するメソッドを持っています。

 逆にDateからInstantではないDate-Time APIの日時を表すクラスに変換したい場合は、Instantからタイムゾーンの情報などを使って対象のDate-Time APIの日時を表すクラスに変換する必要があります。

タイムゾーンと日時の情報基にした変換

static 戻り値 メソッド 概要
java.util.GregorianCalendar
ZonedDateTime toZonedDateTime() java.util.GregorianCalendarからjava.time.ZonedDateTimeを生成するメソッド。生成されるZonedDateTimeは呼び出し元のGregorianCalendarが持つタイムライン(エポックからの経過時間)を持ち、かつ、年、月、日、時、分、秒、ミリ秒およびタイムゾーンの情報なども持っている
GregorianCalendar from(ZonedDateTime zonedDateTime) java.time.ZonedDateTimeからjava.util.GregorianCalendarを生成するメソッド。生成されるGregorianCalendarは引数のzonedDateTimeが持つInstantのタイムライン(エポックからの経過時間)を持ち、かつ、年、月、日、時、分、秒、ミリ秒およびタイムゾーンの情報なども持っている
// GregorianCalendar → ZonedDateTime
System.out.println("GregorianCalendar → ZonedDateTime");
 
GregorianCalendar calendar2 = (GregorianCalendar) GregorianCalendar.getInstance();
ZonedDateTime zonedDateTime = calendar2.toZonedDateTime();
 
System.out.println("calendar2=" + calendar2);
System.out.println("zonedDateTime=" + zonedDateTime);
 
System.out.println();
 
// ZonedDateTime → GregorianCalendar
System.out.println("ZonedDateTime → GregorianCalendar");
 
GregorianCalendar calendar3 = GregorianCalendar.from(zonedDateTime);
 
System.out.println("zonedDateTime=" + zonedDateTime);
System.out.println("calendar3=" + calendar3);
サンプル
GregorianCalendar → ZonedDateTime
calendar2=java.util.GregorianCalendar[time=1419645363784,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2014,MONTH=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=4,DAY_OF_MONTH=27,DAY_OF_YEAR=361,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=10,HOUR_OF_DAY=10,MINUTE=56,SECOND=3,MILLISECOND=784,ZONE_OFFSET=32400000,DST_OFFSET=0]
zonedDateTime=2014-12-27T10:56:03.784+09:00[Asia/Tokyo]
 
ZonedDateTime → GregorianCalendar
zonedDateTime=2014-12-27T10:56:03.784+09:00[Asia/Tokyo]
calendar3=java.util.GregorianCalendar[time=1419645363784,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2014,MONTH=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=4,DAY_OF_MONTH=27,DAY_OF_YEAR=361,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=10,HOUR_OF_DAY=10,MINUTE=56,SECOND=3,MILLISECOND=784,ZONE_OFFSET=32400000,DST_OFFSET=0]
実行結果

 多くのソースコードでは、Calendarの変数を作る際にその変数の型を、GregorianCalendarなどの実装クラスではなく、Calendarとして定義しているかと思います。その場合、Calendarとして定義されたインスタンスがGregorianCalendarなら、そのCalendarをGregorianCalendarにキャストすることでZonedDateTimeとの変換も可能になります。

タイムゾーンの情報自体の変換

static 戻り値 メソッド 概要
java.util.TimeZone
ZoneId toZoneId() java.util.TimeZoneからjava.time.ZoneIdを生成するメソッド
TimeZone getTimeZone( ZoneId zoneId ) java.time.ZoneIdからjava.util.TimeZoneを生成するメソッド。引数のZoneIdはZoneOffsetの親クラスであるため、ZoneOffsetのインスタンスを設定することも可能

 次の例ではTimeZoneとZoneIdとの相互変換と、ZoneOffsetからTimeZoneへの変換を行っています。

// TimeZone → ZoneId
System.out.println("TimeZone → ZoneId");
 
TimeZone timeZone1 = TimeZone.getTimeZone("America/Los_Angeles");
ZoneId zoneId1 = timeZone1.toZoneId();
 
System.out.println("timeZone1=" + timeZone1);
System.out.println("zoneId1=" + zoneId1);
 
System.out.println();
 
// ZoneId → TimeZone
System.out.println("ZoneId → TimeZone");
 
ZoneId zoneId2 = ZoneId.of("Asia/Tokyo");
TimeZone timeZone2 = TimeZone.getTimeZone(zoneId2);
 
System.out.println("timeZone2=" + timeZone2);
System.out.println("zoneId2=" + zoneId2);
 
System.out.println();
 
// ZoneOffset → TimeZone
System.out.println("ZoneId → TimeZone");
 
ZoneOffset zoneOffset = ZoneOffset.of("+01:00");
TimeZone timeZone4 = TimeZone.getTimeZone(zoneOffset);
 
System.out.println("timeZone4=" + timeZone4);
System.out.println("zoneOffset=" + zoneOffset);
サンプル
TimeZone → ZoneId
timeZone1=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
zoneId1=America/Los_Angeles
ZoneId → TimeZone
timeZone2=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null]
zoneId2=Asia/Tokyo
ZoneId → TimeZone
timeZone4=sun.util.calendar.ZoneInfo[id="GMT+01:00",offset=3600000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
zoneOffset=+01:00
実行結果

java.text.Formatへの変換

 Date-Time APIのDateTimeFormatterは旧日時APIのjava.text.Formatを返すtoFormatメソッドが用意されています。しかし、ここで生成されたFormatのインスタンスを使ってformatメソッドで文字列を生成する場合、引数がDate-Time APIのクラスでないと例外が発生します。toFormatメソッドより生成されたFormatのインスタンスに対して旧日時APIのjava.util.Dateのインスタンスを受け取ることはできません。これは引数にTemporalAccessorを実装したクラスを受け取るように実装したFormatのインスタンスがtoFormatメソッドで生成されるためです。

LocalDate localDate = LocalDate.of(2014, 1, 1);
Instant instant = Instant.from(localDate.atStartOfDay(ZoneId.systemDefault()));
Date date = Date.from(instant);
 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/M/d");
Format format = formatter.toFormat();
System.out.println(format);
System.out.println("format.format(localDate)=" + format.format(localDate));
System.out.println("format.format(date)=" + format.format(date)); // 例外が発生する
サンプル
java.time.format.DateTimeFormatter$ClassicFormat@85ede7b
format.format(localDate)=2014/1/1
Exception in thread "main" java.lang.IllegalArgumentException: Format target must implement TemporalAccessor
	at java.time.format.DateTimeFormatter$ClassicFormat.format(Unknown Source)
	at java.text.Format.format(Unknown Source)
	at jp.co.atmark.datetimeapi.sample02.LegacyDateTimeSample02.main(LegacyDateTimeSample02.java:22)
実行結果

 また、DateTimeFormatterから生成されたFormatのインスタンスを使ってparseメソッドを使って文字列の解析を行うとjava.time.format.Parsedのインスタンスが生成されます。しかし、このParsedに直接アクセスする権限がないため、いったんTemporalAccessorに変換する必要があります。そして、そこから意図したクラスに変換することで、その生成したインスタンスを扱えます。

 toFormatメソッドには関数型インターフェースのTemporalQueryを引数に持つメソッドもあります。その場合、意図したTemporalAccessor のクラスのインスタンスを返すようにTemporalQueryを実装することで、Parsedのインスタンスから変換する処理を記述することなく解析することが可能です。ただしparseObjectの戻り値はObjectのため、戻り値に対してのキャスト(任意の型の指定)は必要になります。

// DateTimeFormatter → Formatへの変換
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/M/d");
Format format = formatter.toFormat();
 
// Formatから文字列の解析
Object parsedObject = format.parseObject("2014/2/2");
System.out.println("parsedObject=" + parsedObject);
System.out.println("parsedObject.getClass()=" + parsedObject.getClass());
 
// Parsed → Date-Time API
TemporalAccessor temporalAccessor = (TemporalAccessor) parsedObject;
LocalDate converted = LocalDate.from(temporalAccessor);
System.out.println("converted=" + converted);
System.out.println("converted.getClass()=" + converted.getClass());
 
// TemporalQueryを使って直接Date-Time APIのクラスに変換
Format format2 = formatter.toFormat(LocalDate::from);
Object parsedObject2 = format2.parseObject("2014/3/3");
System.out.println("parsedObject2=" + parsedObject2);
System.out.println("parsedObject2.getClass()=" + parsedObject2.getClass());
サンプル
parsedObject={},ISO resolved to 2014-02-02
parsedObject.getClass()=class java.time.format.Parsed
converted=2014-02-02
converted.getClass()=class java.time.LocalDate
parsedObject2=2014-03-03
parsedObject2.getClass()=class java.time.LocalDate
実行結果

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。