Java,获取两个日期之间的天数

5
在Java中,我想要获取两个日期之间的天数,但不包括这两个日期。
例如:
如果第一个日期为2011年11月11日,第二个日期为2011年11月13日,那么应该是1
这是我正在使用的代码,但不起作用(secondDatefirstDateCalendar对象):
long diff=secondDate.getTimeInMillis()-firstDate.getTimeInMillis();
float day_count=(float)diff / (24 * 60 * 60 * 1000);
daysCount.setText((int)day_count+"");                    

我甚至尝试四舍五入结果,但那也没有帮助。

在Java中,如何获取日期之间的天数,不包括这些日期本身?


你在计算之前是否对日期进行了规范化?我的意思是,你必须清除HOUR_OF_DAY、MINUTE、SECOND和MILLISEC字段。 - slkorolev
@slkorolev:哦...我真的不知道。让我再试一次。 - Hiral Vadodaria
2
可能是Java中两个日期之间的天数差异?的重复问题。 - marcolopes
现代化的注释:避免使用Java 1.0和1.1中的CalendarGregorianCalendar和其他日期和时间类。使用现代的LocalDate来表示日期。 - Ole V.V.
6个回答

12

我刚刚在SDK 8(Android 2.2)上测试了以下代码片段:

Calendar date1 = Calendar.getInstance();
Calendar date2 = Calendar.getInstance();

date1.clear();
date1.set(
   datePicker1.getYear(),
   datePicker1.getMonth(),
   datePicker1.getDayOfMonth());
date2.clear();
date2.set(
   datePicker2.getYear(),
   datePicker2.getMonth(),
   datePicker2.getDayOfMonth());

long diff = date2.getTimeInMillis() - date1.getTimeInMillis();

float dayCount = (float) diff / (24 * 60 * 60 * 1000);

textView.setText(Long.toString(diff) + " " + (int) dayCount);

它可以完美地工作,在两种情况下(2011年11月10日至2011年11月8日和2011年11月13日至2011年11月11日),都给出 dayCount = 2.0


2
两个注意事项:1.不要使用浮点数将毫秒转换为天数。2.为了从计算中排除这两天,从“dayCount”中减去1。 - Aaron Digulla
1
@Aaron:感谢您的评论,我同意。我认为原始问题的原因不在计算中,而是在于日历初始化。 - slkorolev
datePicker1是什么?我怎样能用这段代码来使用我的日历对象呢? - Hiral Vadodaria
@Hiral:dattePicker1和2只是我的测试应用程序中的复合组件。它们存储输入日期的年、月和日。实际上,这段代码片段的主要目的是首先清除所有日期字段,然后仅设置年、月和日: date1.clear(); date1.set(year, month, dayOfMonth); - slkorolev
3
当涉及到夏令时时区的时候,比如在中央时区,"2013年3月10日"和"2013年3月12日"之间就会出现问题。 - Luiggi Mendoza
1
不行!这个答案是错误的。如果day2在夏令时而day1在非夏令时,那么答案会偏移一天。 - Dawood ibn Kareem

7

获取两个java.util.Date之间的天数,忽略夏令时

以下是一种简单的方法:

public int get_days_between_dates(Date date1, Date date2)
{       
    //if date2 is more in the future than date1 then the result will be negative
    //if date1 is more in the future than date2 then the result will be positive.

    return (int)((date2.getTime() - date1.getTime()) / (1000*60*60*24l));
}

这个函数在99.99%的情况下都有效,除了在闰秒、夏令时、时区变化、闰年等边缘情况下可能会出现意外。如果你可以接受偶尔计算偏差为1(或2)小时,那么这就足够了。

获取两个日期之间的天数,考虑到闰秒、夏令时、时区等因素

如果你问这个问题,你需要扇自己一巴掌。两个日期至少相隔1天是什么意思?这很令人困惑。如果一个日期是某个时区的午夜,而另一个日期是另一个时区的凌晨1点,怎么办?根据您的解释方式,答案既是1又是0。

您认为可以将传递到上述函数中的日期强制转换为通用时间格式;这样可以解决一些问题。但是,您只是将问题转移到如何将本地时间转换为通用时间上。从您的时区到通用时间的逻辑转换可能不是直观的。在某些情况下,即使传递的日期显然相隔两天,您也会得到一天的差异。

您认为您可以处理吗?世界上有一些简单的日历系统,这些系统随着收获季节和安装的政治统治者而不断变化。如果您想将它们的时间转换为UTC,则java.util.Date会在最糟糕的时刻让您失望。

如果您需要计算日期之间的天数,并且所有结果都正确至关重要,则需要获取名为Joda Time的外部库(他们已经为您处理了所有细节,因此您可以保持幸福无知):http://joda-time.sourceforge.net/index.html


1
+1 建议使用 Joda Time,这最好可以成为您回答的第一句话。请注意,在大约 25% 的情况下,您的“快速脏代码”将会出现整整一天的错误,而不仅仅是 1 或 2 小时;也就是说,当 day2 发生在夏令时期间而 day1 没有发生时,它将无法像您所声称的那样“99.99% 的时间正常工作”。 - Dawood ibn Kareem
1
如果您不想要负值,可以在其中添加一个Math.abs(diff)。 - Philippe Carriere
我还会添加一个 Math.round(diff) 来确保您不会将值向下取整。 - Philippe Carriere
我知道这很老,但是 Java 的 Calendar 类不能解决这个问题吗? - emiliopedrollo

2

java.time

Java-8在2014年3月发布了java.time API,取代了过时的日期时间API。从那以后,强烈推荐使用这个现代化的日期时间API。

使用现代日期时间API的解决方案

使用{{link2:Calendar#toInstant}},将您的java.util.Calendar实例转换为java.time.Instant,然后再转换为java.time.ZonedDateTime实例,然后使用{{link3:ChronoUnit.DAYS.between}}获取它们之间的天数。

演示

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;

public class Main {

    public static void main(String[] args) {
        // Sample start and end dates as java.util.Date
        Calendar startCal = Calendar.getInstance();
        startCal.set(2011, 10, 11); // 11 November 2011

        Calendar endCal = Calendar.getInstance();
        endCal.set(2011, 10, 13); // 13 November 2011

        // Convert the java.util.Calendar into java.time.ZonedDateTime
        // Replace ZoneId.systemDefault() with the applicable ZoneId
        ZonedDateTime startDateTime = startCal.toInstant().atZone(ZoneId.systemDefault());
        ZonedDateTime endDateTime = endCal.toInstant().atZone(ZoneId.systemDefault());

        // The end date is excluded by default. Subtract 1 to exclude the start date
        long days = ChronoUnit.DAYS.between(startDateTime, endDateTime) - 1;
        System.out.println(days);
    }
}

Output:

1

了解有关现代日期时间 API 的更多信息,请访问教程:日期时间

0
如果你只需要处理1900年至2100年之间的日期,有一个简单的计算方法可以让你得到自1900年以来的天数:
public static int daysSince1900(Date date) {
    Calendar c = new GregorianCalendar();
    c.setTime(date);

    int year = c.get(Calendar.YEAR);
    if (year < 1900 || year > 2099) {
        throw new IllegalArgumentException("daysSince1900 - Date must be between 1900 and 2099");
    }
    year -= 1900;
    int month = c.get(Calendar.MONTH) + 1;
    int days = c.get(Calendar.DAY_OF_MONTH);

    if (month < 3) {
        month += 12;
        year--;
    }
    int yearDays = (int) (year * 365.25);
    int monthDays = (int) ((month + 1) * 30.61);

    return (yearDays + monthDays + days - 63);
}

因此,要获取两个日期之间的天数差异,您需要计算它们自1900年以来的天数并计算差异。我们的daysBetween方法如下:

public static Integer getDaysBetween(Date date1, Date date2) {
    if (date1 == null || date2 == null) {
        return null;
    }

    int days1 = daysSince1900(date1);
    int days2 = daysSince1900(date2);

    if (days1 < days2) {
        return days2 - days1;
    } else {
        return days1 - days2;
    }
}

在你的情况下,如果天数不相等,你需要减去一天。
不要问我这个计算是从哪里来的,因为我们从90年代初就一直在使用它。

0

我有两个建议:

  1. 确保你的 float day_count 计算正确

    float day_count = ((float)diff) / (24f * 60f * 60f * 1000f);

  2. 如果是舍入误差,尝试使用 floor 方法

    daysCount.setText("" + (int)Math.floor(day_count));


我应该像slkorolev建议的那样清除HOUR_OF_DAY、MINUTE、SECOND和MILLISEC字段吗?还是我可以按原样使用它们? - Hiral Vadodaria
@Hiral 我认为用这种方法是不必要的。尽管我还没有测试过。 - Vladimir
我已经尝试了这两个选项,无论是否设置这些字段,都没有成功!不管怎样,感谢您的回复! - Hiral Vadodaria
@Hiral,你能发布一下设置“firstDate”和“secondDate”的代码吗? - Vladimir
实际上我正在使用NumberPickerDialog,它可以根据需要提供日期,并且它运行得非常完美。在设置日期方面没有任何问题。 - Hiral Vadodaria

0
  1. 不要在整数计算中使用浮点数。

  2. 你确定你的日期是天吗?Date类型的精度是毫秒。所以你需要做的第一件事就是将日期四舍五入到没有小时的时间。例如:从2011-11-01 23:302011-11-02 00:30只有一个小时,但这两个日期属于不同的天。


在计算天数之前,我将两个日期的HOUR_OF_DAY、MINUTE、SECOND和MILLISEC字段都设置为0,这样做可以吗?还是会有其他问题? - Hiral Vadodaria
那应该就可以了。如果这不起作用,你应该在你的“dayCount”中看到分数。 - Aaron Digulla

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