1976 年 3 月 28 日至 3 月 29 日之间,java.util.GregorianCalendar 发生了什么?

试图使用公历日历,我在计算自特定日期以来的天数时被困在一个奇点上。在 scala 解释器中,我输入:

scala>import java.util.GregorianCalendar
scala>import java.util.Calendar
scala>val dateToday = new GregorianCalendar(2012,Calendar.MAY,22).getTimeInMillis()
dateToday: Long = 1337637600000
scala>val days1 = (dateToday - (new GregorianCalendar(1976,Calendar.MARCH,28).getTimeInMillis())) / (1000*3600*24)
days1: Long = 13203
scala>val days2 = (dateToday - (new GregorianCalendar(1976,Calendar.MARCH,29).getTimeInMillis())) / (1000*3600*24)
days2: Long = 13203

我不知道1976年是闰年的事实是否重要,但days1和days2应该相隔1。这是自1970年以来历史上唯一一次发生这种奇点的时刻。

想知道发生了什么,我计算了前面提到的两个日期之间的差异,它只给了我23小时的差异!那天发生了什么?维基百科显然对此只字未提。

更重要的是,如何计算自特定日期以来的实际天数?


答案 1

问题

夏令时只有23小时。

根据这个3月28日,1976年至少在巴黎是一个夏令时日。那天凌晨1点到凌晨2点之间的时间根本不存在。

虽然这肯定是一个区域设置问题,因为在我的计算机上我做对了:

scala> val days1 = (dateToday - (new GregorianCalendar(1976, Calendar.MARCH, 28).getTimeInMillis())) / (1000 * 3600 * 24)
days1: Long = 13203

scala> val days2 = (dateToday - (new GregorianCalendar(1976, Calendar.MARCH, 29).getTimeInMillis())) / (1000 * 3600 * 24)
days2: Long = 13202

我不在巴黎或其他任何那天有时间变化的地方;时间在1976年4月25日改变,我在哪里。因此,我在该日期得到了“跳过的一天”行为:

scala> val days3 = (dateToday - (new GregorianCalendar(1976, Calendar.APRIL, 25).getTimeInMillis())) / (1000 * 3600 * 24)
days3: Long = 13175

scala> val days4 = (dateToday - (new GregorianCalendar(1976, Calendar.APRIL, 26).getTimeInMillis())) / (1000 * 3600 * 24)
days4: Long = 13175

Erwin在评论中指出,您在这里唯一注意到不正确的日期差异的可能原因是,所有其他夏令时日被那些年份中发生的25小时日所抵消,当夏令时日被纠正时。

解决方案

使用更好的库进行日期处理。joda-time库正确地做到了这一点(以及总体上更好的日期/时间框架):

import org.joda.time.Days
import org.joda.time.DateTimeConstants
import org.joda.time.DateMidnight

val d1 = new DateMidnight(1976, DateTimeConstants.APRIL, 25)
val d2 = new DateMidnight(1976, DateTimeConstants.APRIL, 26)
val x = Days.daysBetween(d1, d2).getDays()
println(x) // 1

答案 2

如果浮点数除法的选项不可用,我能想到的最好的答案是添加一个小时(1000 * 3600),同时计算两天之间的差异:

scala> (dateToday - (new GregorianCalendar(1976, Calendar.MARCH, 28).getTimeInMillis()) + 1000*3600) / (1000 * 3600 * 24)
days1: Long = 13204
scala> (dateToday - (new GregorianCalendar(1976, Calendar.MARCH, 29).getTimeInMillis()) + 1000*3600) / (1000 * 3600 * 24)
days1: Long = 13203

它应该适用于每个日期,因为您不再遭受闰时的困扰。