1.概要

Java 8は、古い

java.util.Date



java.util.Calendar

の欠点を解決するために、

Date



Time

の新しいAPIを導入しました。

この記事の一部として、既存の

Date

および

Calendar

APIの問題から始めて、新しいJava 8

Date

および

Time

APIがそれらにどう対処するかを説明しましょう。

また、

LocalDate



LocalTime、LocalDateTime、ZonedDateTime、Period、Duration

のような

java.time

パッケージの一部である新しいJava 8プロジェクトのコアクラスとそれらがサポートするAPIについても見ていきます。

2.既存の

Date

/

Time

APIに関する問題


  • スレッドセーフ



    Date



    Calendar

    クラスはスレッドではありません

安全なため、開発者は並行性の問題をデバッグするのが難しいという頭痛に対処し、スレッドの安全性を処理する追加のコードを書く必要があります。

それどころか、Java 8で導入された新しい

Date



Time

のAPIは不変でスレッドセーフなので、その同時実行性は開発者の頭を悩ませます。


  • APIの設計と理解の容易さ



    Date



    Calendar

APIは、日常的な操作を実行するための不適切なメソッドを使用して設計されていません。新しい

Date/Time

APIはISO中心で、日付、時刻、期間、期間について一貫したドメインモデルに従います。最も一般的な操作をサポートする多種多様なユーティリティメソッドがあります。



  • ZonedDate



    Time


    – 開発者は追加のロジックを書く必要がありました

古いAPIを使用してタイムゾーンロジックを処理しますが、新しいAPIを使用すると、

Local

および

ZonedDate

/

Time

APIを使用してタイムゾーンを処理できます。

3.

LocalDate



LocalTime

、および

LocalDateTime

を使用する

最も一般的に使用されるクラスは

LocalDate



LocalTime

、および

LocalDateTime

です。それらの名前が示すように、それらはオブザーバのコンテキストからのローカルの日付/時刻を表します。

これらのクラスは主に、タイムゾーンをコンテキスト内で明示的に指定する必要がない場合に使用されます。このセクションの一部として、最も一般的に使用されているAPIについて説明します。


3.1.

LocalDate


を使った作業


LocalDate

は、** 時間なしのISO形式(yyyy-MM-dd)の日付を表します。

誕生日や給料日などの日付を保存するために使用できます。

現在の日付のインスタンスは、以下のようにシステムクロックから作成できます。

LocalDate localDate = LocalDate.now();

特定の日、月、年を表す

LocalDate

は、“

of

”メソッドまたは“

parse

”メソッドを使用して取得できます。たとえば、以下のコードスニペットは2015年2月20日の

LocalDate

を表します。

LocalDate.of(2015, 02, 20);

LocalDate.parse("2015-02-20");


LocalDate

は、さまざまな情報を取得するためのさまざまなユーティリティメソッドを提供します。これらのAPIメソッドのいくつかをちょっと見てみましょう。

次のコードスニペットは、現在の現地日付を取得して1日加算します。

LocalDate tomorrow = LocalDate.now().plusDays(1);

この例では、現在の日付を取得して1か月引きます。時間単位として

enum

をどのように受け入れるかに注意してください。

LocalDate previousMonthSameDay = LocalDate.now().minus(1, ChronoUnit.MONTHS);

次の2つのコード例では、日付「2016-06-12」を解析して、それぞれ曜日と月の日を取得します。戻り値に注意してください。最初のオブジェクトは

DayOfWeek

を表すオブジェクトで、2番目のオブジェクトは月の序数値を表します。

DayOfWeek sunday = LocalDate.parse("2016-06-12").getDayOfWeek();

int twelve = LocalDate.parse("2016-06-12").getDayOfMonth();

うるう年に日付があるかどうかをテストできます。この例では、現在の日付がうるう年かどうかをテストします。

boolean leapYear = LocalDate.now().isLeapYear();

ある日付と別の日付の関係は、別の日付の前後に発生すると判断できます。

boolean notBefore = LocalDate.parse("2016-06-12")
  .isBefore(LocalDate.parse("2016-06-11"));

boolean isAfter = LocalDate.parse("2016-06-12")
  .isAfter(LocalDate.parse("2016-06-11"));

日付の境界は特定の日付から取得できます。次の2つの例では、特定の日付の1日の始まり(2016-06-12T00:00)を表す

LocalDateTime

と、月の始まり(2016-06-01)を表す

LocalDate

がそれぞれ得られます。

LocalDateTime beginningOfDay = LocalDate.parse("2016-06-12").atStartOfDay();
LocalDate firstDayOfMonth = LocalDate.parse("2016-06-12")
  .with(TemporalAdjusters.firstDayOfMonth());

それでは、現地時間とどのように連携しているかを見てみましょう。


3.2.

LocalTime


を使った作業


LocalTime



日付のない時間

を表します。


LocalDate

と同様に、

LocalTime

のインスタンスは、システムクロックから、または「parse」および「of」メソッドを使用して作成できます。以下でよく使われるAPIのいくつかを簡単に見てください。

現在の

LocalTime

のインスタンスは、以下のようにシステムクロックから作成できます。

LocalTime now = LocalTime.now();

以下のコードサンプル

__では、文字列表現を解析して、午前6時30分を表す

LocalTime__を作成します。

LocalTime sixThirty = LocalTime.parse("06:30");

Factoryメソッドのofを使用して

LocalTime

を作成できます。たとえば、次のコードはファクトリメソッドを使って午前6時30分を表す

LocalTime

を作成します。

LocalTime sixThirty = LocalTime.of(6, 30);

以下の例では、文字列を解析して「LocalTime」を作成し、「plus」APIを使用して1時間を追加します。結果は午前7時30分を表す

LocalTime

になります。

LocalTime sevenThirty = LocalTime.parse("06:30").plus(1, ChronoUnit.HOURS);

以下のように時、分、秒などの特定の時間単位を取得するために使用できるさまざまな取得メソッドがあります。

int six = LocalTime.parse("06:30").getHour();

特定の時間が別の特定の時間の前後にあるかどうかも確認できます。以下のコード例は、結果が真となる2つの

LocalTime

を比較しています。

boolean isbefore = LocalTime.parse("06:30").isBefore(LocalTime.parse("07:30"));

一日の最大、最小、正午の時間は

LocalTime

クラスの定数で取得できます。これは、データベースクエリを実行して特定の期間内にレコードを検索するときに非常に便利です。たとえば、次のコードは23:59:59.99を表します。

LocalTime maxTime = LocalTime.MAX

それでは、

LocalDateTime

に飛び込みましょう。


3.3.

LocalDateTime


を使った作業


LocalDateTime

は、

日付と時刻の組み合わせ

を表すために使用されます。

これは、日付と時刻の組み合わせが必要なときに最も一般的に使用されるクラスです。このクラスはさまざまなAPIを提供しています。最も一般的に使用されているものをいくつか見ていきます。


LocalDateTime

のインスタンスは、

LocalDate

および__LocalTimeと同様に、システムクロックから取得できます。

LocalDateTime.now();

以下のコードサンプルは、ファクトリの “of”メソッドと “parse”メソッドを使ってインスタンスを作成する方法を説明しています。結果は、2015年2月20日午前6時30分を表す

LocalDateTime

インスタンスになります。

LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);

LocalDateTime.parse("2015-02-20T06:30:00");

日、月、年、分などの特定の時間単位の加算と減算をサポートするユーティリティAPIがあります。以下のコードサンプルは、「プラス」および「マイナス」メソッドの使用法を示しています。

これらのAPIは、

LocalDate

および__LocalTimeの対応するAPIとまったく同じように動作します。

localDateTime.plusDays(1);

localDateTime.minusHours(2);

ゲッターメソッドは、日付と時刻のクラスに似た特定の単位を抽出するために利用可能です。上記の

LocalDateTime

のインスタンスを考えると、次のコードサンプルは2月を返します。

localDateTime.getMonth();

4.

ZonedDateTime

APIを使用する

このコードスニペットでは、Paris用に

Zone

を作成します。

ZoneId zoneId = ZoneId.of("Europe/Paris");

以下のように、すべてのゾーンIDのセットを取得できます。

Set<String> allZoneIds = ZoneId.getAvailableZoneIds();


LocalDateTime

は特定のゾーンに変換できます。

ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);


ZonedDateTime

は、タイムゾーン固有の日時を取得するための

parse

メソッドを提供します。

ZonedDateTime.parse("2015-05-03T10:15:30+01:00[Europe/Paris]");

タイムゾーンを操作するもう1つの方法は、

OffsetDateTime

を使用することです。

OffsetDateTime

は、オフセット付きの日時の不変表現です。このクラスは、UTC/グリニッジからのオフセットと同様に、すべての日付と時刻フィールドをナノ秒の精度で格納します。


OffSetDateTime

インスタンスは、

ZoneOffset

を使用して以下のように作成できます。ここで、2015年2月20日の午前6時30分を表す

LocalDateTime

を作成します。

LocalDateTime localDateTime = LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);

次に、

ZoneOffset

を作成し、

localDateTime

インスタンスを設定して、時間を2時間追加します。

ZoneOffset offset = ZoneOffset.of("+02:00");

OffsetDateTime offSetByTwo = OffsetDateTime
  .of(localDateTime, offset);

現在、

localDateTime

は2015-02-20 06:30 02:00です。それでは、

Period

クラスと

Duration

クラスを使って日付と時刻の値を変更する方法に進みましょう。

=== 5.

Period



Duration

を使う


Period

クラスは年数、月数、日数で時間量を表し、

Duration

クラスは秒数とナノ秒数で時間量を表します。

====

5.1.

Period


を使った作業


Period

クラスは、与えられた日付の値を修正したり、2つの日付間の差を得るために広く使われています。

LocalDate initialDate = LocalDate.parse("2007-05-10");

次のコードスニペットに示すように、

Date



Period

を使用して操作できます。

LocalDate finalDate = initialDate.plus(Period.ofDays(5));


Period

クラスには、

Period

オブジェクトから値を取得するための

getYears、getMonths



getDays

などのさまざまな取得メソッドがあります。以下のコード例は、日数の差を求めるため、

int

の値5を返します。

__

int five = Period.between(finalDate, initialDate).getDays();

2つの日付間の

Period

は、__ChronoUnit.betweenを使用して、日、月、年などの特定の単位で取得できます。

int five = ChronoUnit.DAYS.between(finalDate , initialDate);

このコード例は5日を返します。

Duration

クラスを見てみましょう。

====

5.2.

Duration


を使った作業


Periodと同様に、

Durationクラスは

Timeを処理するために使用されます。次のコードでは、6:30 amの

LocalTime

を作成し、それから06:30:30 amの

LocalTime__を作成するために30秒の期間を追加します。

LocalTime initialTime = LocalTime.of(6, 30, 0);

LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30));

2つの瞬間の間の期間は、期間または特定の単位として取得できます。最初のコードスニペットでは、

Duration

クラスの

between()

メソッドを使用して

finalTime



initialTime

の時間差を見つけ、その差を秒単位で返します。

int thirty = Duration.between(finalTime, initialTime).getSeconds();

2番目の例では、

ChronoUnit

クラスの

between()

メソッドを使用して同じ操作を実行します。

int thirty = ChronoUnit.SECONDS.between(finalTime, initialTime);

今度は、既存の

Date



Calendar

を新しい

Date

/

Time.

に変換する方法を見ていきます。

=== 6.

Date

および

Calendar

との互換性

Java 8では、次のコードスニペットのように、既存の

Date

および

Calendar

インスタンスを新しいDate Time APIに変換するのに役立つ

toInstant()

メソッドが追加されました。

LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());


LocalDateTime

は、以下のようにエポック秒から構築できます。以下のコードの結果は、2016-06-13T11:34:50を表す

LocalDateTime

になります。

LocalDateTime.ofEpochSecond(1465817690, 0, ZoneOffset.UTC);

それでは

Date



Time

のフォーマットに移りましょう。

=== 7.

日付と

時間のフォーマット

Java 8は

Date



Time

の簡単なフォーマットのためのAPIを提供します

LocalDateTime localDateTime = LocalDateTime.of(2015, Month.JANUARY, 25, 6, 30);

以下のコードは、ローカル日付をフォーマットするためにISO日付フォーマットを渡します。結果は2015-01-25になります。

String localDateString = localDateTime.format(DateTimeFormatter.ISO__DATE);


DateTimeFormatter

は、さまざまな標準フォーマットオプションを提供します。

以下のように、カスタムパターンをformatメソッドにも提供できます。これは、2015/01/25として

LocalDate

を返します。

localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));

フォーマットオプションの一部として、

SHORT



LONG

、または

MEDIUM

のいずれかとしてフォーマットスタイルを渡すことができます。以下のコードサンプルは、2015年1月25日06:30:00に

LocalDateTime

を表す出力を生成します。

localDateTime
  .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
  .withLocale(Locale.UK);

Java 8 Core

Date

/__ Time APIに利用可能な代替手段を見てみましょう。

=== 8.バックポートと代替オプション

====

8.1. プロジェクトThreetenの使用

Java 7またはJava 6からJava 8に移行する予定で、日時APIを使用したい組織のために、プロジェクトhttp://www.threeten.org/[threeten]がバックポート機能を提供しています。

開発者は、このプロジェクトで利用可能なクラスを使用して、新しいJava 8

Date

および

Time

APIと同じ機能を実現し、Java 8に移行したらパッケージを切り替えることができます。プロジェクトthreetenの成果物は、http://mvnrepository.com/artifact/org.threeten/threetenbp[maven中央レポジトリ]にあります。

<dependency>
    <groupId>org.threeten</groupId>
    <artifactId>threetenbp</artifactId>
    <version>1.3.1</version>
</dependency>

====

8.2. 上田タイムライブラリー

Java 8の

Date

および

Time

ライブラリのもう1つの代替方法はhttp://www.joda.org/joda-time/[Joda-Time]ライブラリです。実際、Java 8 Date Time APIは、Joda-Timeライブラリ(Stephen Colebourne)とOracleの作者によって共同で主導されてきました。このライブラリは、Java 8 Date Timeプロジェクトでサポートされているほとんどすべての機能を提供します。成果物は、プロジェクトに以下のpom依存関係を含めることで、http://mvnrepository.com/artifact/joda-time/joda-time[maven central]にあります。

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.4</version>
</dependency>

===

9結論

Java 8は、開発を容易にするための一貫したAPI設計を備えた豊富なAPIセットを提供します。

上記の記事のサンプルコードはhttps://github.com/eugenp/tutorials/tree/master/java-dates[Java 8 Date/Time]gitリポジトリにあります。