motoSEのブログ

SEの徒然備忘録&意思表示

Java8の日付

Java8になってLocalDate, LocalTime, LocalDateTimeクラスが追加されましたね。 おかげで面倒だった日付の操作が一気に簡単になったので紹介したいと思います。

現在の時間

LocalDateTime ldt = LocalDateTime.now();

これで簡単に取得できます。

日付を数値で指定してインスタンスを作成する

年、月、日付、時間、分、秒、ナノ秒それぞれ数値で指定してインスタンスを作成できます。

LocalDateTime ldt = LocalDateTime.of(2017, 12, 10, 23, 59, 59, 1);

当然、月の場合は13以上、日付はその月に存在しない日を指定したらエラーとなります。

LocalDateTime.of(2017, 13, 10, 23, 59, 59, 1); // ⇒ java.time.DateTimeException
LocalDateTime.of(2017, 12, 32, 23, 59, 59, 1); // ⇒ java.time.DateTimeException
LocalDateTime.of(2017, 12, 10, 24, 59, 59, 1); // ⇒ java.time.DateTimeException

日付を文字で指定してインスタンスを作成する

この機能が個人的に一番うれしいです。今までよりずっと楽にパースできるようになりました。 まずは、DateTimeFormatterクラスを使用して、パースしたい日付のフォーマット、ロケール等を指定してフォーマッターを作成します。

DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss", Locale.JAPANESE);

作成したフォーマッターを使用して、日付文字列をパースします。

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss", Locale.JAPANESE);
LocalDateTime ldt = LocalDateTime.parse("2017/10/15 13:53:15", dtf);

当然、存在しない日付を指定するとエラーとなります。

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss", Locale.JAPANESE);
LocalDateTime ldt = LocalDateTime.parse("2017/10/32 13:53:15", dtf);
// ⇒  java.time.DateTimeException: Invalid value for DayOfMonth

これは、Datetimeformatterがもつリゾルバ・スタイルという属性により制御されています。 リゾルバ・スタイルとは、日時文字列を解析した際の値が範囲外(10月なら32日以降)の場合に、それを解決する方法を決定する属性です。デフォルトがSMART(賢い)で、他にLENIENT(寛大)、STRICT(厳密)があります。 以下、各設定における動作です。

  • SMART(賢い):

明らかに許容範囲を超えている場合はエラーになる。例えば13月や32日は絶対にありえないのでエラー。 31日より少ない月では、31以下で本来ありえない日を指定すると、その月の末日になる。

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")
        .withResolverStyle(ResolverStyle.SMART);
LocalDateTime ldt = LocalDateTime.parse("2017/11/31 23:16:10", dtf);
// ⇒ 2017/11/30T23:16:10

このように31で指定しても30に丸み込まれる ちなみに、日付以外の項目が限界値を超えてもエラーとなる。 23時ではなく24時を指定してもエラー。60分を指定してもエラー。

  • LENIENT(寛大):

許容範囲を超えている場合は、本来の月末日を超えて、超過分を加算した日付になる。

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")
        .withResolverStyle(ResolverStyle.LENIENT);
LocalDateTime ldt = LocalDateTime.parse("2017/11/33 23:16:10", dtf);
// ⇒ 2017/12/03T23:16:10
LocalDateTime ldt = LocalDateTime.parse("2017/13/33 24:61:61", dtf);
// ⇒ 2018-02-03T01:02:01
  • STRICT(厳密):

その日時に許されないものは全てエラーになる。 書式「yyyy」は、厳密には暦(ERA。和暦の場合は昭和とか平成とか)に対する年なので、ERAが指定されていない場合はエラーになる。 書式「uuuu」はERAと無関係な年を表す。

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd G HH:mm:ss")
        .withLocale(Locale.ENGLISH)
        .withResolverStyle(ResolverStyle.STRICT);
LocalDateTime ldt = LocalDateTime.parse("2017/11/30 AD 23:16:10", dtf);
// ⇒ 2017/11/30T23:16:10

DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("uuuu/MM/dd HH:mm:ss")
        .withResolverStyle(ResolverStyle.STRICT);
LocalDateTime ldt2 = LocalDateTime.parse("2017/11/30 23:16:10", dtf2);

英語の月のパース

ロケールを変更するだけでできます。

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MMM/dd HH:mm:ss", Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse("2017/Oct/15 13:53:15", dtf);

ただ、この場合、月を「oct」のように小文字にするとエラーとなってしまいます。 この場合どうするか。 ちょっとわからないです。また、調べてから載せます。

計算方法

LocalDateは、日付の加算、減算が非常に簡単にできるようになっています。 みたとおりの機能なので、説明は省きます。

LocalDateTime ldt = LocalDateTime.now();
ldt.plusYears(1);
ldt.plusMonths(1);
ldt.plusDays(1);
ldt.plusHours(1);
ldt.plusMinutes(1);
ldt.plusSeconds(1);
ldt.minusYears(1);
ldt.minusMonths(1);
ldt.minusDays(1);
ldt.minusHours(1);
ldt.minusMinutes(1);
ldt.minusSeconds(1);

こんな感じでしょうか。また、使用することがあれば、追加していきます。