计算两个Java日期实例之间的差异

501

我在Scala中使用Java的java.util.Date类,想要比较一个Date对象和当前时间。我知道可以通过使用getTime()计算时间差:

(new java.util.Date()).getTime() - oldDate.getTime()

然而,这只给我留下了一个表示毫秒的long。是否有更简单、更好的方法来获得时间差?


12
为什么joda time没有得到更多的喜爱?如果你需要处理Java中的日期,它几乎是最佳选择。 - Doctor Jones
9
请检查我优雅的两行代码解决方案,不使用Joda库并将结果以任何TimeUnit显示,详情请见https://dev59.com/c3I_5IYBdhLWcg3wBub4#10650881。 - Sebastien Lorber
8
谴责那些推荐Joda时间库而不推荐真正的Java解决方案的人... - Zizouz212
5
关于推荐Joda-Time,Java捆绑的旧日期时间类很糟糕,真的很糟糕,设计差、令人困惑且麻烦。这些类非常糟糕以至于Joda-Time成为它们的替代品并取得了巨大成功。甚至Sun/Oracle也放弃了它们,并采用了java.time包作为Java 8及更高版本的一部分。java.time类受到Joda-Time的启发。Joda-Time和java.time都由同一个人Stephen Colbourne领导。我想说,“谁要是推荐使用DateCalendarSimpleDateFormat就太不应该了”。 - Basil Bourque
4
当问题提出时,Joda Time可能是一个很好的答案,但如今对于任何能使用Java 8的人来说,最好的答案是使用java.time.Period和/或Duration。请查看下面的Basil Bourque的答案。 - Ole V.V.
显示剩余4条评论
46个回答

1
阅读了许多回答和评论后,我得出的印象是一个人要么使用Joda时间,要么考虑夏令时等一些特殊情况。由于我不想做这两件事,最终我写了几行代码来计算两个日期之间的差异,而不使用任何与日期或时间相关的Java类。
在下面的代码中,年份、月份和日期的数字与现实生活中相同。例如,在2015年12月24日,年份=2015,月份=12,日期=24。
我想分享这段代码,以防其他人想使用它。有三种方法:1)查找给定年份是否为闰年的方法;2)计算给定年份的某一天与该年1月1日的关系的方法;3)使用方法2(结束日期的编号减去开始日期的编号)计算任意两个日期之间的天数的方法。
以下是这些方法:
1)
public static boolean isLeapYear (int year) {
    //Every 4. year is a leap year, except if the year is divisible by 100 and not by 400
    //For example 1900 is not a leap year but 2000 is

    boolean result = false;

    if (year % 4 == 0) {
        result = true;
    }
    if (year % 100 == 0) {
        result = false;
    }
    if (year % 400 == 0) {
        result = true;
    }

    return result;

}

2)

public static int daysGoneSince (int yearZero, int year, int month, int day) {
    //Calculates the day number of the given date; day 1 = January 1st in the yearZero

    //Validate the input
    if (year < yearZero || month < 1 || month > 12 || day < 1 || day > 31) {
        //Throw an exception
        throw new IllegalArgumentException("Too many or too few days in month or months in year or the year is smaller than year zero");
    }
    else if (month == 4 || month == 6 || month == 9 || month == 11) {//Months with 30 days
        if (day == 31) {
            //Throw an exception
            throw new IllegalArgumentException("Too many days in month");
        }
    }
    else if (month == 2) {//February 28 or 29
        if (isLeapYear(year)) {
            if (day > 29) {
                //Throw an exception
                throw new IllegalArgumentException("Too many days in month");
            }
        }
        else if (day > 28) {
            //Throw an exception
            throw new IllegalArgumentException("Too many days in month");
        }
    }

    //Start counting days
    int days = 0;

    //Days in the target month until the target day
    days = days + day;

    //Days in the earlier months in the target year
    for (int i = 1; i < month; i++) {
        switch (i) {
            case 1: case 3: case 5:
            case 7: case 8: case 10:
            case 12:
                days = days + 31;
                break;
            case 2:
                days = days + 28;
                if (isLeapYear(year)) {
                    days = days + 1;
                }
                break;
            case 4: case 6: case 9: case 11:
                days = days + 30;
                break;
        }
    }

    //Days in the earlier years
    for (int i = yearZero; i < year; i++) {
        days = days + 365;
        if (isLeapYear(i)) {
            days = days + 1;
        }
    }

    return days;

}

3)

public static int dateDiff (int startYear, int startMonth, int startDay, int endYear, int endMonth, int endDay) {

    int yearZero;

    //daysGoneSince presupposes that the first argument be smaller or equal to the second argument
    if (10000 * startYear + 100 * startMonth + startDay > 10000 * endYear + 100 * endMonth + endDay) {//If the end date is earlier than the start date
        yearZero = endYear;
    }
    else {
        yearZero = startYear;
    }

    return daysGoneSince(yearZero, endYear, endMonth, endDay) - daysGoneSince(yearZero, startYear, startMonth, startDay);

}

1

只需使用下面的方法,使用两个 Date 对象。如果您想传递当前日期,只需将 new Date() 作为第二个参数传递,因为它已初始化为当前时间。

public String getDateDiffString(Date dateOne, Date dateTwo)
{
    long timeOne = dateOne.getTime();
    long timeTwo = dateTwo.getTime();
    long oneDay = 1000 * 60 * 60 * 24;
    long delta = (timeTwo - timeOne) / oneDay;

    if (delta > 0) {
        return "dateTwo is " + delta + " days after dateOne";
    }
    else {
        delta *= -1;
        return "dateTwo is " + delta + " days before dateOne";
     }
}

此外,除了天数之外,如果您想要其他参数差异,请使用以下代码片段。
int year = delta / 365;
int rest = delta % 365;
int month = rest / 30;
rest = rest % 30;
int weeks = rest / 7;
int days = rest % 7;

注:代码完全摘自SO答案。


很高兴看到您参与StackOverflow。请在您发布的代码示例中添加一些讨论。解释您的代码如何工作,您的答案与其他答案的区别,指出任何棘手的概念,链接到文档等等。StackOverflow的目的不仅仅是一个代码片段库。 - Basil Bourque

1

你可以尝试早期版本的Java。

 public static String daysBetween(Date createdDate, Date expiryDate) {

        Calendar createdDateCal = Calendar.getInstance();
        createdDateCal.clear();
        createdDateCal.setTime(createdDate);

        Calendar expiryDateCal = Calendar.getInstance();
        expiryDateCal.clear();
        expiryDateCal.setTime(expiryDate);


        long daysBetween = 0;
        while (createdDateCal.before(expiryDateCal)) {
            createdDateCal.add(Calendar.DAY_OF_MONTH, 1);
            daysBetween++;
        }
        return daysBetween+"";
    }

1

try this:

int epoch = (int) (new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm:ss").parse("01/01/1970  00:00:00").getTime() / 1000);

你可以编辑parse()方法参数中的字符串。

1

由于您正在使用Scala,因此有一个非常好的Scala库Lamma。 使用Lamma,您可以直接使用-运算符减去日期。

scala> Date(2015, 5, 5) - 2     // minus days by int
res1: io.lamma.Date = Date(2015,5,3)

scala> Date(2015, 5, 15) - Date(2015, 5, 8)   // minus two days => difference between two days
res2: Int = 7

0
以下是一种解决方案,因为我们可以采用多种方法来实现这个目标:
  import java.util.*; 
   int syear = 2000;
   int eyear = 2000;
   int smonth = 2;//Feb
   int emonth = 3;//Mar
   int sday = 27;
   int eday = 1;
   Date startDate = new Date(syear-1900,smonth-1,sday);
   Date endDate = new Date(eyear-1900,emonth-1,eday);
   int difInDays = (int) ((endDate.getTime() - startDate.getTime())/(1000*60*60*24));

0

这是另一个示例。基本上适用于用户定义的模式。

   public static LinkedHashMap<String, Object> checkDateDiff(DateTimeFormatter dtfObj, String startDate, String endDate)
   {
          Map<String, Object> dateDiffMap = new HashMap<String, Object>();
          DateTime start = DateTime.parse(startDate,dtfObj);
          DateTime end = DateTime.parse(endDate,dtfObj);
          Interval interval = new Interval(start, end);
          Period period = interval.toPeriod();

          dateDiffMap.put("ISO-8601_PERIOD_FORMAT", period);
          dateDiffMap.put("YEAR", period.getYears());
          dateDiffMap.put("MONTH", period.getMonths());
          dateDiffMap.put("WEEK", period.getWeeks());
          dateDiffMap.put("DAY", period.getWeeks());             
          dateDiffMap.put("HOUR", period.getHours());
          dateDiffMap.put("MINUTE", period.getMinutes());
          dateDiffMap.put("SECOND", period.getSeconds());

          return dateDiffMap;        
   }

感谢您尝试为StackOverflow做出贡献。我建议您添加一些讨论或解释。StackOverflow的目的不仅仅是代码片段库。因此,您应该解释(a)您的代码如何工作,以及(b)它如何在已发布的许多其他答案之上增加价值。此外,这段代码不完整,例如dtfObj等变量没有声明或赋值。最后,顺便提一下,我提供这个提示:您可以在第一行左侧使用更通用的“Map”接口,而不是“LinkedHashMap”。这是多态性的一个点/好处之一。 - Basil Bourque
@BasilBourque:你好,我已经做出了预期的更改,并且一定会尽力添加更多的解释。 - The N..

0
public static void main(String[] args) {

    String dateStart = "01/14/2012 09:29:58";
    String dateStop = "01/14/2012 10:31:48";

    SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

    Date d1 = null;
    Date d2 = null;

    try {
        d1 = format.parse(dateStart);
        d2 = format.parse(dateStop);

        DateTime date11 = new DateTime(d1);
        DateTime date22 = new DateTime(d2);
        int days = Days.daysBetween(date11.withTimeAtStartOfDay(), date22.withTimeAtStartOfDay()).getDays();
        int hours = Hours.hoursBetween(date11, date22).getHours() % 24;
        int minutes = Minutes.minutesBetween(date11, date22).getMinutes() % 60;
        int seconds = Seconds.secondsBetween(date11, date22).getSeconds() % 60;
        if (hours > 0 || minutes > 0 || seconds > 0) {
            days = days + 1;
        }

        System.out.println(days);

    } catch (Exception e) {
        e.printStackTrace();
    }

}

这将同时给出同一天的日期差异


1
能否添加一些解释,说明你的答案有什么附加价值?这似乎与此答案此答案重复了。其他答案似乎更好,比如利用TimeUnit枚举实用类。 - Basil Bourque
实际上,这些实现中的问题是它对于同一天的日期时间计数为0...但是使用此方法,您可以获得相同日期的差异为1,此外这取决于@BasilBourque。 - spysr

0

在Java中有一种简单的方法可以做到这一点。

//创建一个实用方法

public long getDaysBetweenDates(Date d1, Date d2){
return TimeUnit.MILLISECONDS.toDays(d1.getTime() - d2.getTime());
}

该方法将返回两个日期之间的天数。您可以使用默认的Java日期格式,也可以轻松地从任何日期格式进行转换。


3
假设一天中的毫秒数是恒定的,但这并不总是正确的。 - pr1001
是的,正如@pr1001所提到的。如果你在今天晚上22:00测量,后天在00:00测量呢?你会得到1.08,这将导致差异为1天,但实际上应该是2天! - M22

0

@Michael Borgwardt的答案在Android上实际上并不正确。存在舍入误差。例如,从5月19日到21日显示为1天,因为它将1.99转换为1。在强制转换为int之前使用round函数。

修复方法

int diffInDays = (int)Math.round(( (newerDate.getTime() - olderDate.getTime()) 
                 / (1000 * 60 * 60 * 24) ))

请注意,这适用于UTC日期,因此如果您查看本地日期,则差异可能会偏离一天。要使其与本地日期正确配合需要完全不同的方法,这是由于夏令时的缘故。

只要两个时间戳在同一个时区,那么日期之间的差异无论是否从UTC调整为本地时区(调整会被减去),都将是相同的。结果可能需要进行夏令时校正,但如果您假设一天是86400秒,则实际上并不重要。 - scottb
这不是答案的重点。除非您在Android的Java SDK中四舍五入并转换为int,否则Michael Brogwardt的答案会导致错误的结果。 - Pratik Mandrekar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接