Я хотів би мати метод 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);
EDIT: Як зазначалося в коментарях, якщо ви використовуєте, DateTimeComparator.getDateOnlyInstance()це ще простіше :)
// TODO: consider extracting the comparator to a field.
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
("Використовуйте Joda Time" - це основа майже всіх запитань щодо запитань, про які задаються 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
EDIT
Я ставлю нижче значення 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
той самийДата: помилково
Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date, Calendar, і SimpleDateFormat.
Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти до класів java.time .
Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - 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)
Редагувати:
Тепер ми можемо використовувати потужність функцій розширення Котліна
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());
}
EDIT : За словами @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());
}
Зауважте, що клас Дата застарілий, оскільки він не підлягає інтернаціоналізації. Замість цього використовується клас Календар!
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класівЯ взяв свободу змінити тип вашого поля екземпляра з 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 годину ранку та 13 вечора 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());
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, - це використання методу усіченогоTo
Повертає копію цього Миттєвого усіченого до вказаного блоку.
Приклад:
@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 }