ZonedDateTimeを日付に変換する方法は?


101

サーバーに依存しない日付時刻をデータベースに設定しようとしています。そのためのベストプラクティスは、UTC DateTimeを設定することです。私のdbサーバーはCassandraであり、Java用のdbドライバーはDateタイプのみを認識します。

私のコードで新しいJava 8 ZonedDateTimeを使用して今すぐUTCを取得すると(ZonedDateTime.now(ZoneOffset.UTC))、このZonedDateTimeインスタンスを「レガシー」Dateクラスに変換するにはどうすればよいですか?


Javaに組み込まれている「日付」クラスは2つjava.util.Dateありjava.sql.Dateます。
バジルブルク2015

回答:


161

ZonedDateTimeをインスタントに変換できます。これは、Dateで直接使用できます。

Date.from(java.time.ZonedDateTime.now().toInstant());

27
いいえ、それはゾーンシステムのデフォルトの現在の日付になります。
スリムソルターニドリディ

6
@MilenKovachevあなたの質問は意味がありません-日付にはタイムゾーンがありません-それは瞬間を表すだけです。
アッシリア2015

1
@assylias実際、あなたの発言は意味がありません。時刻が格納される形式は、タイムゾーンを意味します。日付はUTCに基づいていますが、残念ながら、Javaはいくつかの愚かなことを行い、そのように扱いません。さらに、時間をUTCではなくローカルTZと見なします。Data、LocalDateTime、ZonedDateTimeがデータを格納する方法は、タイムゾーンを意味します。それらが使用される方法は、まるでTZが存在しないかのようです。java.util.Dateには、暗黙的にJVMデフォルトのTZがあります。人々がそれを何か違うものとして扱うという事実(そのためのJavaドキュメントを含む!)は単に悪い人々です。
David

5
@Davidええいいえ-a Dateはエポックからのミリ秒数です-そのため、UTCに関連しています。あなたがいる場合、印刷、それを、デフォルトのタイムゾーンが使用されますが、Dateクラスは、ユーザーのタイムゾーンの知識を持たない...を参照してくださいたとえばで「マッピング」セクションdocs.oracle.com/javase/tutorial/datetime/iso/legacy .htmlそしてLocalDateTimeは明示的にタイムゾーンを参照せずにいます-これは混乱を招くように見える
かもしれませ

1
あなたの答えは不必要に含まれZonedDateTimeます。このjava.time.Instantクラスはの直接の置き換えでありjava.util.Date、どちらもUTCの瞬間を表しますInstantが、ミリ秒ではなくナノ秒のより細かい分解能を使用します。Date.from( Instant.now() )あなたのソリューションでなければなりませんでした。あるいはnew Date()、同じことをするだけで、UTCで現在の瞬間を捉えます。
バジルブルク2018

64

tl; dr

java.util.Date.from(  // Transfer the moment in UTC, truncating any microseconds or nanoseconds to milliseconds.
    Instant.now() ;   // Capture current moment in UTC, with resolution as fine as nanoseconds.
)

上記のコードには意味がありませんでしたが。とは両方ともUTCでの瞬間java.util.DateInstant表し、常にUTCで表します。上記のコードは次と同じ効果があります。

new java.util.Date()  // Capture current moment in UTC.

ここではを使用してもメリットはありませんZonedDateTime。すでにがある場合は、ZonedDateTimeを抽出してUTCに調整しInstantます。

java.util.Date.from(             // Truncates any micros/nanos.
    myZonedDateTime.toInstant()  // Adjust to UTC. Same moment, same point on the timeline, different wall-clock time.
)

その他の正解

回答によるssoltanidは正しく新しい学校のjava.timeオブジェクトを(変換する方法を、あなたの特定の質問に取り組むZonedDateTime古い学校に)java.util.Dateオブジェクト。InstantZonedDateTimeからを抽出してに渡しjava.util.Date.from()ます。

データロス

あなたがすることに注意してくださいデータの損失を被るよう、Instantトラックナノ秒ので、エポックjava.util.Dateのトラックのミリ秒エポック。

ミリ秒、マイクロ秒、ナノ秒の分解能を比較する図

あなたの質問とコメントは他の問題を引き起こします。

サーバーをUTCに保つ

サーバーのホストOSは、一般的にベストプラクティスとしてUTCに設定する必要があります。JVMは、私が知っているJava実装で、このホストOS設定をデフォルトのタイムゾーンとしてピックアップします。

タイムゾーンを指定する

ただし、JVMの現在のデフォルトのタイムゾーンに依存することはできません。ホスト設定を取得するのではなく、JVMの起動時に渡されるフラグにより​​別のタイムゾーンを設定できます。さらに悪いことに、任意のアプリの任意のスレッドの任意のコードがいつでもを呼び出してjava.util.TimeZone::setDefault、実行時にそのデフォルトを変更できます!

カサンドラTimestampタイプ

適切なデータベースとドライバーはすべて、渡された日時をUTCに合わせて格納するための調整を自動的に処理する必要があります。私はCassandraを使用していませんが、日付と時刻の基本的なサポートがあるようです。ドキュメントでは、そのTimestamp型は同じエポック(UTCでの1970年の最初の瞬間)からのミリ秒のカウントであると述べています。

ISO 8601

さらに、CassandraはISO 8601標準形式の文字列入力を受け入れます。さいわい、java.timeは、文字列の解析/生成のデフォルトとしてISO 8601形式を使用します。InstantクラスのtoString実装がうまく行います。

精度:ミリ秒とナノ秒

しかし、最初に、ZonedDateTimeのナノ秒の精度をミリ秒に減らす必要があります。1つの方法は、ミリ秒を使用して新しいインスタントを作成することです。幸い、java.timeには、ミリ秒単位の変換を行う便利なメソッドがいくつかあります。

コード例

以下は、Java 8 Update 60のコード例です。

ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );

Instant instant = zdt.toInstant();
Instant instantTruncatedToMilliseconds = Instant.ofEpochMilli( instant.toEpochMilli() );
String fodderForCassandra = instantTruncatedToMilliseconds.toString();  // Example: 2015-08-18T06:36:40.321Z

または、このCassandra Javaドライバーのドキュメントによれば、java.util.Dateインスタンスを渡すことができます(と混同しないでくださいjava.sqlDate)。したがってinstantTruncatedToMilliseconds、上のコードでそれからju​​Dateを作成できます。

java.util.Date dateForCassandra = java.util.Date.from( instantTruncatedToMilliseconds );

これを頻繁に行う場合は、ワンライナーを作成できます。

java.util.Date dateForCassandra = java.util.Date.from( zdt.toInstant() );

しかし、少しユーティリティメソッドを作成するほうがきれいです。

static public java.util.Date toJavaUtilDateFromZonedDateTime ( ZonedDateTime zdt ) {
    Instant instant = zdt.toInstant();
    // Data-loss, going from nanosecond resolution to milliseconds.
    java.util.Date utilDate = java.util.Date.from( instant ) ;
    return utilDate;
}

質問よりもこのすべてのコードの違いに注意してください。質問のコードは、ZonedDateTimeインスタンスのタイムゾーンをUTCに調整しようとしました。しかし、それは必要ではありません。概念的に:

ZonedDateTime = Instant + ZoneId

すでにUTCにあるインスタント部分を抽出するだけです(基本的にはUTCで、詳細についてはクラスのドキュメントをお読みください)。


モダンとレガシーの両方のJavaの日時タイプの表


java.timeについて

java.timeのフレームワークは、Java 8に組み込まれており、後にされています。これらのクラスは面倒古い取って代わるレガシーのような日付時刻クラスをjava.util.DateCalendar、& SimpleDateFormat

ジョダタイムプロジェクトは、今でメンテナンスモードへの移行をアドバイスjava.timeのクラス。

詳細については、Oracleチュートリアルを参照してください。スタックオーバーフローで多くの例と説明を検索してください。仕様はJSR 310です。

java.timeオブジェクトをデータベースと直接交換できます。JDBC 4.2以降に準拠したJDBCドライバーを使用します。文字列もクラスも必要ありません。java.sql.*

java.timeクラスはどこで入手できますか?

JavaまたはAndroidのどのバージョンで使用するjava.timeライブラリの表

ThreeTen-エクストラプロジェクトでは、追加のクラスでjava.timeを拡張します。このプロジェクトは、java.timeに将来追加される可能性があることを証明する場です。あなたはここにいくつかの有用なクラスのような見つけることがIntervalYearWeekYearQuarter、および多くを


素晴らしい説明、詳細。どうもありがとうございます!
ジャンマルコF.

4

現在のシステム時刻をUTCに変換する例を次に示します。これには、ZonedDateTimeを文字列としてフォーマットし、Stringオブジェクトをjava.text DateFormatを使用して日付オブジェクトに解析します。

    ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
    final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
    final DateFormat FORMATTER_YYYYMMDD_HH_MM_SS = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
    String dateStr = zdt.format(DATETIME_FORMATTER);

    Date utcDate = null;
    try {
        utcDate = FORMATTER_YYYYMMDD_HH_MM_SS.parse(dateStr);
    }catch (ParseException ex){
        ex.printStackTrace();
    }

4

Android のThreeTenバックポートを使用していて、新しいDate.from(Instant instant)API(最低限のAPI 26が必要)を使用できない場合は、以下を使用できます。

ZonedDateTime zdt = ZonedDateTime.now();
Date date = new Date(zdt.toInstant().toEpochMilli());

または:

Date date = DateTimeUtils.toDate(zdt.toInstant());

バジル・ブルクの回答のアドバイスも読んでください


1
ThreeTenバックポート(およびThreeTenABP)にはDateTimeUtils変換メソッドを含むクラスが含まれているため、Date date = DateTimeUtils.toDate(zdt.toInstant()); `を使用します。それほど低レベルではありません。
Ole VV

1

これは、Java 8以降に組み込まれているjava.timeクラスを使用して行うことができます。

ZonedDateTime temporal = ...
long epochSecond = temporal.getLong(INSTANT_SECONDS);
int nanoOfSecond = temporal.get(NANO_OF_SECOND);
Date date = new Date(epochSecond * 1000 + nanoOfSecond / 1000000);

申し訳ありませんが、一時的なものはどこにありますか?
Milen Kovachev 2015

@MilenKovachev ZonedDateTimeはTemporalのインスタンスです
Peter Lawrey

ありがとうございます。ただし、私のコメントが馬鹿げていないように、回答を編集したことをお伝えしたはずです。
Milen Kovachev 2015

2
@MilenKovachevコメントを削除できますが、それはばかげた質問ではありません。
Peter Lawrey、2015

1
なぜこの回答が反対投票されたのか理解できません。 Instant■秒とナノ秒を追跡します。 Datesミリ秒を追跡します。この一方から他方への変換は正しいです。
scottb

1

これを使っています。

public class TimeTools {

    public static Date getTaipeiNowDate() {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Asia/Taipei");
        ZonedDateTime dateAndTimeInTai = ZonedDateTime.ofInstant(now, zoneId);
        try {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInTai.toString().substring(0, 19).replace("T", " "));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

動か Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant()); ないから!!! コンピューターでアプリケーションを実行する場合、問題はありません。ただし、AWS、Docker、またはGCPのいずれかのリージョンで実行すると、問題が発生します。コンピューターはクラウド上のタイムゾーンではないからです。コードでタイムゾーンを正しく設定する必要があります。たとえば、Asia / Taipeiです。その後、AWS、Docker、またはGCPで修正されます。

public class App {
    public static void main(String[] args) {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Australia/Sydney");
        ZonedDateTime dateAndTimeInLA = ZonedDateTime.ofInstant(now, zoneId);
        try {
            Date ans = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInLA.toString().substring(0, 19).replace("T", " "));
            System.out.println("ans="+ans);
        } catch (ParseException e) {
        }
        Date wrongAns = Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant());
        System.out.println("wrongAns="+wrongAns);
    }
}

1
これは、7つの回答の中で最も複雑な方法の1つであるようです。何かメリットはありますか?私はそうは思いません。実際にフォーマットや解析を行う必要はありません。
Ole VV

ありがとうございます。なぜなら、date wrongAns = Date.from(java.time.ZonedDateTime.ofInstant(now、zoneId).toInstant());; 動かない!!!!!
beehuang

私のタイムゾーンで17:08の直前に秒のスニペットを実行したところans=Mon Jun 18 01:07:56 CEST 2018、不正解である、そしてwrongAns=Sun Jun 17 17:07:56 CEST 2018正解が表示されました。
Ole VV

コンピューターでアプリを実行しても問題ありません。しかし、ドッカーを使用すると問題があります。Dockerのタイムゾーンはコンピューターのタイムゾーンではないためです。コードでタイムゾーンを正しく設定する必要があります。たとえば、Asia / Taipeiです。次に、AWS、Docker、GCPまたは任意のコンピューターで修正されます。
beehuang

@ OleV.V。あなたは理解できますか?または何か質問はありますか?
beehuang

1

受け入れられた答えは私にとってうまくいきませんでした。返される日付は常にローカルの日付であり、元のタイムゾーンの日付ではありません。私はUTC + 2に住んでいます。

//This did not work for me
Date.from(java.time.ZonedDateTime.now().toInstant()); 

ZonedDateTimeから正しい日付を取得する2つの方法があります。

ハワイのこのZonedDateTimeがあるとします

LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneId.of("US/Hawaii"); // UTC-10

またはもともと尋ねられたUTCの場合

Instant zulu = Instant.now(); // GMT, UTC+0
ZonedDateTime zdt = zulu.atZone(ZoneId.of("UTC"));

代替案1

java.sql.Timestampを使用できます。シンプルですが、プログラミングの整合性にも影響があります。

Date date1 = Timestamp.valueOf(zdt.toLocalDateTime());

代替案2

ミリ秒から日付を作成します(ここで前に回答しました)。ローカルZoneOffsetは必須であることに注意してください。

ZoneOffset localOffset = ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now());
long zonedMillis = 1000L * zdt.toLocalDateTime().toEpochSecond(localOffset) + zdt.toLocalDateTime().getNano() / 1000000L;
Date date2 = new Date(zonedMillis);

0

beehuangのようなDockerアプリケーションについては、タイムゾーンを設定する必要があるとコメントしています。

あるいは、withZoneSameLocalを使用できます。例えば:

2014-07-01T00:00 + 02:00 [GMT + 02:00]は、

Date.from(zonedDateTime.withZoneSameLocal(ZoneId.systemDefault()).toInstant())

火7月1日午後12時00分00秒2014 CESTとによって、

Date.from(zonedDateTime.toInstant())

月6月30日夜09時00分00秒UTC 2014


-1

今だけ興味がある場合は、次のように使用してください:

Date d = new Date();

今は興味がありますが、今はUTCです。
Milen Kovachev

2
はい、これでUTCになります。日付はわかりません。
Jacob Eckel 2015

3
いいえ、ありません。これは、ローカルシステムのタイムゾーンになります。日付はタイムゾーン情報を格納しませんが、現在のシステム時刻を使用し、現在のシステムタイムゾーンを無視するため、日付をUTCに変換し直すことはありません
David

2
@David DateはUTCエポックとの相対的な時間を維持するため、米国のシステムから新しいDate()を取得し、日本のコンピューターから別のオブジェクトを取得した場合、それらは同一になります(両方が内部的に保持する長さを調べます)。
Jacob Eckel、2015年

1
@JacobEckel-はい、ただし、tzがUS tzであるときに9amを日付に入れ、JP tzに変更して、9amで新しい日付を作成すると、Dateの内部値は異なります。DSTを混在させるとすぐに、アプリが常にUTCで実行されていない限り、確実にDateを使用できません。これは、多くの理由で変更される可能性があり、その多くは不良コードに関係しています。
David
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.