Сравните объект Date с меткой времени в Java - PullRequest
26 голосов
/ 19 января 2012

Когда я проверю этот код:

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

Я буду ожидать истину, истину, истину, ложь.Из-за этого:

В javadoc для java.sql.Timestamp говорится:

Примечание. Этот тип является составной частью java.util.Date и отдельных наносекунд.значение.В компоненте java.util.Date хранятся только целые секунды.Дробные секунды - нано - разделены.Метод Timestamp.equals (Object) никогда не возвращает true, если передано значение типа java.util.Date, поскольку компонент nanos даты неизвестен.В результате метод Timestamp.equals (Object) не является симметричным относительно метода java.util.Date.equals (Object).Кроме того, метод хэш-кода использует базовую реализацию java.util.Date и, следовательно, не учитывает в своих вычислениях nanos.

Из-за различий между классом Timestamp и классом java.util.Date, упомянутых выше, онРекомендуется, чтобы код не просматривал значения Timestamp в общем случае как экземпляр java.util.Date.Отношения наследования между Timestamp и java.util.Date действительно обозначают наследование реализации, а не наследование типов.

Но вместо этого я получу true, false, true, false.Есть идеи?

РЕДАКТИРОВАТЬ: эта проблема возникает, когда я проверял две даты с помощью метода equals, но один из объектов Date пришел из класса Hibernate и отладки я вижу, что объект содержит TimeStamp.Таким образом, метод equals оценивается как false, тогда я обнаружил следующее: http://mattfleming.com/node/141

Но когда я пробую код, я получаю разные результаты ... если я не могу использовать ни equals, ни CompareTo, что я должен использоватьпроверить, совпадают ли 2 даты?!?!

Ответы [ 10 ]

6 голосов
/ 29 февраля 2016

ТЛ; др

Используйте современные классы 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.time , встроенной в Java 8 и более поздние версии. Эти новые классы определены в 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 имеют тщательно продуманный дизайн классов. Таким образом, вы можете использовать equals и compareTo, как и ожидалось. Обратите внимание, что классы со смещением от UTC или часовым поясом также предлагают методы isEqual, isBefore и isAfter. Эти методы сравниваются, учитывая моменты времени, их хронологический порядок. Методы equals и compareTo также учитывают смещение или часовой пояс.

Минимизация использования java.sql при одновременном максимизации использования java.time делает вопрос Вопрос спорным.

В Hibernate используйте конвертеры для java.time.

JDBC 4.2

Начиная с JDBC 4.2 и более поздних версий вам вообще не нужно использовать устаревшие классы. Вы можете напрямую обмениваться java.time объектами с вашей базой данных с помощью методов getObject & setObject.

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.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться java.time объектами напрямую с вашей базой данных. Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти некоторые полезные классы, такие как Interval, YearWeek, YearQuarter и еще .

6 голосов
/ 19 января 2012

Никан объяснил часть equals, о compareTo:

  • Timestamp имеет метод compareTo(Date), который преобразует его в Timestamp внутренне
  • Date выполняет сравнение с понижением (поскольку Timestamp является его подклассом); но, как утверждает javadoc: «Отношения наследования между меткой времени и java.util.Date действительно означают наследование реализации, а не наследование типов»

На мой взгляд, это, конечно, ужасная идея.

4 голосов
/ 01 мая 2013

У меня была такая же проблема в тесте, где я хотел сравнить 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;
}
3 голосов
/ 19 января 2012
  1. date.equals (штамп) возвращает истину И штамп равен (дата) возвращает ложь. REASON : Дата пренебрегает наносекундной частью метки времени, а так как остальные части совпадают, результат равен.Дробные секунды - nanos - являются отдельными. Метод Timestamp.equals (Object) никогда не возвращает true, когда передается значение типа java.util.Date, поскольку компонент даты даты nanos неизвестен.См. здесь для получения более подробной информации.

    1. date.compareTo (stamp) == 0 возвращает false И stamp.compareTo (date) == 0 возвращает true. REASON : В соответствии с этой ошибкой функция compareTo будет работать так же, как и раньше.
2 голосов
/ 20 января 2012
Значение

Timestamp - это НЕ количество наносекунд, это число с точностью до наносекунды в миллисекундах (т.е. доли секунды). Таким образом, в конструкторе Timestamp он устанавливает время на супер без миллисекунд. Следовательно, Timestamp всегда будет иметь более низкое значение для члена fastTime (используется в compareTo() Даты), чем соответствующее Date (если, конечно, у него нет дробных секунд).

Проверьте источник метки времени в строке 110.

1 голос
/ 29 февраля 2016

Я решил преобразовать 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 голос
/ 07 января 2015

Попробуйте воссоздать дату объекта, используя "Строки", как это

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
0 голосов
/ 03 декабря 2014

Небольшая заметка о реализации наследования и наследования типов ..

"Класс объекта определяет, как объект реализован. Напротив, тип объекта относится только к его интерфейсу. Наследование класса (наследование реализации) определяет реализацию объекта в терминах реализации другого объекта. Наследование типа описывает, когда один объект можно использовать вместо другого. "

Классы Timestamp и Date имеют наследование реализации, как сказал JAVADOC.

0 голосов
/ 19 января 2012

К сожалению, класс Timestamp перегружает метод equals(Object) с помощью equals(Timestamp), поэтому сравнивать временные метки сложно.

В equals(Object) Javadocs говорит:

Проверяет, является ли этот объект Timestamp равный данному объекту. Эта версия метода равна добавлено исправление неверной подписи Timestamp.equals (Timestamp) и сохранить обратную совместимость с существующими файлами классов. Примечание: этот метод не является симметричным относительно равенства (Объект) метод в базовом классе.

Мое эмпирическое правило - никогда не сравнивать временные метки на равенство (что в любом случае бесполезно), но если вам действительно нужно проверить на равенство, сравните их, используя результат getTime() (число миллисекунд с 1 января 1970, 00:00).

0 голосов
/ 19 января 2012

Взгляните на метод сравнения исходного кода для Timestamp:

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, и поскольку временная метка наследует дату, она может сравнивать ее.

...