Java:计算跨越多个月份的两个日期之间的差异

4
我有以下代码,成功地得出了两个日期之间的时间差(以天、小时、分钟和秒为单位):
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");
Date d1 = format.parse(startTime);
Date d2 = format.parse(endTime);
long diff = d2.getTime() - d1.getTime();
long diffSeconds = diff / 1000 % 60;
long diffMinutes = diff / (60 * 1000) % 60;
long diffHours = diff / (60 * 60 * 1000) % 24;
long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.println("Start: " + startTime);
System.out.println("End: " + endTime);
System.out.println(diffDays + " days, " + diffHours + " hours, " + diffMinutes + " minutes, " + diffSeconds + " seconds");

然而,当日期跨越到另一个月份时,这种方法就不起作用了,例如:
Start: 2013-07-31 10:15:01
End: 2013-08-01 11:22:33
-29 days, -22 hours, -52 minutes, -28 seconds

Start: 2013-05-31 10:15:01
End: 2013-08-01 11:22:33
-29 days, -22 hours, -52 minutes, -28 seconds

能否智能地跨越月份并获取准确的时间差?我熟悉Joda,但是想坚持使用标准的Java API,除非没有Joda这样的东西是不可能的。


1
你可以重新实现 Joda Time 中的一些工作,毕竟它不是魔法,但最好还是直接使用 Joda Time。话虽如此,在这种情况下并不清楚你是否需要 Joda Time - 我认为你只是需要修复格式化程序... - Jon Skeet
4个回答

5

虽然您应该将Joda Time仅用作更好的日期/时间API,但我怀疑问题实际上只是您错误解析了值。请使用以下格式字符串进行解析:

yyyy-MM-dd HH:mm:ss

使用dd而不是DD。我怀疑DD的值是“一年中的日”,这可能会造成混淆,基本上完全覆盖了月份部分。有关详细信息,请参见SimpleDateFormat文档。

您可以通过解析后打印d1d2来验证问题所在...


4

你正在使用

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");

应该是

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

你将获得正确的结果。
Start: 2013-05-31 10:15:01
End: 2013-08-01 11:22:33
62 days, 1 hours, 7 minutes, 32 seconds

这里是日期格式的 javadoc。其中,dd 表示月中的天数,DD 表示年中的天数。你的 Date 对象没有按照你的预期解析。基本上,31 的年中天数值正在覆盖月份值。
调试工具是你的好朋友。

天啊,我想我应该更加关注那些Javadocs。谢谢你的回复。 - Matt
@Matt 当出现这种情况时,最好在使用它们之前检查您的日期(或其他)对象的值。 - Sotirios Delimanolis

2

太长没看

Java 9及以后版本中...

Duration.between(
    LocalDateTime.parse( "2013-07-31T10:15:01" ) ,
    LocalDateTime.parse( "2013-08-01T11:22:33" )
).getDaysPart()

"...和..."
.getHoursPart()
.getMinutesPart()
.getSecondsPart()

java.time

其他答案已经过时。Joda-Time项目现在处于维护模式,并建议迁移到Java内置的java.time类。
Period
Duration类表示与时间轴无关的一段时间,作为总秒数和纳秒数。这样的时间段可以被解释为一定数量的天、小时、分钟和秒,假设没有包含任何时区,使用通用的24小时制和60分钟制。
LocalDateTime start = LocalDateTime.parse( "2013-07-31T10:15:01" );
LocalDateTime stop = LocalDateTime.parse( "2013-08-01T11:22:33" );
Duration d = Duration.between( start , stop );
d.toString(): PT25H7M32S
这些字符串采用标准的ISO 8601格式。字符串“PT25H7M32S”使用PnYnMnDTnHnMnS格式,其中P表示开始,“T”分隔年-月-日和小时-分钟-秒。字符串PT25H7M32S表示“二十五小时七分钟三十二秒”。
请参见 IdeOne.com中的实时代码
在Java 9及更高版本(但不包括Java 8)中,可以使用这些Duration方法提取每个部分。
给定持续时间为49H30M20.123S...
  • toDaysPart() = 2 (天)
  • toHoursPart() = 1(小时)
  • toMinutesPart() = 30(分钟)
  • toSecondsPart() = 20(秒)
  • toMillisPart() = 123(毫秒)
  • toNanosPart() = 123000000(纳秒)

关于java.time

java.time框架内置于Java 8及更高版本中。这些类替代了旧的日期和时间类,如java.util.Date.Calendarjava.text.SimpleDateFormat

Joda-Time项目现在处于维护模式,建议迁移到java.time。

要了解更多信息,请参阅Oracle教程。并在Stack Overflow上搜索许多示例和解释。

大部分java.time的功能被迁移回到Java 6和7中,使用ThreeTen-Backport,并在ThreeTenABP中进一步适应Android(请参见如何使用…)。
该项目是ThreeTen-Extra,它扩展了java.time的额外类。这个项目是java.time可能未来添加的一个试验场。你可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter更多

1

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