Android两个日期之间的天数

50

我希望在我的Android应用中比较两个日期,但我遇到了一个非常奇怪的问题。

例如:

如果我将“回到过去”的日期设置为127天前:

this.dateEvent = System.currentTimeMillis() - (127 * 24 * 3600 * 1000)

然后将其与当前日期进行比较(计算天数)

    Calendar sDate = getDatePart(new Date(this.dateEvent));
    Calendar eDate = getDatePart(new Date(System.currentTimeMillis()));

    int daysBetween = 0;
    while (sDate.before(eDate))
    {
        sDate.add(Calendar.DAY_OF_MONTH, 1);
        daysBetween ++;
    }

    while (sDate.after(eDate))
    {
        eDate.add(Calendar.DAY_OF_MONTH, 1);
        daysBetween ++;
    }

    return daysBetween;

它将返回22,这完全不是预期的结果。

我是否做错了什么,还是Calendar类存在问题?


dateEvent 的声明是什么样子的? - keyser
你总是添加DAY_OF_MONTH(因此,在31或30或28或29之后,它将从1重新开始计数),而不是DAY。在第一种情况下,我会添加一个负数(-1)。 - Phantômaxx
你好,dateEvent 声明为 private long dateEvent = 0L;。我也尝试将 DAY_OF_MONTH 替换为 DAY_OF_YEAR,但这并没有解决问题。 - Manitoba
1
我建议您使用现代Java日期和时间API - java.time来处理日期。Calendar类设计不佳且已经过时,因此请勿使用它。与Calendar相反,java.time不仅更易于使用,而且还具有直接支持查找两个日期之间天数的功能。 - Ole V.V.
我在这里提供了超级简单的解决方案 https://dev59.com/QVgQ5IYBdhLWcg3wkE3m#65551309 - Abhinav Chauhan
19个回答

134

以下是一个两行的解决方案:

long msDiff = Calendar.getInstance().getTimeInMillis() - testCalendar.getTimeInMillis();
long daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff);

这个例子获取了日期"testCalendar"和当前日期之间的天数。


2
啊,这正是我所需要的。谢谢! - Tobias Reich
哇!!谢谢 :) - gunjot singh
3
夏令时时区存在问题。 - Amir Hossein Ghasemi

22
请参考这段代码,它可能会对您有帮助。
public String getCountOfDays(String createdDateString, String expireDateString) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());

    Date createdConvertedDate = null, expireCovertedDate = null, todayWithZeroTime = null;
    try {
        createdConvertedDate = dateFormat.parse(createdDateString);
        expireCovertedDate = dateFormat.parse(expireDateString);

        Date today = new Date();

        todayWithZeroTime = dateFormat.parse(dateFormat.format(today));
    } catch (ParseException e) {
        e.printStackTrace();
    }

    int cYear = 0, cMonth = 0, cDay = 0;

    if (createdConvertedDate.after(todayWithZeroTime)) {
        Calendar cCal = Calendar.getInstance();
        cCal.setTime(createdConvertedDate);
        cYear = cCal.get(Calendar.YEAR);
        cMonth = cCal.get(Calendar.MONTH);
        cDay = cCal.get(Calendar.DAY_OF_MONTH);

    } else {
        Calendar cCal = Calendar.getInstance();
        cCal.setTime(todayWithZeroTime);
        cYear = cCal.get(Calendar.YEAR);
        cMonth = cCal.get(Calendar.MONTH);
        cDay = cCal.get(Calendar.DAY_OF_MONTH);
    }


    /*Calendar todayCal = Calendar.getInstance();
    int todayYear = todayCal.get(Calendar.YEAR);
    int today = todayCal.get(Calendar.MONTH);
    int todayDay = todayCal.get(Calendar.DAY_OF_MONTH);
    */

    Calendar eCal = Calendar.getInstance();
    eCal.setTime(expireCovertedDate);

    int eYear = eCal.get(Calendar.YEAR);
    int eMonth = eCal.get(Calendar.MONTH);
    int eDay = eCal.get(Calendar.DAY_OF_MONTH);

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

    date1.clear();
    date1.set(cYear, cMonth, cDay);
    date2.clear();
    date2.set(eYear, eMonth, eDay);

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

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

    return ("" + (int) dayCount + " Days");
}

你好,感谢报告这个错误。如果“date1”指向与“date2”相同的日期,则程序将无法正常工作。 - Manitoba
2
正是我所寻找的,谢谢!long diff = date2.getTimeInMillis() - date1.getTimeInMillis(); float dayCount = (float) diff / (24 * 60 * 60 * 1000); - Bram
2
但是如果用户手动更改设备日期,这可能会被伪造...如何解决? - statosdotcom
@statosdotcom 在您的服务器中设置Unix格式的日期并每天更新。 - Izhan Ali

9

您不应该使用类似24 * 60 * 60 * 1000这样的公式!为什么?因为有夏令时,不是所有的日子都有24小时,还有闰年,它有额外的1天。这就是为什么有一个日历类。

如果您不想将任何外部库添加到项目中,如Jodatime,那么您可以使用纯Calendar类,它具有非常高效的功能:

public static int numDaysBetween(final Calendar c, final long fromTime, final long toTime) {
    int result = 0;
    if (toTime <= fromTime) return result;

    c.setTimeInMillis(toTime);
    final int toYear = c.get(Calendar.YEAR);
    result += c.get(Calendar.DAY_OF_YEAR);

    c.setTimeInMillis(fromTime);
    result -= c.get(Calendar.DAY_OF_YEAR);

    while (c.get(Calendar.YEAR) < toYear) {
        result += c.getActualMaximum(Calendar.DAY_OF_YEAR);
        c.add(Calendar.YEAR, 1);
    }

    return result;
}

2
这是最好的答案! - Rodrigo Gontijo

9

我终于找到了最简单的处理方法。以下是我的代码:

public int getTimeRemaining()
{
    Calendar sDate = toCalendar(this.dateEvent);
    Calendar eDate = toCalendar(System.currentTimeMillis());

    // Get the represented date in milliseconds
    long milis1 = sDate.getTimeInMillis();
    long milis2 = eDate.getTimeInMillis();

    // Calculate difference in milliseconds
    long diff = Math.abs(milis2 - milis1);

    return (int)(diff / (24 * 60 * 60 * 1000));
}

private Calendar toCalendar(long timestamp)
{
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(timestamp);
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    return calendar;
}

希望能对您有所帮助。

我修改了Calendar.HOUR为Calendar.HOUR_OF_DAY后,这个方法对我起作用了。 - ePeace
Calendar类设计不良的另一个令人困惑的特性:HOUR不意味着HOUR_OF_DAY - Ole V.V.

7
public long Daybetween(String date1,String date2,String pattern)
{
    SimpleDateFormat sdf = new SimpleDateFormat(pattern,Locale.ENGLISH);
    Date Date1 = null,Date2 = null;
    try{
        Date1 = sdf.parse(date1);
        Date2 = sdf.parse(date2);
    }catch(Exception e)
    {
        e.printStackTrace();
    }
    return (Date2.getTime() - Date1.getTime())/(24*60*60*1000);
}

7
    Date userDob = new SimpleDateFormat("yyyy-MM-dd").parse(dob);
    Date today = new Date();
    long diff =  today.getTime() - userDob.getTime();
    int numOfYear = (int) ((diff / (1000 * 60 * 60 * 24))/365);
    int numOfDays = (int) (diff / (1000 * 60 * 60 * 24));
    int hours = (int) (diff / (1000 * 60 * 60));
    int minutes = (int) (diff / (1000 * 60));
    int seconds = (int) (diff / (1000));

3

我有同样的需求,最终我选择使用Joda Time,在提供你所需要的功能的同时还非常方便且提供了许多附加函数。

你可以从这里下载文件。

一旦将jar文件包含在您的项目中,您就可以轻松地执行以下操作:

int daysBetween = Days.daysBetween(new DateTime(sDate), new DateTime(eDate)).getDays();

2
Joda时间库将向您的项目中添加4744个方法。如果想避免65K方法限制,请谨慎选择。 - Lior Iluz
添加JAR文件对于简单的计算来说真的不明智。要注意方法限制,就像@LiorIluz所提到的那样。 - sud007

2

最佳方法:

        long fromCalender = Calender.getInstance();
        fromCalender.set...// set the from dates
        long toCalender = Calender.getInstance();
        fromCalender.set...// set the to dates

        long diffmili = fromCalender - toCalender;

        long hours = TimeUnit.MILLISECONDS.toHours(diffmili);
        long days = TimeUnit.MILLISECONDS.toDays(diffmili);
        long min = TimeUnit.MILLISECONDS.toMinutes(diffmili);
        long sec = TimeUnit.MILLISECONDS.toSeconds(diffmili);

在发布之前,您应该测试您的解决方案,Calendar中存在复制/粘贴错误和拼写错误。 - Bogy

2

java.time和ThreeTenABP

我想提供现代的解决方案:使用java.time,这是现代Java日期和时间API来处理您的日期。如果是为了开发Android API 25或更低版本,则可以通过Android的向后兼容包ThreeTenABP(链接在底部)来实现。

    LocalDate eDate = LocalDate.now(ZoneId.of("Europe/Paris"));
    LocalDate sDate = eDate.minusDays(127);

    long daysBetween = ChronoUnit.DAYS.between(sDate, eDate);
    System.out.println(daysBetween);

当我今天运行这段代码时,输出结果如下所示:

127

请注意,这段代码不仅更短,只有一行用于查找差异;而且更清晰、更自然。您使用的类和设计得很差,已经过时了。我建议您不要使用它们。

您的代码出了什么问题?

在将127天转换为毫秒时,您的代码发生了int溢出。在数学上,127 * 24 * 3600 * 1000等于10,972,800,000。由于您相乘的数字是int类型,Java会在int中执行乘法,而int可以容纳的最大数字是2,147,483,647,远远不足以满足您的预期结果。在这种情况下,如果Java抛出异常或以其他方式使我们意识到错误,那就太好了。但它没有。它默认丢弃高位比特,给我们一个结果为-1,912,101,888。从当前时间减去这个负数相当于添加了22天和几个小时。这就解释了为什么您得到了22。有趣的是,已经发布了13个答案,似乎没有人发现这个问题...
即使使用long类型进行乘法运算,仍无法正确计算127天。如果127天跨越夏令时(DST)的转换,这在法国是一年中365天中的254天的情况下发生,那么转换日不是24小时,而是23或25小时。这会导致毫秒数不正确。
您应该始终将日期计算留给经过验证的库方法。永远不要自己编写代码。它比大多数人想象的更复杂,因此出错的风险很高。

问题:java.time是否需要Android API级别26?

java.time在旧版和新版Android设备上都可以很好地工作。它只需要至少Java 6。
  • 在Java 8及更高版本和更新的Android设备(从API级别26开始),现代API内置。
  • 在非Android Java 6和7中使用ThreeTen Backport,即现代类的后移(JSR 310的ThreeTen;请参见底部链接)。
  • 在(较旧的)Android上使用ThreeTen Backport的Android版本。它被称为ThreeTenABP。并确保从org.threeten.bp的子包中导入日期和时间类。

链接


1
更新:java.time现在可以在API <26中原生使用,无需ThreeTehnABP。您只需要使用Android 4.0并添加一些简单的配置:developer.android.com/studio/preview/features#j8-desugar - 7200rpm

2

按照以下方式操作即可支持所有API级别

    Calendar cal = Calendar.getInstance();
    SimpleDateFormat sdf = new SimpleDateFormat("MMM dd yyyy HH:mm:ss", 
    Locale.ENGLISH);
    try {
        String datestart="June 14 2018 16:02:37";
        cal.setTime(sdf.parse(datestart));// all done
         Calendar cal1=Calendar.getInstance();
        String formatted = sdf.format(cal1.getTime());//formatted date as i want
        cal1.setTime(sdf.parse(formatted));// all done

        long msDiff = cal1.getTimeInMillis() - cal.getTimeInMillis();
        long daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff);
        Toast.makeText(this, "days="+daysDiff, Toast.LENGTH_SHORT).show();
    } catch (ParseException e) {
        e.printStackTrace();
    }

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