web-dev-qa-db-ja.com

DateオブジェクトをJavaのTimeStampと比較する

このコードをテストするとき:

Java.util.Date date = new Java.util.Date();
Java.util.Date stamp = new Java.sql.Timestamp(date.getTime());

assertTrue(date.equals(stamp));
assertTrue(date.compareTo(stamp) == 0);
assertTrue(stamp.compareTo(date) == 0);
assertTrue(stamp.equals(date));

私は真、真、真、偽を期待するでしょう。このため:

Java.sql.Timestampのjavadocでは、次のように記述されています。

注:このタイプは、Java.util.Dateと個別のナノ秒値の複合です。整数秒のみがJava.util.Dateコンポーネントに格納されます。秒の小数部-ナノ-は独立しています。 Timestamp.equals(Object)メソッドは、日付のnanosコンポーネントが不明であるため、Java.util.Date型の値が渡されたときにtrueを返すことはありません。その結果、Timestamp.equals(Object)メソッドは、Java.util.Date.equals(Object)メソッドに対して対称ではありません。また、hashcodeメソッドは基盤となるJava.util.Date実装を使用するため、計算にnanoを含めません。

上記のTimestampクラスとJava.util.Dateクラスの違いにより、コードはTimestamp値をJava.util.Dateのインスタンスとして一般的に表示しないことをお勧めします。 TimestampとJava.util.Dateの間の継承関係は、型の継承ではなく実装の継承を実際に示しています。

しかし、代わりに、true、false、true、falseを取得します。何か案は?

編集:この問題は、equalsメソッドで2つのDateをチェックしているときに表示されますが、Dateオブジェクトの1つはHibernateクラスから来ており、デバッグにはオブジェクトにTimeStampが含まれていることがわかります。したがって、equalsメソッドはfalseと評価され、次にこれを見つけました: http://mattfleming.com/node/141

しかし、コードを試してみると、異なる結果が得られます... equalsとcompareToのどちらも使用できない場合、2つの日付が同じであるかどうかを確認するために何を使用する必要がありますか?!?!

27
Torres

tl; dr

面倒なレガシー日時クラスの代わりに、最新のJava.timeクラスを使用してください。

myPreparedStatement.setObject(
    … , 
    Instant.now()                // Capture the current moment in UTC.
)

設計が不十分な古い日時クラス

もっと率直に言って、Java.sql.Timestamp/.Date/.Timeクラスはハックであり、悪いハックです。 Java.util.Date/.Calendarと同様に、これらは設計の選択が不十分な結果です。

Java.sqlタイプはできる限り短時間使用する必要があり、データベースのデータの送受信にのみ使用します。ビジネスロジックやその他の作業には使用しないでください。

Java.time

古い日時クラスは、Java 8以降に組み込まれた Java.time フレームワークに置き換えられました。これらの新しいクラスはJSR 310によって定義され、非常に成功したJoda-Timeライブラリに触発され、ThreeTen-Extraプロジェクトによって拡張されています。

最終的に、JDBCドライバーが更新され、これらのJava.time型と直接連携するようになります。ただし、その日までは、Java.sql型との間で変換を行う必要があります。そのような変換では、古いクラスに追加された 新しいメソッドを呼び出します

Instant は、UTCのタイムライン上の ナノ秒 の解像度です。

Instant instant = myJavaSqlTimestamp.toInstant();

他の方向に進むには:

Java.sql.Timestamp ts = Java.sql.Timestamp.valueOf( instant );

タイムゾーンを適用して、 実時間 を取得します。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Java.timeクラスには、賢明に選択されたクラス設計があります。したがって、equalscompareToを期待どおりに使用できます。 UTCからのオフセットまたはタイムゾーンを持つクラスは、isEqualisBefore、およびisAfterメソッドも提供することに注意してください。これらの方法は、タイムライン上の瞬間、それらの時系列順を考慮して比較します。 equalsおよびcompareToメソッドは、オフセットまたはタイムゾーンも考慮します。

Java.sqlの使用を最小限に抑えながら、Java.timeの使用を最大限にすると、質問の問題が浮き彫りになります。

Hibernateでは、Java.timeのコンバーターを使用します。

JDBC 4.2

JDBC 4.2以降では、レガシークラスをまったく使用する必要はありません。 getObjectおよびsetObjectメソッドを介して、データベースとJava.timeオブジェクトを直接交換できます。

myPreparedStatement.setObject( … , instant ) ;

そして検索。

Instant instant = myResultSet.getObject( … , Instant.class ) ;

多くのデータベースは、Java.timeで使用されているナノ秒ほどの解像度で瞬間を保存できないことに注意してください。 JDBCドライバー を暗黙的にそうするのではなく、明示的に切り捨てることができます。

Instant instant = Instant.now().truncatedTo( ChronoUnit.MILLIS ) ; // Lop off any nanoseconds & microseconds, keeping only the milliseconds, to match limitations of database. 

Java.timeについて

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

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

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

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

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

ThreeTen-Extra プロジェクトは、追加のクラスでJava.timeを拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある証明の場です。 IntervalYearWeekYearQuartermore などの便利なクラスがあります。

6
Basil Bourque

ニカンはequals部分について、compareToについて説明しました:

  • Timestampには、内部的にTimestampに変換するcompareTo(Date)メソッドがあります
  • Dateはダウンキャストによって比較を行います(Timestampはそのサブクラスであるため);ただし、javadocの状態:「TimestampとJava.util.Dateの間の継承関係は、型の継承ではなく実装の継承を実際に示します」

私の意見では、これはもちろん恐ろしいアイデアです。

6
Viruzzo

比較したいテストでも同じ問題が発生しましたJava.util.DateおよびJava.sql.Timestampオブジェクト。

私はそれらをLocalDateに変換しましたが、うまくいきました:

import org.Apache.commons.lang.ObjectUtils;

// date1 and date2 can be Java.util.Date or Java.sql.Timestamp
boolean datesAreEqual = ObjectUtils.equals(toLocalDate(date1), toLocalDate(date2));

ここで、toLocalDateは次のとおりです。

import org.joda.time.LocalDate;
import Java.util.Date;

public static void LocalDate toLocalDate(Date date)
{
    return date != null ? LocalDate.fromDateFields(date) : null;
}
5
Ferran Maylinch
  1. date.equals(stamp)はtrueを返し、スタンプequals(date)はfalseを返します。 [〜#〜] reason [〜#〜]:日付はタイムスタンプのナノ秒部分を無視し、他の部分は偶然等しいため、結果は等しい。秒の小数部-nanos-は分離されています。日付のnanosコンポーネントが不明なため、Timestamp.equals(Object)メソッドは、Java.util.Date型の値を渡されたときにtrueを返しません。詳細については here を参照してください

    1. date.compareTo(stamp)== 0はfalseを返し、stamp.compareTo(date)== 0はtrueを返します。 [〜#〜] reason [〜#〜]:これによると bug compareTo関数はそのまま動作します。
3
Saurabh

Timestampのnanos値はナノ秒数ではありません-ナノ秒分解能のミリ秒(つまり、秒の小数部)です。そのため、Timestampコンストラクターでは、スーパーの時間をミリ秒なしに設定しています。したがって、Timestampは、対応するfastTime(もちろん、秒の小数部がない場合)よりも、メンバーDate(DateのcompareTo()で使用)の値が常に低くなります。

タイムスタンプのソース 110行目を確認します。

2
sarumont

このような「文字列」を使用してオブジェクトの日付を再作成してください

Date date = new Date();
Date stamp = Timestamp(date.getTime());

SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String date1String = ft.format(date);
String date2String = ft.format(stamp);

Date date1 = ft.parse(date1String);
Date date2 = ft.parse(date2String);


assertTrue(date1.equals(date2)); // true
assertTrue(date1.compareTo(date2) == 0); //true
assertTrue(date2.compareTo(date1) == 0); //true
assertTrue(date2.equals(date1)); // true
1
Marco Mendão

DateとTimeStampの両方をCalendarオブジェクトに変換することを解決し、単一のCalendarのプロパティを比較しました。

Calendar date = Calendar.getInstance();
date.setTimeInMillis(dateObject.getTime());
Calendar timestamp = Calendar.getInstance();
timestamp.setTimeInMillis(timestampObject.getTime());
if (effettoAppendice.get(Calendar.DAY_OF_MONTH) == timestamp.get(Calendar.DAY_OF_MONTH) &&
    effettoAppendice.get(Calendar.MONTH) == timestamp.get(Calendar.MONTH) &&
    effettoAppendice.get(Calendar.YEAR) == timestamp.get(Calendar.YEAR)) {
    System.out.println("Date and Timestamp are the same");
} else {
    System.out.println("Date and Timestamp are NOT the same");
}

お役に立てれば。

1

悲しいことに、Timestampクラスはequals(Object)メソッドをequals(Timestamp)でオーバーロードするため、タイムスタンプでの比較は困難です。

equals(Object)では、javadocsは次のように述べています。

このTimestampオブジェクトが指定されたオブジェクトと等しいかどうかをテストします。メソッドequalsのこのバージョンは、Timestamp.equals(Timestamp)の不正な署名を修正し、既存のクラスファイルとの下位互換性を維持するために追加されました。注:このメソッドは、基本クラスのequals(Object)メソッドに関して対称ではありません。

私の経験則では、タイムスタンプの等価性を比較することはありません(とにかくほとんど役に立たない)が、等価性をチェックする必要がある場合は、getTime()(1月からのミリ秒数1970年1月、00:00)。

0

実装の継承と型の継承に関する小さなメモ。

「オブジェクトのクラスは、オブジェクトの実装方法を定義します。対照的に、オブジェクトの型は、そのインターフェースのみを参照します。クラス継承別の場所で使用できます。 "

JAVADOCが述べたように、タイムスタンプと日付クラスには実装の継承があります。

0
Shanmugavel

タイムスタンプのソースコード比較方法をご覧ください。

public boolean equals(Java.lang.Object ts) {
  if (ts instanceof Timestamp) {
    return this.equals((Timestamp)ts);
  } else {
    return false;
  }
}

http://www.docjar.com/html/api/Java/sql/Timestamp.Java.html

比較オブジェクトがタイムスタンプの場合にのみtrueを返します。また、ここに日付のソースコードがあります: http://www.docjar.com/html/api/Java/util/Date.Java.html 、およびTimestampはDateを継承するため、比較できます。

0
Nican