【问题标题】:Difference between two dates, in days, varies两个日期之间的差异(以天为单位)会有所不同
【发布时间】:2014-02-11 21:49:27
【问题描述】:
Date d = new Date(today.getTimeInMillis());
Date d1 = new Date(dueDate.getTimeInMillis());

int daysUntil = (int) ((d1.getTime() - d.getTime())/ (1000 * 60 * 60 * 24));

使用上面的代码,其中today 是在当天设置为 00:00 的日历,dueDate 在我今天比较的日期设置为 00:00,我的结果不同。

其中有一些变化,使我的输出为 x 或 x+1,其中 x 是正确答案。

这里有什么问题,我该怎么做才能使它更稳定?

【问题讨论】:

  • 注意:我已经设法通过简单地将 1000 添加到 'dueDate.getTimeInMillis()' 来解决此问题,但这更像是一种解决方法。是什么导致了这个问题,如何解决?
  • 你能打印出today.getTimeInMillis()dueDate.getTimeInMillis()的值吗?
  • 不知道是不是闰秒造成的? - 特别是,阅读 Date 的规范,“一秒由 0 到 61 之间的整数表示;值 60 和 61 仅出现在闰秒,即使如此,也仅出现在实际正确跟踪闰秒的 Java 实现中。”所以你不能假设一天是 60*60*24 秒长。
  • "today 是一个设置为 00:00 的日历" 那么毫秒呢?它们也是getTimeInMillis() 结果的一部分。

标签: java android date time calendar


【解决方案1】:

模糊的问题

您没有提供实际值,因此我们无法准确确定问题所在。我们不知道todaydueDate 变量是什么。

过时

这个问题现在已经过时了,因为包括 java.util.Date/.Calendar 在内的麻烦的旧日期时间类已被新的java.time 框架取代。见Tutorial。由JSR 310 定义,受Joda-Time 启发,并由ThreeTen-Extra 项目扩展。

在 java.time 中:

  • Instant 是 UTC 时间线上的时刻。
  • ZoneId 表示时区。使用正确的时区名称,不要使用“EST”或“IST”之类的 3-4 个字母代码,因为它们既不标准化也不唯一。
  • 从概念上讲,ZonedDateTime = Instant + ZoneId。

三十加分

不幸的是,java.time 不包括计算日期时间值之间经过的天数的工具。我们可以使用ThreeTen-Extra 项目及其Days 类和between 方法来提供该计算。 ThreeTen-Extra 项目是一组在 JSR 过程中被认为对 java.time 不重要的特性。

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
ZonedDateTime then = now.minusDays ( 4 );
ZonedDateTime due = now.plusDays ( 3 );
Integer days = org.threeten.extra.Days.between ( then , due ).getAmount ();

转储到控制台。

System.out.println ( "From then: " + then + " to due: " + due + " = days: " + days );

从那时起:2015-10-31T16:01:13.082-04:00[美国/蒙特利尔] 截止日期:2015-11-07T16:01:13.082-05:00[美国/蒙特利尔] = 天数:7

乔达时间

对于 Android 或更早版本的 Java,请使用出色的 Joda-Time 库。

Days 类很聪明,可以处理 Daylight Saving Time (DST) 等异常情况。

请注意,与 java.util.Date 不同,Joda-Time DateTime 对象知道自己的时区。

// Specify a time zone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "America/Regina" ); // Or "Europe/London".

DateTime now = new DateTime( timeZone );
DateTime startOfToday = now.withTimeAtStartOfDay();

DateTime fewDaysFromNow = now.plusDays( 3 );
DateTime startOfAnotherDay = fewDaysFromNow.withTimeAtStartOfDay();

Days days = Days.daysBetween( startOfToday, startOfAnotherDay );

转储到控制台...

System.out.println( days.getDays() + " days between " + startOfToday + " and " + startOfAnotherDay + "." );

运行时……

3 days between 2014-01-21T00:00:00.000-06:00 and 2014-01-24T00:00:00.000-06:00.

【讨论】:

  • 是的,“对于 java.time 来说不是必需的”是 Threeten-Extra 关于计算经过天数的正确标签;-) 您可以简单地使用 @987654348 而不是使用 Days 类@。这里不需要 Threeten-Extra,Java-8 更简单——一个方法调用更少。
【解决方案2】:

您的代码损坏主要有两个原因:

  • 秒部分或毫秒分数(您可能忽略了)
  • 夏令时效果

我演示并解释第二个原因。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d1 = sdf.parse("2016-03-20");
Date d2 = sdf.parse("2016-03-28");
int daysUntil = (int) ((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
System.out.println(daysUntil); // 7 (should be 8)

代码在“欧洲/柏林”时区运行。由于从冬季时间到夏季时间的变化导致时钟在 2016 年 3 月 27 日凌晨 2 点向前跳动一小时,因此缺少一小时。一天只有 23 小时,因此除以 24 得出零,从而减少一天。

你还能做什么?

您将 1000 毫秒添加到 dueDate 的解决方法听起来好像您忽略了输入中可能的毫秒增量。这可能会解决一种特殊情况,但通常也不足以解决夏令时问题。无论您根据java.util.Date 选择什么,它或多或少都是一种邪恶的黑客攻击。

我想到的最好的方法(在 Android 内置内容的范围内)是 构造一个 java.util.GregorianCalendar 的实例,然后一天接一天地添加,直到你过了截止日期,然后计算你增加了多少天。不优雅且容易出错,因为在这里也很容易忽略不同的毫秒部分。

否则,您可以为此任务尝试各种外部库。 Android 上有四种可用的方法可以轻松计算经过的天数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-29
    • 2012-01-03
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 2012-09-13
    • 1970-01-01
    相关资源
    最近更新 更多