0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

java.util.Date使い向けのDateTimeFormatter簡単使い方

Last updated at Posted at 2021-02-01

この記事は、昔からJavaのプログラムを書いていて、java.util.Datejava.util.Calendarでの日付処理に慣れ親しんでいる人が、「DateTimeFormatterはスレッドセーフなのでSimpleDateFormatより使いやすそう。でも今のコードでどう使えばいいの?」というときのヘルプになればと思います。

Javaでの日時処理

Javaでの旧世代の日時処理と言えばDateクラスとCalendarクラスです。DateクラスはJavaの一番最初期JDK1.0のころからある日時のクラス。Calendarクラスはその直後Javaの国際化の中で導入された日時のクラスです。プログラムではDateクラスは日時の入れ物に、Calendarクラスをタイムゾーン処理や日時演算に使うという使い方が多かったのではないでしょうか。
一方、Java8で新しい日時処理のクラスとして、java.timeパッケージが導入され日時の概念や処理が整理され詳細化されました。日時を文字列変換して出力するためにはDateTimeFormatterクラスが導入され、これまでSimpleDateFormatterの悩みだった「マルチスレッドで使えない」「インスタンス毎回作ると遅い」などの不満が解決されたものになっています。

DateTimeFormatterで日時出力

前置きはこれくらいにしておいて、DateTimeFormatterクラスで日付を整形して出力するにはDateTimeFormatter.formatter(TemporalAccessor)を使います。Dateクラスをこの引数にどう合わせるか、ですが、Date.toInstant()というメソッドを使うと変換ができるようになっています。DateTimeFormatterにはあらかじめ定数としてよく使いそうな書式が定義されています。例えばISO-8601の書式で出力するには次のコードで出力できます。

iso_date.java
import java.util.Date;
import java.time.format.DateTimeFormatter;

// java.util.Dateの現在時刻をISO-8601形式(UTC)で表示
Date now = new Date();
System.out.println(DateTimeFormatter.ISO_INSTANT.format(now.toInstant()));

出力例

2021-02-01T02:33:29.291Z

ただ、他の書式、例えば RFC1123形式の Mon, 01 Feb 2021 15:00:00 +0900 で表示をするには同じコードではNGで例外が発生します。

rfc_date.java
Date now = new Date();
System.out.println(DateTimeFormatter.RFC_1123_DATE_TIME.format(now.toInstant()));

発生した例外

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth
	at java.time.Instant.getLong(Instant.java:603)
	at java.time.format.DateTimePrintContext$1.getLong(DateTimePrintContext.java:205)
	at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
	at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2551)
	at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2190)
	at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
	at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)

この書式では時刻のオフセット、すなわちタイムゾーン情報を設定することが必要です。Dateクラスにはタイムゾーンの概念が無いのでこうなるのですかね。エラーメッセージはちょっとわかりにくいところです。

Date.toInstant()で得られたオブジェクトにatZoneメソッドでタイムゾーン情報を設定すればこの書式で表示ができるようになります。ZoneId.systemDefault()はシステムの既定のタイムゾーンを取得します。

rfc_date.java
Date now = new Date();
System.out.println(DateTimeFormatter.RFC_1123_DATE_TIME.format(now.toInstant().atZone(ZoneId.systemDefault())));

出力例

Mon, 1 Feb 2021 11:34:30 +0900

タイムゾーンを指定したい場合は以下のように指定ができます。

rfc_date_UTC.java
System.out.println(DateTimeFormatter.RFC_1123_DATE_TIME.format(now.toInstant().atZone(ZoneId.of("UTC"))));

出力例

Mon, 1 Feb 2021 02:34:30 GMT

おわりに

過去に作ったコードや各種ライブラリなどDateクラスは未だ現役で使われる場面も多いので、「内部データとしてDateは使わざるを得ない。でも表示処理など周辺は楽に書きたい」という場合にお役に立てば幸いです。
ただ、これからの日付処理ではjava.timeパッケージの日時クラスを使うのが良いと思うので、乱用はバッドノウハウになるかもしれません。十分ご注意ください。(かくいう私も結構つかってしまっています…)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?