Я хотел бы иметь метод CompareTo, который игнорирует часть времени java.util.Date. Я думаю, что есть несколько способов решить эту проблему. Какой самый простой способ?
Я хотел бы иметь метод CompareTo, который игнорирует часть времени java.util.Date. Я думаю, что есть несколько способов решить эту проблему. Какой самый простой способ?
Ответы:
Обновление: хотя в то время Joda Time была хорошей рекомендацией, по возможности используйте java.time
библиотеку из Java 8+.
Я предпочитаю использовать Joda Time, что делает это невероятно простым:
DateTime first = ...;
DateTime second = ...;
LocalDate firstDate = first.toLocalDate();
LocalDate secondDate = second.toLocalDate();
return firstDate.compareTo(secondDate);
РЕДАКТИРОВАТЬ: Как отмечено в комментариях, если вы используете, DateTimeComparator.getDateOnlyInstance()
это еще проще :)
// TODO: consider extracting the comparator to a field.
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
(«Используйте Joda Time» - это основа почти всех вопросов SO, которые задают java.util.Date
или java.util.Calendar
. Это совершенно превосходный API. Если вы делаете что- то существенное с датами / временем, вы должны действительно использовать его, если возможно).
Если вы абсолютно вынуждены использовать встроенный API, вы должны создать экземпляр Calendar
с соответствующей датой и с использованием соответствующего часового пояса. Затем вы можете установить для каждого поля в каждом календаре значение часа, минуты, секунды и миллисекунды, равное 0, и сравнить полученное время. Определенно неприглядный по сравнению с решением Joda, хотя :)
Часовой пояс часть важна: java.util.Date
это всегда основан на UTC. В большинстве случаев, когда меня интересовала дата, это была дата в определенном часовом поясе . Это Calendar
само по себе заставит вас использовать Joda Time (если вы сами не хотите учитывать часовой пояс, что я не рекомендую).
Краткий справочник для разработчиков Android
//Add joda library dependency to your build.gradle file
dependencies {
...
implementation 'joda-time:joda-time:2.9.9'
}
Пример кода (пример)
DateTimeComparator dateTimeComparator = DateTimeComparator.getDateOnlyInstance();
Date myDateOne = ...;
Date myDateTwo = ...;
int retVal = dateTimeComparator.compare(myDateOne, myDateTwo);
if(retVal == 0)
//both dates are equal
else if(retVal < 0)
//myDateOne is before myDateTwo
else if(retVal > 0)
//myDateOne is after myDateTwo
Apache commons-lang почти вездесущ. Так что по этому поводу?
if (DateUtils.isSameDay(date1, date2)) {
// it's same
} else if (date1.before(date2)) {
// it's before
} else {
// it's after
}
Если вы действительно хотите использовать java.util.Date, вы должны сделать что-то вроде этого:
public class TimeIgnoringComparator implements Comparator<Date> {
public int compare(Date d1, Date d2) {
if (d1.getYear() != d2.getYear())
return d1.getYear() - d2.getYear();
if (d1.getMonth() != d2.getMonth())
return d1.getMonth() - d2.getMonth();
return d1.getDate() - d2.getDate();
}
}
или, используя вместо этого Календарь (предпочтительно, так как getYear () и тому подобное устарели)
public class TimeIgnoringComparator implements Comparator<Calendar> {
public int compare(Calendar c1, Calendar c2) {
if (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR))
return c1.get(Calendar.YEAR) - c2.get(Calendar.YEAR);
if (c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH))
return c1.get(Calendar.MONTH) - c2.get(Calendar.MONTH);
return c1.get(Calendar.DAY_OF_MONTH) - c2.get(Calendar.DAY_OF_MONTH);
}
}
Мое предпочтение было бы использовать Joda библиотеки insetad из java.util.Date
непосредственно, как Joda делает различие между датой и временем (см YearMonthDay и DateTime классы).
Однако, если вы хотите использовать, java.util.Date
я бы предложил написать вспомогательный метод; например
public static Date setTimeToMidnight(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime( date );
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
Есть мнения по поводу этой альтернативы?
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.format(date1).equals(sdf.format(date2));
==
Оператор не обязательно возвращать true
равноценные строки (использование equals()
). Вы также не можете использовать другие операторы сравнения, которые вы упомянули.
sdf.format(date1).compareTo(sdf.format(date2));
.
Я тоже предпочитаю Joda Time , но вот альтернатива:
long oneDay = 24 * 60 * 60 * 1000
long d1 = first.getTime() / oneDay
long d2 = second.getTime() / oneDay
d1 == d2
РЕДАКТИРОВАТЬ
Я добавлю UTC ниже, если вам нужно сравнить даты для определенного часового пояса, отличного от UTC. Если у вас есть такая необходимость, тогда я действительно советую идти за Йодой.
long oneDay = 24 * 60 * 60 * 1000
long hoursFromUTC = -4 * 60 * 60 * 1000 // EST with Daylight Time Savings
long d1 = (first.getTime() + hoursFromUTC) / oneDay
long d2 = (second.getTime() + hoursFromUTC) / oneDay
d1 == d2
Math.abs(second.getTime() - first.getTime()) < 1000L*60*60*24
< oneDay
.
myJavaUtilDate1.toInstant()
.atZone( ZoneId.of( "America/Montreal" ) )
.toLocalDate()
.isEqual (
myJavaUtilDate2.toInstant()
.atZone( ZoneId.of( "America/Montreal" ) )
.toLocalDate()
)
Избегайте проблемных старых унаследованных классов даты и времени, таких как Date
& Calendar
, теперь замененных классами java.time.
A java.util.Date
представляет момент на временной шкале в UTC. Эквивалент в java.time есть Instant
. Вы можете конвертировать, используя новые методы, добавленные в унаследованный класс.
Instant instant1 = myJavaUtilDate1.toInstant();
Instant instant2 = myJavaUtilDate2.toInstant();
Вы хотите сравнить по дате. Часовой пояс имеет решающее значение при определении даты. В любой момент времени дата меняется по всему земному шару в зависимости от зоны. Например, через несколько минут после полуночи в Париже Франция - новый день, а в Монреале-Квебеке все еще «вчера» .
Укажите правильное время имя зоны в формате continent/region
, например America/Montreal
, Africa/Casablanca
или Pacific/Auckland
. Никогда не используйте 3-4-буквенное сокращение, такое как EST
или, IST
поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "America/Montreal" );
Применить ZoneId
к, Instant
чтобы получить ZonedDateTime
.
ZonedDateTime zdt1 = instant1.atZone( z );
ZonedDateTime zdt2 = instant2.atZone( z );
LocalDate
Класс представляет собой дату только значение без времени суток и без временной зоны. Мы можем извлечь LocalDate
из ZonedDateTime
, эффективно устраняя часть времени суток.
LocalDate localDate1 = zdt1.toLocalDate();
LocalDate localDate2 = zdt2.toLocalDate();
Теперь сравните, используя методы , такие как isEqual
, isBefore
, и isAfter
.
Boolean sameDate = localDate1.isEqual( localDate2 );
Смотрите этот код в прямом эфире на IdeOne.com .
instant1: 2017-03-25T04: 13: 10.971Z | Instant2: 2017-03-24T22: 13: 10,972Z
zdt1: 2017-03-25T00: 13: 10.971-04: 00 [Америка / Монреаль] | zdt2: 2017-03-24T18: 13: 10.972-04: 00 [Америка / Монреаль]
localDate1: 2017-03-25 | localDate2: 2017-03-24
sameDate: false
Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как java.util.Date
, Calendar
, и SimpleDateFormat
.
Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .
Чтобы узнать больше, смотрите Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .
Где взять классы java.time?
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь , такие как Interval
, YearWeek
, YearQuarter
, и более .
Уже упоминавшиеся apache commons-utils :
org.apache.commons.lang.time.DateUtils.truncate(date, Calendar.DAY_OF_MONTH)
дает вам объект Date, содержащий только дату, без времени, и вы можете сравнить его с Date.compareTo
Боюсь, что не существует метода сравнения двух дат, который можно было бы назвать «простым» или «простым».
При сравнении двух экземпляров времени с любым видом пониженной точности (например, просто сравнение дат) вы всегда должны учитывать, как часовой пояс влияет на сравнение.
Если date1
это указание события , которое произошло в +2 часового пояса и date2
уточняет событие , которое произошло в EST, к примеру, вы должны позаботиться , чтобы правильно понять последствие сравнения.
Ваша цель - выяснить, произошли ли два события в одну и ту же календарную дату в соответствующих часовых поясах? Или Вам нужно знать, попадают ли две даты в одну и ту же календарную дату в определенном часовом поясе (например, UTC или местный TZ).
Как только вы выясните, что именно вы пытаетесь сравнить, это просто вопрос получения тройки год-дата-дата в соответствующем часовом поясе и проведите сравнение.
Время Joda может заставить реальную операцию сравнения выглядеть намного чище, но семантика сравнения все еще является чем-то, что вам нужно выяснить самостоятельно.
Если вы используете Java 8, вы должны использовать java.time.*
классы для сравнения дат - предпочтительнее различных java.util.*
классов
например; https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
LocalDate date1 = LocalDate.of(2016, 2, 14);
LocalDate date2 = LocalDate.of(2015, 5, 23);
date1.isAfter(date2);
Если вы просто хотите сравнить только две даты без времени, то вам может помочь следующий код:
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
Date dLastUpdateDate = dateFormat.parse(20111116);
Date dCurrentDate = dateFormat.parse(dateFormat.format(new Date()));
if (dCurrentDate.after(dLastUpdateDate))
{
add your logic
}
Вот решение от этого блога: http://brigitzblog.blogspot.com/2011/10/java-compare-dates.html
long milliseconds1 = calendar1.getTimeInMillis();
long milliseconds2 = calendar2.getTimeInMillis();
long diff = milliseconds2 - milliseconds1;
long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.println("Time in days: " + diffDays + " days.");
т.е. вы можете видеть, меньше ли разница во времени в миллисекундах, чем продолжительность одного дня.
Я не знаю, что это новое, или иначе, но я покажу вам, как я сделал
SimpleDateFormat dtf = new SimpleDateFormat("dd/MM/yyyy");
Date td_date = new Date();
String first_date = dtf.format(td_date); //First seted in String
String second_date = "30/11/2020"; //Second date you can set hear in String
String result = (first_date.equals(second_date)) ? "Yes, Its Equals":"No, It is not Equals";
System.out.println(result);
Просто проверьте DAY_OF_YEAR в сочетании со свойством YEAR
boolean isSameDay =
firstCal.get(Calendar.YEAR) == secondCal.get(Calendar.YEAR) &&
firstCal.get(Calendar.DAY_OF_YEAR) == secondCal.get(Calendar.DAY_OF_YEAR)
РЕДАКТИРОВАТЬ:
Теперь мы можем использовать возможности функций расширения Kotlin
fun Calendar.isSameDay(second: Calendar): Boolean {
return this[Calendar.YEAR] == second[Calendar.YEAR] && this[Calendar.DAY_OF_YEAR] == second[Calendar.DAY_OF_YEAR]
}
fun Calendar.compareDatesOnly(other: Calendar): Int {
return when {
isSameDay(other) -> 0
before(other) -> -1
else -> 1
}
}
public Date saveDateWithoutTime(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime( date );
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
Это поможет вам сравнить даты без учета времени.
Используя getDateInstance SimpleDateFormat, мы можем сравнивать только два объекта даты без времени. Выполните приведенный ниже код.
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
DateFormat dfg = SimpleDateFormat.getDateInstance(DateFormat.DATE_FIELD);
String dateDtr1 = dfg.format(date1);
String dateDtr2 = dfg.format(date2);
System.out.println(dateDtr1+" : "+dateDtr2);
System.out.println(dateDtr1.equals(dateDtr2));
}
Использование http://mvnrepository.com/artifact/commons-lang/commons-lang
Date date1 = new Date();
Date date2 = new Date();
if (DateUtils.truncatedCompareTo(date1, date2, Calendar.DAY_OF_MONTH) == 0)
// TRUE
else
// FALSE
DateUtils.isSameDay(date1, date2)
.
Другой простой метод сравнения, основанный на ответах здесь и моих наставнических указаниях
public static int compare(Date d1, Date d2) {
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTime(d1);
c1.set(Calendar.MILLISECOND, 0);
c1.set(Calendar.SECOND, 0);
c1.set(Calendar.MINUTE, 0);
c1.set(Calendar.HOUR_OF_DAY, 0);
c2.setTime(d2);
c2.set(Calendar.MILLISECOND, 0);
c2.set(Calendar.SECOND, 0);
c2.set(Calendar.MINUTE, 0);
c2.set(Calendar.HOUR_OF_DAY, 0);
return c1.getTime().compareTo(c2.getTime());
}
РЕДАКТИРОВАТЬ : Согласно @Jonathan Drapeau, приведенный выше код не удается в некоторых случаях (я хотел бы видеть эти случаи, пожалуйста), и он предложил следующее, как я понимаю:
public static int compare2(Date d1, Date d2) {
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.clear();
c2.clear();
c1.set(Calendar.YEAR, d1.getYear());
c1.set(Calendar.MONTH, d1.getMonth());
c1.set(Calendar.DAY_OF_MONTH, d1.getDay());
c2.set(Calendar.YEAR, d2.getYear());
c2.set(Calendar.MONTH, d2.getMonth());
c2.set(Calendar.DAY_OF_MONTH, d2.getDay());
return c1.getTime().compareTo(c2.getTime());
}
Обратите внимание, что класс Date устарел, поскольку он не поддается интернационализации. Класс Календарь используется вместо!
getXxx
методы Date
.
Во-первых, имейте в виду, что эта операция зависит от часового пояса. Так что выберите, хотите ли вы сделать это в UTC, в часовом поясе компьютера, в своем любимом часовом поясе или где. Если вы еще не уверены, что это важно, посмотрите мой пример внизу этого ответа.
Поскольку ваш вопрос не совсем ясен по этому поводу, я предполагаю, что у вас есть класс с полем экземпляра, представляющим момент времени и реализующий его Comparable
, и вы хотите, чтобы естественный порядок ваших объектов был по дате, но не по времени из этого поля. Например:
public class ArnesClass implements Comparable<ArnesClass> {
private static final ZoneId arnesTimeZone = ZoneId.systemDefault();
private Instant when;
@Override
public int compareTo(ArnesClass o) {
// question is what to put here
}
}
java.time
Классы Java 8Я взял на себя свободу изменять тип поля вашего экземпляра с Date
на Instant
, соответствующий класс в Java 8. Я обещаю вернуться к рассмотрению Date
ниже. Я также добавил постоянную часового пояса. Вы можете установить его ZoneOffset.UTC
или ZoneId.of("Europe/Stockholm")
или, что вы считаете подходящим (установка его на ZoneOffset
произведения, потому что ZoneOffset
это подкласс ZoneId
).
Я решил показать решение с использованием классов Java 8. Вы просили самый простой способ, верно? :-) Вот compareTo
метод, который вы просили:
public int compareTo(ArnesClass o) {
LocalDate dateWithoutTime = when.atZone(arnesTimeZone).toLocalDate();
LocalDate otherDateWithoutTime = o.when.atZone(arnesTimeZone).toLocalDate();
return dateWithoutTime.compareTo(otherDateWithoutTime);
}
Если вам не нужно время часть when
, это, конечно , проще объявить и пропустить все преобразования. Тогда нам больше не нужно беспокоиться о часовом поясе.when
LocalDate
Теперь предположим, что по какой-то причине вы не можете объявить свое when
поле Instant
или вы хотите оставить его старомодным Date
. Если вы все еще можете использовать Java 8, просто преобразуйте его в Instant
, а затем сделайте как раньше:
LocalDate dateWithoutTime = when.toInstant().atZone(arnesTimeZone).toLocalDate();
Аналогично для o.when
.
Если вы не можете использовать Java 8, есть два варианта:
Calendar
или SimpleDateFormat
.Ответ Адамски показывает вам, как избавиться от части времени, Date
используя Calendar
класс. Я предлагаю вам использовать, getInstance(TimeZone)
чтобы получить Calendar
экземпляр для часового пояса, который вы хотите. В качестве альтернативы вы можете использовать идею из второй половины ответа Йорна .
Использование SimpleDateFormat
действительно косвенный способ использования, Calendar
поскольку объект SimpleDateFormat
содержит Calendar
объект. Тем не менее, вы можете найти это менее хлопотно, чем использовать Calendar
напрямую:
private static final TimeZone arnesTimeZone = TimeZone.getTimeZone("Europe/Stockholm");
private static final DateFormat formatter = new SimpleDateFormat("yyyyMMdd");
static {
formatter.setTimeZone(arnesTimeZone);
}
private Date when;
@Override
public int compareTo(ArnesClass o) {
return formatter.format(when).compareTo(formatter.format(o.when));
}
Это было вдохновлено ответом Роба .
Почему мы должны выбрать определенный часовой пояс? Скажем, мы хотим сравнить два раза в UTC: 24 марта, 0:00 (полночь) и 12:00 (полдень). Если вы сделаете это в CET (скажем, в Европе / Париже), это будет 1 час ночи и 1 час дня 24 марта, то есть в тот же день. В Нью-Йорке (восточное дневное время) они наступают в 20:00 23 марта и в 8:00 24 марта, то есть не в одну и ту же дату. Так что важно, какой часовой пояс вы выберете. Если вы просто полагаетесь на настройки компьютера по умолчанию, вас могут ожидать сюрпризы, когда кто-то пытается запустить ваш код на компьютере в другом месте в этом глобализированном мире.
Ссылка на ThreeTen Backport, бэкпорт классов даты и времени Java 8 для Java 6 и 7: http://www.threeten.org/threetenbp/ .
Мое предложение:
Calendar cal = Calendar.getInstance();
cal.set(1999,10,01); // nov 1st, 1999
cal.set(Calendar.AM_PM,Calendar.AM);
cal.set(Calendar.HOUR,0);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
// date column in the Thought table is of type sql date
Thought thought = thoughtDao.getThought(date, language);
Assert.assertEquals(cal.getTime(), thought.getDate());
Используя Apache Commons, вы можете сделать:
import org.apache.commons.lang3.time.DateUtils
DateUtils.truncatedEquals(first, second, Calendar.DAY_OF_MONTH)
public static Date getZeroTimeDate(Date fecha) {
Date res = fecha;
Calendar calendar = Calendar.getInstance();
calendar.setTime( fecha );
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
res = calendar.getTime();
return res;
}
Date currentDate = getZeroTimeDate(new Date());// get current date
это самый простой способ решить эту проблему.
Я решил это путем сравнения по метке времени:
Calendar last = Calendar.getInstance();
last.setTimeInMillis(firstTimeInMillis);
Calendar current = Calendar.getInstance();
if (last.get(Calendar.DAY_OF_MONTH) != current.get(Calendar.DAY_OF_MONTH)) {
//not the same day
}
Я избегаю использовать Joda Time, потому что на Android используется огромное пространство. Размер имеет значение. ;)
Другое решение, использующее Java 8 и Instant, использует метод truncatedTo
Возвращает копию этого Мгновенного обрезанного до указанного модуля.
Пример:
@Test
public void dateTruncate() throws InterruptedException {
Instant now = Instant.now();
Thread.sleep(1000*5);
Instant later = Instant.now();
assertThat(now, not(equalTo(later)));
assertThat(now.truncatedTo(ChronoUnit.DAYS), equalTo(later.truncatedTo(ChronoUnit.DAYS)));
}
// Create one day 00:00:00 calendar
int oneDayTimeStamp = 1523017440;
Calendar oneDayCal = Calendar.getInstance();
oneDayCal.setTimeInMillis(oneDayTimeStamp * 1000L);
oneDayCal.set(Calendar.HOUR_OF_DAY, 0);
oneDayCal.set(Calendar.MINUTE, 0);
oneDayCal.set(Calendar.SECOND, 0);
oneDayCal.set(Calendar.MILLISECOND, 0);
// Create current day 00:00:00 calendar
Calendar currentCal = Calendar.getInstance();
currentCal.set(Calendar.HOUR_OF_DAY, 0);
currentCal.set(Calendar.MINUTE, 0);
currentCal.set(Calendar.SECOND, 0);
currentCal.set(Calendar.MILLISECOND, 0);
if (oneDayCal.compareTo(currentCal) == 0) {
// Same day (excluding time)
}
Вот что сработало для меня:
var Date1 = new Date(dateObject1.toDateString()); //this sets time to 00:00:00
var Date2 = new Date(dateObject2.toDateString());
//do a normal compare
if(Date1 > Date2){ //do something }