web-dev-qa-db-ja.com

Java SE 8 TemporalAccessor.fromの問題をJava.time.Instantオブジェクトと共に使用した場合

_Java.time_には、タイムライン上の位置(または「モーメント」)をカプセル化する Instant クラスがあります。これは秒/ナノ秒の値であるため、タイムゾーンやタイムオフセットに直接関係しないことを理解していますが、そのtoStringは2014-05-13T20:05などのUTC日付/時刻としてフォーマットされた日付と時刻を返します:08.556Z。また、anInstant.atZone(zone)anInstant.atOffset(offset)はどちらも、Instantが暗黙のUTCタイムゾーン/「ゼロ」オフセットを持つものとして扱うことと整合性のある値を生成します。

したがって、私は期待していたでしょう:

  • ZoneOffset.from(anInstant)は「ゼロ」を生成しますZoneOffset
  • OffsetDateTime.from(anInstant)は、「ゼロ」オフセットで日付/時刻を生成します
  • ZoneId.from(anInstant)(おそらく)UTCを生成するZoneId
  • ZonedDateTime.from(anInstant)(おそらく)UTCでZonedDateTimeを生成するZoneId

_ZonedDateTime.from_ のドキュメントは、私が読んだように、これを支持しているようです。

実際、ZoneOffset.from(anInstant)DateTimeExceptionで失敗し、そのためOffsetDateTime.from(anInstant)も失敗します。他の2つも同様です。

これは予想される動作ですか?

30
user3627702

短い答え:

JSR-310設計者は、ZoneIdZoneOffsetOffsetDateTimeなどの型の静的from()メソッドを使用して、マシン時間と人間時間の間の変換を行うことを望まない、ZonedDateTimeなど。これは、javadocを注意深く学習する場合に明示的に指定されます。代わりに使用します:

_OffsetDateTime#toInstant():Instant
ZonedDateTime#toInstant():Instant
Instant#atOffset(ZoneOffset):OffsetDateTime
Instant#atZone(ZoneId):ZonedDateTime
_

静的from()メソッドの問題は、そうでないと、人々がInstantと、たとえばLocalDateTimeの間でタイムゾーンを考慮せずに変換できることです。

長答:

Instantをカウンターと見なすかフィールドTupleと見なすか、JSR-310-teamによって与えられた答えは、いわゆるマシンタイムとヒューマンタイムの厳密な分離でした。実際、彼らは厳密に分離するつもりです-彼らの ガイドライン を見てください。最後に、彼らはInstantがマシンタイムカウンターとしてのみ解釈されることを望んでいます。そのため、年、時間などのフィールドにInstantを要求できない設計を意図的に持っています。

しかし、確かに、JSR-310チームはまったく一貫していません。彼らは、メソッドInstant.toString()を、年、...、時間、...、オフセット記号Z(UTCタイムゾーン)を含むフィールドタプルビューとして実装しました(脚注:JSR-310の外部そのようなマシンタイムでフィールドベースの外観を持つことは非常に一般的です-例えば、ウィキペディアまたは他のサイトで TAIおよびUTC )を参照してください。 S. Colebourneが仕様をリードすると、 threeten-github-issue

」私たちが本当に難しいラインだった場合、インスタントのtoStringは、1970-01-01Zからの秒数になります。開発者を支援するためによりフレンドリーなtoStringを出力することを選択しました。 、しかし、インスタントは秒のカウントであり、ある種のタイムゾーンなしでは年/月/日に変換できないという基本的な事実は変わりません。 "

人々はこの設計決定が好きかどうか(私のように)できますが、その結果、年、...、時間、...およびオフセットのInstantを求めることができません。サポートされているフィールドのドキュメントも参照してください。

_NANO_OF_SECOND 
MICRO_OF_SECOND 
MILLI_OF_SECOND 
INSTANT_SECONDS 
_

ここでは、何が欠落しているのかが興味深いです。とりわけ、ゾーン関連のフィールドが欠落しています。 理由として、Instantや_Java.util.Date_のようなオブジェクトにはタイムゾーンがないという声明をよく耳にします。あまりにも単純な見方です。これらのオブジェクトは内部的にタイムゾーン状態を持たない(そして、そのような内部値を持つ必要もありません)のは事実ですが、これらのオブジェクトはすべてのタイムゾーンオフセットの計算とローカルタイプへの変換の基礎であるため、UTCタイムゾーンに関連する必要があります。したがって、正しい答えは次のとおりです。Instantは、タイムゾーンUTC(仕様ごと)のUNIXエポック以降の秒とナノ秒をカウントするマシンカウンターです。最後の部分-UTCゾーンとの関係-は、JSR-310-teamによって十分に指定されていませんが、拒否することはできません。 デザイナーはInstantからタイムゾーンの側面を廃止したいのです。なぜなら人間の時間に関連しているように見えるからです。ただし、完全に廃止することはできませんそれは内部オフセット計算の基本的な部分だからです。に関してあなたの観察

」また、Instant.atZone(zone)Instant.atOffset(offset)の両方は、暗黙のUTCタイムゾーン/「ゼロ」オフセットを持つようにInstantを扱うことと一致する値を生成します。 "

は正しい。

ZoneOffset.from(anInstant)が_ZoneOffset.UTC_を生成することは非常に直感的かもしれませんが、from()-メソッドが存在しない OFFSET_SECONDS-field を検索するため、例外をスローします。 JSR-310の設計者は、同じ理由で仕様でそれを行うことに決めました。つまり、InstantはUTCタイムゾーンと公式には関係がない、つまり「タイムゾーンがない」と人々に思わせるためです。すべての内部計算でこの基本的な事実を受け入れなければなりません!)。

同じ理由で、OffsetDateTime.from(anInstant)ZoneId.from(anInstant)も失敗します。

ZonedDateTime.from(anInstant)についてwe 読み取り

」変換は、最初に一時オブジェクトからZoneIdを取得し、必要に応じてZoneOffsetにフォールバックします。次に、Instantを取得し、必要に応じてLocalDateTimeにフォールバックします。 ZoneIdまたはZoneOffsetとInstantまたはLocalDateTimeの組み合わせ。 "

したがって、ZoneIdZoneOffsetInstantから取得できないため、同じ理由でこの変換は再び失敗します。例外メッセージは次のようになります。

「TemporalAccessorからZoneIdを取得できません:タイプJava.time.Instantの1970-01-01T00:00:00Z」

最後に、すべての静的from()メソッドは、直感的に見えても、人間の時間と機械の時間との間の変換を行えないという問題に直面しています。場合によっては、たとえばLocalDateInstantの間の変換に疑問がある場合があります。この動作は指定されていますが、あなたの質問はこの種の最後の質問ではなく、多くのユーザーが混乱し続けると予測しています。

私の意見では、実際の設計上の問題は次のとおりです:

a)人間の時間と機械の時間の間に明確な分離があってはなりません。 Instantのような一時オブジェクトは、両方のように動作する必要があります。量子力学の類推:電子は粒子と波の両方として見ることができます。

b)静的from()メソッドはすべて公開されすぎています。私の意見では、Ihatは簡単にアクセスできるので、パブリックAPIから削除するか、TemporalAccessorよりも具体的な引数を使用する方が良いでしょう。これらのメソッドの弱点は、ローカルタイプでクエリを開始するため、そのような変換に関連するタイムゾーンについて考えることを忘れることができることです。例えば:LocalDate.from(anInstant)(timezone ???)を考慮してください。ただし、Instantinstant.getDate()のような日付を直接要求する場合、ここではクエリがUTCの観点から開始されるため、個人的にはUTCタイムゾーンの日付を有効な回答と見なします。

c)結論として、タイムゾーンを指定せずにローカルタイプとInstantなどのグローバルタイプとの間の変換を避けるための良いアイデアをJSR-310チームと絶対に共有します。ユーザーがそのようなtimezone-less変換を行うのを防ぐためのAPI設計に関しては、私は違います。私の好ましい方法は、グローバル型がカレンダー日付や壁時間、UTCタイムゾーンオフセットなどの人間の時間形式と関係を持たないことを言うのではなく、from()メソッドを制限することでした。

とにかく、後方互換性を維持するため、マシン時間と人間時間の分離のこの(重要ではない)設計は明確になり、新しいJava.time-APIを使用したいすべての人がそれに対応しなければなりません。

長い回答で申し訳ありませんが、選択したJSR-310の設計を説明するのは非常に困難です。

41
Meno Hochschild

Instantクラスにはタイムゾーンがありません。 UTCゾーンのように印刷されますが、それはあるゾーンで印刷する必要があるためです(ティックで印刷したくないでしょうか?)が、それはタイムゾーンではありません-次の例に示すように:

Instant instant=Instant.now();
System.out.println(instant);//prints 2014-05-14T06:18:48.649Z
System.out.println(instant.atZone(ZoneId.of("UTC")));//prints 2014-05-14T06:18:48.649Z[UTC]

ZoneOffset.fromおよびOffsetDateTime.fromの署名は、一時オブジェクトを受け入れますが、一部のタイプでは失敗します。 Java.timeには、タイムゾーンまたはオフセットを持つテンポラル用のインターフェイスがないようです。このようなインターフェイスでは、getOffsetおよびgetZoneを宣言できます。このようなインターフェイスがないため、これらのメソッドは複数の場所で別々に宣言されます。

ZonedDateTimeがある場合は、getOffsetと呼ぶことができます。 OffsetDateTimeがある場合、それをオフセットと呼ぶこともできますが、これらは2つの異なるgetOffsetメソッドです。2つのクラスは共通のインターフェースからそのメソッドを取得しないからです。つまり、いずれかのTemporalオブジェクトがある場合は、instanceofであるかどうかをテストして、両方ともオフセットを取得する必要があります。

OffsetDateTime.fromはあなたのためにそれをすることでプロセスを単純化します-しかし、それはまた共通のインターフェースに依存できないので、Temporalオブジェクトを受け入れ、持っていないものに対して例外を投げる必要がありますオフセット。

17
Idan Arye

単なる例w.r.tの変換、私はいくつかの人々が例外を下回ると信じています

(Java.time.DateTimeException:Java.time.Instant型のTemporalAccessor:2014-10-24T18:22:09.800ZからLocalDateTimeを取得できません)

彼らが試せば-

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());

問題を解決するには、ゾーンを渡してください-

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant().atZone(ZoneId.of("UTC")));

9
rohtakdev