簡體   English   中英

如何比較沒有時間部分的兩個日期?

[英]How to compare two Dates without the time portion?

我想要一個忽略 java.util.Date 的時間部分的 compareTo 方法。 我想有很多方法可以解決這個問題。 最簡單的方法是什么?

更新:雖然當時 Joda Time 是一個很好的建議,但請盡可能使用 Java 8+ 中的java.time庫。


我更喜歡使用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 時間”是幾乎所有關於java.util.Datejava.util.Calendar SO 問題的基礎。這是一個非常出色的 API。如果您正在對日期/時間做任何重要的事情,您真的應該如果可能,請使用它。)

如果您絕對被迫使用內置 API,則應該使用適當的日期和適當的時區創建Calendar實例。 然后,您可以將每個日歷中的時、分、秒和毫秒中的每個字段設置為 0,並比較結果時間。 與 Joda 解決方案相比,絕對令人討厭:)

時區部分很重要: java.util.Date始終基於 UTC。 在我對日期感興趣的大多數情況下,這是特定時區中的日期。 這本身將迫使您使用Calendar或 Joda Time(除非您想自己考慮時區,我不建議這樣做。)

安卓開發者快速參考

//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();
  }
}

或者,使用 Calendar 代替(首選,因為不推薦使用 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);
  }
}

我更喜歡直接使用java.util.DateJoda庫 insetad,因為 Joda 區分日期和時間(請參閱YearMonthDayDateTime類)。

但是,如果您確實希望使用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));

如果您只想比較兩個日期的月、日和年,以下代碼對我有用:

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.format(date1).equals(sdf.format(date2));

謝謝羅布。

tl;博士

myJavaUtilDate1.toInstant()
               .atZone( ZoneId.of( "America/Montreal" ) )
               .toLocalDate()
               .isEqual (
                   myJavaUtilDate2.toInstant()
                                  .atZone( ZoneId.of( "America/Montreal" ) )
                                  .toLocalDate()
               )

避免遺留的日期時間類

避免麻煩的舊的遺留日期時間類,例如Date & Calendar ,現在被 java.time 類取代。

使用 java.time

java.util.Date代表 UTC 時間線上的一個時刻。 java.time 中的等價物是Instant 您可以使用添加到遺留類的新方法進行轉換。

Instant instant1 = myJavaUtilDate1.toInstant();
Instant instant2 = myJavaUtilDate2.toInstant();

您想按日期進行比較。 時區對於確定日期至關重要。 對於任何給定時刻,日期因地區而異。 例如,在法國巴黎午夜過后幾分鍾是新的一天,而在魁北克蒙特利爾仍然是“昨天”。

continent/region的格式指定正確的時區名稱,例如America/MontrealAfrica/CasablancaPacific/Auckland 永遠不要使用ESTIST等 3-4 個字母的縮寫,因為它們不是真正的時區,不是標准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" );

ZoneId應用於Instant以獲取ZonedDateTime

ZonedDateTime zdt1 = instant1.atZone( z );
ZonedDateTime zdt2 = instant2.atZone( z );

LocalDate類表示沒有時間和時區的僅日期值。 我們可以從ZonedDateTime提取LocalDate ,有效地消除時間部分。

LocalDate localDate1 = zdt1.toLocalDate();
LocalDate localDate2 = zdt2.toLocalDate();

現在使用isEqualisBeforeisAfter等方法進行比較。

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 | 本地日期2:2017-03-24

相同日期:假


關於 java.time

java.time框架內置於 Java 8 及更高版本中。 這些類取代麻煩的老傳統日期時間類,如java.util.DateCalendar ,和SimpleDateFormat

現在處於維護模式Joda-Time項目建議遷移到java.time類。

要了解更多信息,請參閱Oracle 教程 並在 Stack Overflow 上搜索許多示例和解釋。 規范是JSR 310

從哪里獲得 java.time 類?

ThreeTen-Extra項目用額外的類擴展了 java.time。 該項目是未來可能添加到 java.time 的試驗場。 您可以在這里找到一些有用的類,比如IntervalYearWeekYearQuarter ,和更多

我也更喜歡Joda Time ,但這里有一個替代方案:

long oneDay = 24 * 60 * 60 * 1000
long d1 = first.getTime() / oneDay
long d2 = second.getTime() / oneDay
d1 == d2

編輯

如果您需要比較 UTC 以外的特定時區的日期,我將 UTC 放在下面。 不過,如果您確實有這樣的需求,那么我真的建議您選擇 Joda。

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

已經提到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 time 可能會使實際的比較操作看起來更清晰,但是比較的語義仍然需要您自己弄清楚。

如果您使用的是 Java 8,則應該使用java.time.*類來比較日期 - 它比各種java.util.*類更java.time.*

例如; 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
}

只需結合 YEAR 屬性檢查 DAY_OF_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
    }
}

`

SimpleDateFormat sdf= new SimpleDateFormat("MM/dd/yyyy")

   Date date1=sdf.parse("03/25/2015");



  Date currentDate= sdf.parse(sdf.format(new Date()));

   return date1.compareTo(currentDate);

`

我不知道這是新想法還是別的,但我向你展示了我所做的

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

這是此博客的解決方案: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.");

即您可以查看以毫秒為單位的時差是否小於一天的長度。

使用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
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();
}

這將幫助您在不考慮時間的情況下比較日期。

在 Java 8 中,您可以使用 LocalDate,它與 Joda Time 中的 LocalDate 非常相似。

使用 SimpleDateFormat 的 getDateInstance,我們可以只比較兩個沒有時間的日期對象。 執行下面的代碼。

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

    }

基於此處的答案和我的導師指導的另一種簡單比較方法

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 類,因為它不適合國際化。 使用 Calendar 類代替!

首先,請注意此操作取決於時區。 因此,請選擇是否要在 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 8 java.time

我已經自由地將實例字段的類型從Date更改為Instant ,即 Java 8 中的相應類。我保證將返回到下面對Date的處理。 我還添加了一個時區常量。 你可以將它設置為ZoneOffset.UTCZoneId.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的時間部分,當然更容易聲明whenLocalDate並跳過所有轉換。 然后我們也不必再擔心時區了。

現在假設由於某種原因您不能將when字段聲明為Instant或者您想將其保留為老式的Date 如果您仍然可以使用 Java 8,只需將其轉換為Instant ,然后像以前一樣執行:

    LocalDate dateWithoutTime = when.toInstant().atZone(arnesTimeZone).toLocalDate();

同樣對於o.when

沒有 Java 8?

如果您不能使用 java 8,則有兩種選擇:

  1. 使用舊類之一解決它, CalendarSimpleDateFormat
  2. 將 Java 8 日期和時間類的反向移植到 Java 6 和 7,然后按上述操作。 我在底部包含一個鏈接。 不要使用 JodaTime。 在寫推薦它的答案時,JodaTime 可能是一個很好的建議; 但是 JodaTime 現在處於維護模式,所以 ThreeTen 向后移植是一個更好、更面向未來的選擇。

老式的方法

Adamski 的回答向您展示了如何使用Calendar類從Date去除時間部分。 我建議您使用getInstance(TimeZone)獲取所需時區的Calendar實例。 作為替代方案,您可以使用Jorn 回答后半部分的想法。

使用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));
}

這是受Rob 回答的啟發。

時區依賴性

為什么我們必須選擇一個特定的時區? 假設我們要比較 UTC 中的兩次,分別是 3 月 24 日 0:00(午夜)和 12:00(中午)。 如果您在 CET(例如歐洲/巴黎)中執行此操作,則它們是 3 月 24 日的凌晨 1 點和下午 1 點,即同一日期。 在紐約(東部夏令時間),它們是3月23日的20:00和3月24日的8:00,即不是同一天。 因此,您選擇的時區會有所不同。 如果您只依賴計算機的默認設置,那么當有人試圖在這個全球化世界的另一個地方的計算機上運行您的代碼時,您可能會感到驚訝。

關聯

鏈接到 ThreeTen Backport,Java 8 日期和時間類到 Java 6 和 7 的反向移植: http : //www.threeten.org/threetenbp/

使用 Apache 公共資源,您可以執行以下操作:

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方法

返回此 Instant 截斷為指定單位的副本。

例子:

@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)
}

我的提議:

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

如果您嚴格想使用 Date ( java.util.Date ),或者不使用任何外部庫。 用這個 :

public Boolean compareDateWithoutTime(Date d1, Date d2) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    return sdf.format(d1).equals(sdf.format(d2));
}
    Date today = new Date();
    Date endDate = new Date();//this
    endDate.setTime(endDate.getTime() - ((endDate.getHours()*60*60*1000) + (endDate.getMinutes()*60*1000) + (endDate.getSeconds()*1000)));
    today.setTime(today.getTime() - ((today.getHours()*60*60*1000) + (today.getMinutes()*60*1000) + (today.getSeconds()*1000)));

    System.out.println(endDate.compareTo(today) <= 0);

我只是將小時/分鍾/秒設置為 0,因此時間沒有問題,因為現在兩個日期的時間都相同。 現在您只需使用 compareTo。 此方法有助於找到“如果dueDate 是今天”,其中true 表示是。

如果您正在尋找一個簡單的解決方案,但又不想更改已棄用的java.util.Date class 從您的項目中,您可以將此方法添加到您的項目中並繼續您的任務:

使用java.util.concurrent.TimeUnit

`

public boolean isSameDay(Date first, Date second) {
    long difference_In_Time = first.getTime() - second.getTime();
        // calculate difference in days
        long difference_In_Days = 
        TimeUnit
              .MILLISECONDS
              .toDays(difference_In_Time);
        if (difference_In_Days == 0) {
            return true;
        }
        return false;
    }

`

像這樣實現它:

`

Date first = ...;
Date second = ...;
if (isSameDay(first, second)) {
    // congratulations, they are the same
}
else {
   // heads up champ, they are not the same
}

`

DateUtil.daysBetween()怎么樣? 它是Java,它返回一個數字(以天為單位)。

這對我有用:

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 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM