我正在尝试计算两天之间的天数差异。由于某种原因,比较 01-03-2013 和 01-04-2013 给出的结果是 30,比较 01-03-2013 和 31-03-2013 的结果也是如此
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2013, Calendar.MARCH, 1);
Date start = cal.getTime();
cal.set(2013, Calendar.APRIL, 1);
Date end = cal.getTime();
long days = TimeUnit.MILLISECONDS.toDays(end.getTime() - start.getTime());
System.out.println("!!! Amount of days : " + String.valueOf(days));
>> 30
cal.set(2013, Calendar.MARCH, 1);
start = cal.getTime();
cal.set(2013, Calendar.MARCH, 31);
end = cal.getTime();
days = TimeUnit.MILLISECONDS.toDays(end.getTime() - start.getTime());
System.out.println("!!! Amount of days : " + String.valueOf(days));
>> 30
这是为什么?
如果您所在时区的夏令时于 3 月 31 日开始,您将获得这些结果。
由于夏令时开始,3 月 1 日至 4 月 1 日期间,有 30 个 24 小时制天和 1 个 23 小时制天。 如果将总毫秒数除以 24 x 60 x 60 x 1000,则将得到 30 加 23/24。 该值向下舍入为 30。
David Wallace 的正确答案解释说,夏令时或其他异常会影响代码的结果。依赖默认时区(或完全忽略时区)会让您陷入这种麻烦。
此外,定义时间跨度的正确方法是使开始具有包容性,使结束具有排除性。因此,如果您想要三月,您需要从第一天的第一时刻到 after 三月(4 月 1 日)的第一天的第一时刻。
要详细讨论这个想法,请参阅我的其他答案,例如 this one 和 this one。
这是我从其他答案中摘取的图表:
与 Java 捆绑在一起的 java.util.Date/Calendar 类是出了名的麻烦。避开他们。使用 Joda-Time 或 Java 8 中的新 java.time.* 包(受 Joda-Time 启发)。
Joda-Time 2.3 库提供了专门针对时间跨度的类:Period、Duration 和 Interval。该库还有一些方便的静态实用方法,例如 Days.daysBetween
时区,不像 java.util.Date/Calendar 看起来有时区但实际上没有。
// Specify a timezone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime marchFirst = new DateTime( 2013, DateTimeConstants.MARCH, 1, 0, 0, 0, timeZone );
DateTime aprilFirst = new DateTime( 2013, DateTimeConstants.APRIL, 1, 0, 0, 0, timeZone );
int days = Days.daysBetween( marchFirst, aprilFirst).getDays();
转储到控制台...
System.out.println( "marchFirst: " + marchFirst );
System.out.println( "aprilFirst: " + aprilFirst ); // Note the change in time zone offset in the output.
System.out.println( "days: " + days );
运行时,注意:
marchFirst: 2013-03-01T00:00:00.000+01:00
aprilFirst: 2013-04-01T00:00:00.000+02:00
days: 31
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.clear();
cal.set( 2013, java.util.Calendar.MARCH, 1 );
java.util.Date start = cal.getTime();
cal.set( 2013, java.util.Calendar.APRIL, 1 );
java.util.Date end = cal.getTime();
long days = java.util.concurrent.TimeUnit.MILLISECONDS.toDays( end.getTime() - start.getTime() );
System.out.println( "!!! Amount of days : " + String.valueOf( days ) );
cal.set( 2013, java.util.Calendar.MARCH, 1 );
start = cal.getTime();
cal.set( 2013, java.util.Calendar.MARCH, 31 );
end = cal.getTime();
days = java.util.concurrent.TimeUnit.MILLISECONDS.toDays( end.getTime() - start.getTime() );
System.out.println( "!!! Amount of days : " + String.valueOf( days ) );
我得到:
!!! Amount of days : 30
!!! Amount of days : 29
有关解释,请参阅 David Wallace 对
此答案的评论。
2013 年夏令时(美国)于 3 月 10 日星期日凌晨 2:00 开始。
!!! Amount of days : 31
请再次检查您的代码。
java.time
java.time
日期时间 API,取代了容易出错的遗留、
java.util
日期时间 API。任何新代码都应使用
java.time
API。如果您收到
java.util.Date
的实例,请使用
java.time.Instant
将其转换为 Date#toInstant
,并根据您的要求从中派生 java.time
的其他日期时间类。另外,下面是
Joda-Time主页上的通知:
使用现代日期时间 API 的解决方案请注意,从 Java SE 8 开始,用户被要求迁移到
java.time
(JSR-310) - JDK 的核心部分,它将取代它 项目。
演示:
public class Main {
public static void main(String[] args) {
System.out.println(ChronoUnit.DAYS.between(
LocalDate.of(2013, Month.MARCH, 1),
LocalDate.of(2013, Month.APRIL,1)));
System.out.println(ChronoUnit.DAYS.between(
LocalDate.of(2013, Month.MARCH, 1),
LocalDate.of(2013, Month.MARCH,31)));
}
}
从