Chciałbym mieć metodę CompareTo, która ignoruje część czasu java.util.Date. Sądzę, że istnieje wiele sposobów rozwiązania tego. Jaki jest najprostszy sposób?
Chciałbym mieć metodę CompareTo, która ignoruje część czasu java.util.Date. Sądzę, że istnieje wiele sposobów rozwiązania tego. Jaki jest najprostszy sposób?
Odpowiedzi:
Aktualizacja: chociaż Joda Time była wówczas świetną rekomendacją, w miarę możliwości korzystaj z java.timebiblioteki Java 8+.
Wolę używać Joda Time, co czyni to niezwykle łatwym:
DateTime first = ...;
DateTime second = ...;
LocalDate firstDate = first.toLocalDate();
LocalDate secondDate = second.toLocalDate();
return firstDate.compareTo(secondDate);
EDYCJA: Jak zaznaczono w komentarzach, jeśli używasz DateTimeComparator.getDateOnlyInstance(), jest jeszcze prostsze :)
// TODO: consider extracting the comparator to a field.
return DateTimeComparator.getDateOnlyInstance().compare(first, second);
(„Użyj Joda Time” jest podstawą prawie wszystkich pytań SO, które zadają pytania dotyczące java.util.Datelub java.util.Calendar. Jest to całkowicie lepszy interfejs API. Jeśli robisz coś znaczącego z datami / godzinami, powinieneś naprawdę go użyć, jeśli to możliwe.)
Jeśli jesteś absolutnie zmuszony do korzystania z wbudowanego interfejsu API, powinieneś utworzyć instancję Calendarz odpowiednią datą i przy użyciu odpowiedniej strefy czasowej. Następnie możesz ustawić każde pole w każdym kalendarzu poza godziną, minutą, sekundą i milisekundą na 0 i porównać otrzymane czasy. Zdecydowanie obrzydliwe w porównaniu z rozwiązaniem Joda :)
Strefa czasowa jest to ważne: java.util.Datejest zawsze oparte na UTC. W większości przypadków, gdy byłem zainteresowany datą, była to data w określonej strefie czasowej . To samo w sobie zmusi cię do korzystania z CalendarJoda Time (chyba że sam chcesz wziąć pod uwagę strefę czasową, której nie polecam).
Krótki przewodnik dla programistów Androida
//Add joda library dependency to your build.gradle file
dependencies {
...
implementation 'joda-time:joda-time:2.9.9'
}
Przykładowy kod (przykład)
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 jest prawie wszechobecny. A co z tym?
if (DateUtils.isSameDay(date1, date2)) {
// it's same
} else if (date1.before(date2)) {
// it's before
} else {
// it's after
}
Jeśli naprawdę chcesz użyć java.util.Date, zrobiłbyś coś takiego:
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();
}
}
lub używając zamiast tego Kalendarza (preferowane, ponieważ getYear () i takie są przestarzałe)
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);
}
}
Moje preferencje byłoby użyć Joda biblioteki insetad się java.util.Datebezpośrednio, jak Joda dokonuje rozróżnienia pomiędzy datą i godziną (patrz YearMonthDay i DateTime klas).
Jeśli jednak chcesz użyć java.util.Date, proponuję napisać metodę narzędzia; na przykład
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();
}
Jakieś opinie na temat tej alternatywy?
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.format(date1).equals(sdf.format(date2));
==Operator nie musi powrócić truerównoważną łańcuchy (stosowania equals()). Z pewnością nie możesz również użyć innych operatorów porównania, o których wspomniałeś.
sdf.format(date1).compareTo(sdf.format(date2));.
Ja też wolę Joda Time , ale oto alternatywa:
long oneDay = 24 * 60 * 60 * 1000
long d1 = first.getTime() / oneDay
long d2 = second.getTime() / oneDay
d1 == d2
EDYTOWAĆ
Poniżej umieszczam UTC na wypadek, gdybyś musiał porównać daty dla określonej strefy czasowej innej niż UTC. Jeśli jednak masz taką potrzebę, to naprawdę radzę wybrać Jodę.
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()
)
Unikaj starych, kłopotliwych klas daty i czasu, takich jak Date& Calendar, teraz zastąpionych przez klasy java.time.
A java.util.Dateoznacza moment na osi czasu w UTC. Odpowiednikiem w java.time jest Instant. Możesz dokonać konwersji za pomocą nowych metod dodanych do starszej klasy.
Instant instant1 = myJavaUtilDate1.toInstant();
Instant instant2 = myJavaUtilDate2.toInstant();
Chcesz porównać według daty. Strefa czasowa ma kluczowe znaczenie przy ustalaniu daty. W danym momencie data zmienia się na całym świecie w zależności od strefy. Na przykład kilka minut po północy w Paryżu Francja to nowy dzień, a jeszcze „wczoraj” w Montrealu Quebec .
Określ prawidłową nazwę strefy czasowej w formacie continent/region, takie jak America/Montreal, Africa/Casablancalub Pacific/Auckland. Nigdy nie używaj 3-4 literowego skrótu, takiego jak ESTlub, ISTponieważ nie są to prawdziwe strefy czasowe, nie są znormalizowane, a nawet unikalne (!).
ZoneId z = ZoneId.of( "America/Montreal" );
Zastosuj ZoneIddo, Instantaby uzyskać ZonedDateTime.
ZonedDateTime zdt1 = instant1.atZone( z );
ZonedDateTime zdt2 = instant2.atZone( z );
LocalDateKlasa reprezentuje wartość data-tylko bez time-of-day i bez strefy czasowej. Możemy wyodrębnić LocalDatez ZonedDateTime, skutecznie eliminując porę dnia.
LocalDate localDate1 = zdt1.toLocalDate();
LocalDate localDate2 = zdt2.toLocalDate();
Teraz porównać, przy użyciu metod, takich jak isEqual, isBeforei isAfter.
Boolean sameDate = localDate1.isEqual( localDate2 );
Zobacz ten kod uruchamiany na żywo w 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 [Ameryka / Montreal] | zdt2: 2017-03-24T18: 13: 10.972-04: 00 [America / Montreal]
localDate1: 2017-03-25 | localDate2: 2017-03-24
sameData: fałsz
Środowisko java.time jest wbudowane w Javę 8 i nowsze wersje . Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak java.util.Date, Calendar, i SimpleDateFormat.
Projekt Joda-Time , teraz w trybie konserwacji , zaleca migrację do klas java.time .
Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj stos przepełnienia dla wielu przykładów i wyjaśnień. Specyfikacja to JSR 310 .
Gdzie można uzyskać klasy java.time?
Projekt ThreeTen-Extra rozszerza java.time o dodatkowe klasy. Ten projekt jest poligonem doświadczalnym dla ewentualnych przyszłych dodatków do java.time. Można znaleźć kilka przydatnych klas tutaj takie jak Interval, YearWeek, YearQuarter, i więcej .
Wspomniane już apache commons-utils :
org.apache.commons.lang.time.DateUtils.truncate(date, Calendar.DAY_OF_MONTH)
daje ci obiekt Date zawierający tylko datę, bez czasu, i możesz ją porównać Date.compareTo
Obawiam się, że nie ma metody porównywania dwóch dat, które można by nazwać „łatwymi” lub „prostymi”.
Porównując dwa wystąpienia czasowe z jakimkolwiek ograniczeniem precyzji (np. Po prostu porównując daty), zawsze należy wziąć pod uwagę wpływ strefy czasowej na porównanie.
Jeśli date1określasz zdarzenie, które wystąpiło w strefie czasowej +2 i date2określa na przykład zdarzenie, które wystąpiło w EST, musisz zadbać o właściwe zrozumienie konsekwencji porównania.
Czy chcesz dowiedzieć się, czy dwa wydarzenia miały miejsce w tym samym dniu kalendarzowym w ich odpowiednich strefach czasowych? A może musisz wiedzieć, czy dwie daty mieszczą się w tej samej dacie kalendarzowej w określonej strefie czasowej (na przykład UTC lub lokalny TZ).
Gdy dowiesz się, co tak naprawdę próbujesz porównać, wystarczy, że potroisz datę roku-miesiąca w odpowiedniej strefie czasowej i dokonasz porównania.
Czas Jody może sprawić, że rzeczywista operacja porównania będzie wyglądać na znacznie czystszą, ale semantyka porównania wciąż jest czymś, co musisz samemu wymyślić.
Jeśli używasz Java 8, powinieneś użyć java.time.*klas do porównania dat - jest to preferowane w stosunku do różnych java.util.*klas
na przykład; 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);
Jeśli chcesz porównać tylko dwie daty bez czasu, poniższy kod może ci pomóc:
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
}
Oto rozwiązanie z tego bloga: 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.");
tzn. możesz zobaczyć, czy różnica czasu w milisekundach jest mniejsza niż długość jednego dnia.
Nie wiem, czy to nowy pomysł, ale pokazuję wam, jak to zrobiłem
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);
Wystarczy sprawdzić DAY_OF_YEAR w połączeniu z właściwością YEAR
boolean isSameDay =
firstCal.get(Calendar.YEAR) == secondCal.get(Calendar.YEAR) &&
firstCal.get(Calendar.DAY_OF_YEAR) == secondCal.get(Calendar.DAY_OF_YEAR)
EDYTOWAĆ:
Teraz możemy wykorzystać moc funkcji rozszerzenia 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();
}
Pomoże Ci to porównać daty bez uwzględnienia czasu.
Używając getDateInstance z SimpleDateFormat, możemy porównać tylko dwa obiekty daty bez czasu. Wykonaj poniższy kod.
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));
}
Korzystanie z 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).
Kolejna prosta metoda porównywania oparta na odpowiedziach tutaj i wskazówkach mojego mentora
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());
}
EDYCJA : Według @Janathan Drapeau, powyższy kod zawodzi w niektórych przypadkach (chciałbym zobaczyć te przypadki, proszę) i zasugerował, co rozumiem:
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());
}
Zwróć uwagę, że klasa Date jest przestarzała, ponieważ nie była podatna na internacjonalizację. Zamiast tego używana jest klasa Calendar!
getXxxmetod Date.
Po pierwsze, należy pamiętać, że ta operacja zależy od strefy czasowej. Więc wybierz, czy chcesz to zrobić w UTC, w strefie czasowej komputera, we własnej ulubionej strefie czasowej lub gdzie. Jeśli nie jesteś jeszcze przekonany, że to ma znaczenie, zobacz mój przykład na dole tej odpowiedzi.
Ponieważ twoje pytanie nie jest do końca jasne, zakładam, że masz klasę z polem instancji reprezentującym moment w czasie i implementującym Comparable, i chcesz, aby naturalne uporządkowanie twoich obiektów było według daty, ale nie godziny , z tego pola. Na przykład:
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.timeKlasy Java 8Brałem wolność zmiany typu swojej dziedzinie instancji od Datecelu Instant, odpowiednie klasy w Javie 8. Obiecuję, aby powrócić do leczenia Dateponiżej. Dodałem również stałą strefy czasowej. Możesz ustawić go na ZoneOffset.UTClub, ZoneId.of("Europe/Stockholm")co uważasz za odpowiednie (ustawienie na ZoneOffsetdziała, ponieważ ZoneOffsetjest to podklasa ZoneId).
Zdecydowałem się pokazać rozwiązanie za pomocą klas Java 8. Prosiłeś o najprostszy sposób, prawda? :-) Oto compareTometoda, o którą prosiłeś:
public int compareTo(ArnesClass o) {
LocalDate dateWithoutTime = when.atZone(arnesTimeZone).toLocalDate();
LocalDate otherDateWithoutTime = o.when.atZone(arnesTimeZone).toLocalDate();
return dateWithoutTime.compareTo(otherDateWithoutTime);
}
Jeśli nie trzeba część czasu when, to oczywiście łatwiejsze zadeklarować i pominąć wszystkie konwersje. W takim razie nie musimy się już martwić strefą czasową.whenLocalDate
Załóżmy teraz, że z jakiegoś powodu nie możesz zadeklarować swojego whenpola Instantlub chcesz zachować staroświeckie Date. Jeśli nadal możesz używać języka Java 8, po prostu przekonwertuj go na Instant, a następnie wykonaj następujące czynności:
LocalDate dateWithoutTime = when.toInstant().atZone(arnesTimeZone).toLocalDate();
Podobnie dla o.when .
Jeśli nie możesz użyć java 8, istnieją dwie opcje:
CalendarczySimpleDateFormat .Odpowiedź Adamskiego pokazuje, jak pozbawić część czasu Datekorzystania z Calendarklasy. Sugeruję, aby użyć getInstance(TimeZone)do uzyskania Calendarinstancji dla wybranej strefy czasowej. Alternatywnie możesz skorzystać z pomysłu z drugiej połowy odpowiedzi Jorna .
Korzystanie SimpleDateFormatjest naprawdę pośrednim sposobem korzystania, Calendarponieważ obiekt SimpleDateFormatzawiera Calendarobiekt. Jednak może być mniej kłopotliwe niż Calendarbezpośrednie:
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));
}
To było zainspirowane odpowiedzią Roba .
Dlaczego musimy wybrać konkretną strefę czasową? Powiedzmy, że chcemy porównać dwa razy w UTC: 24 marca 0:00 (północ) i 12:00 (południe). Jeśli zrobisz to w CET (powiedzmy, Europa / Paryż), będą to 1 rano i 13 po południu 24 marca, czyli w tym samym dniu. W Nowym Jorku (wschodni czas letni) są one 20:00 w dniu 23 marca i 8:00 w dniu 24 marca, czyli nie w tym samym dniu. To decyduje, którą strefę czasową wybierzesz. Jeśli polegasz tylko na domyślnych ustawieniach komputera, możesz być zaskoczony, gdy ktoś próbuje uruchomić kod na komputerze w innym miejscu w zglobalizowanym świecie.
Link do ThreeTen Backport, backport klas daty i godziny Java 8 do Java 6 i 7: http://www.threeten.org/threetenbp/ .
Moja propozycja:
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());
Używając wspólnych Apache możesz:
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
jest to najprostszy sposób rozwiązania tego problemu.
Rozwiązałem to, porównując według znacznika czasu:
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
}
Unikam używania Joda Time, ponieważ na Androidzie używa ogromnej przestrzeni. Rozmiar ma znaczenie. ;)
Innym rozwiązaniem korzystającym z Java 8 i Instant jest metoda truncatedTo
Zwraca kopię tego błyskawicznego skrótu do określonej jednostki.
Przykład:
@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)
}
Oto, co zadziałało dla mnie:
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 }