web-dev-qa-db-ja.com

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

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

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

69
Milen Kovachev

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

Date.from(Java.time.ZonedDateTime.now().toInstant());
119

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.
)

上記のコードには意味がありませんでしたが。 Java.util.DateInstantはどちらもUTCの瞬間を表し、常にUTCです。上記のコードの効果は次のとおりです。

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

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

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 による Answer は、特定の質問、新しい学校のJava.timeオブジェクト(ZonedDateTime)を古い学校の Java.util.Date オブジェクトに変換する方法に正しく対処します。 ZonedDateTimeからInstantを抽出し、 Java.util.Date.from() に渡します。

データロス

InstantEpoch から ナノ秒 を追跡するのに対し、Java.util.DateはEpochから ミリ秒 を追跡するため、データ損失になります。

diagram comparing resolutions of millisecond, microsecond, and nanosecond

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

サーバーをUTCに保つ

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

タイムゾーンを指定

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

Cassandra Timestampタイプ

まともなデータベース およびドライバーは、保存のために渡された日時をUTCに自動的に調整する必要があります。私はCassandraを使用していませんが、date-timeの基本的なサポートがあるようです。ドキュメントでは、その 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ドライバーdoc に従って、 Java.util.Date インスタンスを渡すことができます( Java.sqlDate と混同しないでください)。したがって、上記のコードのinstantTruncatedToMillisecondsからj.u.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 =インスタント+ ZoneId

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


Java.timeについて

Java.time フレームワークは、Java 8以降に組み込まれています。これらのクラスは、 Java.util.DateCalendar 、および SimpleDateFormat などの厄介な古い レガシー 日時クラスに取って代わります。

メンテナンスモード になった Joda-Time プロジェクトは、 Java.time クラスへの移行を推奨しています。

詳細については、 Oracle Tutorial を参照してください。また、Stack Overflowで多くの例と説明を検索してください。仕様は JSR 31 です。

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

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

ThreeTen-Extra プロジェクトは、Java.timeを追加のクラスで拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある証明の場です。ここでは、 IntervalYearWeekYearQuarter 、および more

46
Basil Bourque

Androidに ThreeTenバックポート を使用しており、新しいDate.from(Instant instant)(API 26以上が必要)を使用できない場合は、次を使用できます。

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

または:

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

Basil Bourque's answer のアドバイスもお読みください

3
David Rawson

以下は、現在のシステム時刻を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();
    }
3

これは、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);
1
Peter Lawrey

今だけに興味があるなら、単に使用してください:

Date d = new Date();
0
Jacob Eckel

BeehuangのようなDockerアプリケーションの場合、タイムゾーンを設定する必要があります。

または、withZoneSameLocalを使用できます。例えば:

2014-07-01T00:00 + 02: [GMT + 02:00]は次のように変換されます

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

to Tue Jul 01 00:00:00 CEST 2014 and by

Date.from(zonedDateTime.toInstant())

月6月30日22:00:00 UTC 2014

0
dwe

これを使用します。

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);
    }
}
0
beehuang