web-dev-qa-db-ja.com

Java 8エポックミリスのタイムスタンプからフォーマットされた日付まで、どのように?

Java-8の前は、エポックからミリ秒単位で関連する日付/時刻を常に保持し、UIやログファイル、またはユーザー生成入力を解析するときに、人間が読み取れる日付/時刻のみを処理することに慣れていました。

これはJava-8でも安全だと思います。そして今、ミリ秒のタイムスタンプからフォーマットされた日付を取得するための最も簡潔な方法を探しています。私は試した

_df = Dateformatter.ofPattern("...pattern...");
df.format(Instant.ofEpochMilli(timestamp))
_

しかし、それは私が半分理解しているInstant.getLong(...)の_Unsupported field: YearOfEra_で爆撃します。では、Instantの代わりに何を使用すればよいですか?

LocalDateTime.ofEpoch(Instant, ZoneId)は間違っているようです。現地時間を気にしないからです。フォーマッタを適用するときに、現地のタイムゾーンを確認したいだけです。内部的には、Instantだけである必要があります。

同じことがZonedDateTime.ofInstant(Instant, ZoneId)にも当てはまります。私は、フォーマットするときにのみZoneIdを適用することを考えました。しかし、DateTimeFormatter自体がタイムゾーンを処理しなくなったことに気付いたので、上記のいずれかを使用する必要があると思います。

どちらが好ましいのか、そしてその理由は?または、エポックミリスのタイムスタンプをタイムゾーン付きの日付/時刻としてフォーマットするために、さらに別の方法を使用する必要がありますか?

8
Harald

Instantにはタイムゾーンに関する情報が含まれておらず、他の場所とは異なり、デフォルトのタイムゾーンは自動的に使用されません。そのため、フォーマッタは年が何であるかを把握できないため、エラーメッセージが表示されます。

したがって、インスタントをフォーマットするには、タイムゾーンを追加する必要があります。これは、 withZone(ZoneId) を使用してフォーマッターに直接追加できます-手動でZonedDateTimeに変換する必要はありません*:

_ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter df = DateTimeFormatter.ofPattern("...pattern...").withZone(zone);
df.format(Instant.ofEpochMilli(timestamp))
_

*残念ながら、初期のJava 8バージョンでは、DateTimeformatter.withZone(ZoneId)メソッドは機能しませんでしたが、現在は修正されているため、上記のコードが機能しない場合は、にアップグレードしてください。最新のJava 8パッチリリース。

編集:追加するだけで、Instantは、他のコンテキストなしで瞬間を保存したいときに使用する適切なクラスです。

9
JodaStephen

年または他のフィールドで構築されたフォーマッターを使用してInstantをフォーマットするときに発生するエラーが予想されます。 Instantは、それがどの年、月、または日であるかを認識せず、エポックから経過したミリ秒数のみを認識します。同じ瞬間に、それは地球の2つの異なる場所で2つの異なる日になる可能性があります。

したがって、日を印刷する場合は、タイムゾーン情報を追加する必要があります。 Instantを使用すると、 atZone(zone) を呼び出して ZoneId と組み合わせて-を形成できます。 ZonedDateTime 。これはインスタントに非常によく似ていますが、タイムゾーン情報があるだけです。システムタイムゾーン(実行中のVMの1つ)を使用する場合は、 ZoneId.systemDefault() で取得できます。

印刷するには、2つの組み込みフォーマッタ ISO_OFFSET_DATE_TIME または ISO_ZONED_DATE_TIME を使用できます。 2つの違いは、ゾーン化された日時フォーマッタがゾーンIDを出力に追加することです。

Instant instant = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(formatter.format(instant.atZone(ZoneId.systemDefault())));
System.out.println(formatter.format(instant.atZone(ZoneId.of("America/Los_Angeles"))));

"Europe/Paris"のシステムタイムゾーンを持つ私のマシンで実行すると、次のようになります。

2016-07-31T18:58:54.108+02:00
2016-07-31T09:58:54.108-07:00

もちろん、自分に合わない場合は、 ofPattern またはビルダー DateTimeFormatterBuilder を使用して、独自のフォーマッターを作成できます。

7
Tunaki

特に前任者のJodaDateTimeと比較した場合、これはやや混乱することに同意します。

最も紛らわしいのは、LocalDateTimeのドキュメントに「タイムゾーンのない日時」と記載されているにもかかわらず、LocalDateTime.ofInstantメソッドはパラメーターとしてインスタントとタイムゾーンの両方を使用することです。

とはいえ、UTCタイムゾーンを使用してInstantとLocalDateTime.ofInstantを使用することで、目的を達成できると思います。

public LocalDateTime millisToDateTime(long millis) {
  return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of("Z");
} 
1
GreyBeardedGeek