一方、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と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
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.