データベースに依存しないサーバーの日付時刻を設定しようとしていますが、そのためのベストプラクティスはUTC DateTimeを設定することです。私のdbサーバーはCassandraであり、JavaのdbドライバーはDateタイプのみを理解します。
私のコードで新しいJava 8 ZonedDateTimeを使用して現在UTC(ZonedDateTime.now(ZoneOffset.UTC)
)を取得していると仮定すると、このZonedDateTimeインスタンスを「レガシー」Dateクラスに変換するにはどうすればよいですか?
ZonedDateTimeをインスタントに変換して、Dateで直接使用できます。
Date.from(Java.time.ZonedDateTime.now().toInstant());
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.Date
とInstant
はどちらも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()
に渡します。
Instant
が Epoch から ナノ秒 を追跡するのに対し、Java.util.Date
はEpochから ミリ秒 を追跡するため、データ損失になります。
あなたの質問とコメントは他の問題を提起します。
一般的にベストプラクティスとして、サーバーのホストOSをUTCに設定する必要があります。 JVMは、私が知っているJava実装で、このホストOS設定をデフォルトのタイムゾーンとして選択します。
ただし、JVMの現在のデフォルトのタイムゾーンに依存しないでください。 JVMの起動時に渡されるフラグは、ホスト設定を取得する代わりに、別のタイムゾーンを設定できます。さらに悪いことに、どのアプリのどのスレッドのどのコードでも、いつでも Java.util.TimeZone::setDefault
を呼び出して、実行時にそのデフォルトを変更できます。
Timestamp
タイプまともなデータベース およびドライバーは、保存のために渡された日時をUTCに自動的に調整する必要があります。私はCassandraを使用していませんが、date-timeの基本的なサポートがあるようです。ドキュメントでは、その Timestamp
タイプは同じエポック(UTCで1970年の最初の瞬間)からのミリ秒のカウントであると述べています。
さらに、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 8以降に組み込まれています。これらのクラスは、 Java.util.Date
、 Calendar
、および 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に将来追加される可能性のある証明の場です。ここでは、 Interval
、 YearWeek
、 YearQuarter
、および more 。
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 のアドバイスもお読みください
以下は、現在のシステム時刻を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();
}
これは、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);
今だけに興味があるなら、単に使用してください:
Date d = new Date();
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
これを使用します。
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);
}
}