Java 日期比较差一天

Java date comparison off by a day

我有一个 Java 方法可以比较两个 Dates 和 returns 它们之间的天数,但相差一天。

即使我将小时、分钟和秒设为 0,计算仍然关闭。

public long compareDates(Date exp, Date today){
        TimeZone tzone = TimeZone.getTimeZone("America/New_York");
        Calendar expDate = Calendar.getInstance();
        Calendar todayDate = Calendar.getInstance();

        expDate.setTime(exp);
        todayDate.setTime(today);

        expDate.set(Calendar.HOUR_OF_DAY, 0);
        expDate.set(Calendar.MINUTE, 0);
        expDate.set(Calendar.SECOND, 0);

        todayDate.set(Calendar.HOUR_OF_DAY, 0);
        todayDate.set(Calendar.MINUTE, 0);
        todayDate.set(Calendar.SECOND, 0);

        logger.info("Today = " + Long.toString(todayDate.getTimeInMillis()) + " Expiration = " + Long.toString(expDate.getTimeInMillis()));

        expDate.setTimeZone(tzone);
        todayDate.setTimeZone(tzone);

        return (expDate.getTimeInMillis()-todayDate.getTimeInMillis())/86400000;
    }

输出

Today = 1453939200030 Expiration = 1454544000000

1/28 到 2/4 之间有 7 天,但是这个 returns 6.

好吧,如您所见,您没有清除毫秒数,1454544000000 - 1453939200030 = 604799970 除以 86400000 得到 6.99999965277777...,这意味着 6 当截断为 int.

现在,如果你也清除毫秒,今天就变成了1453939200000,这将导致你回答7

注意:这并不意味着你已经完成了,因为夏令时。使用 DST,其中一个时间戳可能与另一个时间戳相差 ±1 小时,因此您仍然可能遇到截断问题。

这是对您的特定问题的回答。尝试搜索如何在 Java.

中正确查找日期之间的天数

Today = 1453939200030

时间以毫秒为单位,看起来你输入的 Date 多了 30 毫秒。

当我减去 30 毫秒,然后在计算器上计算时,我得到 7 天。按照你的数字,我得到 6.9999996527777777777777777777778,在 long 数学中,小数被截断为 6

也将毫秒归零。

expDate.set(Calendar.MILLISECOND, 0);
todayDate.set(Calendar.MILLISECOND, 0);

java.time

问题和其他答案使用过时的 classes。旧的日期时间 classes,例如 java.util.Date/.Calendar 与最早版本的 Java 捆绑在一起,已被证明是相当麻烦的。那些旧的 classes 已被 Java 8 及更高版本中的 java.time 框架所取代。

正如其他答案正确指出的那样,问题是开始 long 在右侧有 30,排除了全天计算。

天数定义

此外,您必须定义天数的含义。您的意思是按日期计数,所以 1 月 3 日的任何时间到 4 日的任何时间都是一天,即使时间是午夜前后一分钟?还是您指的是通用 24 小时时间块的计数,而忽略了由于夏令时 (DST) 和其他异常情况,特定时区的特定日期并不总是 24 小时的事实?

按日期计算天数

如果你想要前者,按日期计算,然后使用 LocalDate class(没有时间和时区的仅日期)和 Period class(定义为年、月、日计数的时间跨度)在 java.time.

中找到

定义您的输入。使用 long 而不是 int。这些数字显然表示自 1970 年第一刻以来的毫秒数(UTC)。

long startMilli = 1_453_939_200_030L;
long stopMilli = 1_454_544_000_000L;

将那些 long 个数字转换为 Instant 个对象,UTC 时间轴上的一个时刻。

Instant startInstant = Instant.ofEpochMilli ( startMilli );
Instant stopInstant = Instant.ofEpochMilli ( stopMilli );

定义要考虑日历日期的时区。请注意,时区对于定义日期至关重要。全球各地的日期并不相同。日期因时区而异。

ZoneId zoneId = ZoneId.of ( "America/Montreal" );

将该时区应用于每个 Instant 以生成 ZonedDateTime

ZonedDateTime startZdt = ZonedDateTime.ofInstant ( startInstant , zoneId );
ZonedDateTime stopZdt = ZonedDateTime.ofInstant ( stopInstant , zoneId );

要获得 Period,我们需要“本地”日期。 “本地”是指任何特定地点,通用日期值。 LocalDate class 不包含时区,但在确定 LocalDate.

时应用 ZonedDateTime 中包含的时区
LocalDate startLocalDate = startZdt.toLocalDate ();;
LocalDate stopLocalDate = stopZdt.toLocalDate ();

将我们的时间跨度定义为通用天数,单位为 Period

Period period = Period.between ( startLocalDate , stopLocalDate );

询问 Period 以询问其中包含的通用天数。

int days = period.getDays ();

转储到控制台。

System.out.println ( "milli: " + startMilli + "/" + stopMilli + " | Instant: " + startInstant + "/" + stopInstant + " | ZonedDateTime: " + startZdt + "/" + stopZdt + " | LocalDate: " + startLocalDate + "/" + stopLocalDate + " | period: " + period + " | days: " + days );

milli: 1453939200030/1454544000000 | Instant: 2016-01-28T00:00:00.030Z/2016-02-04T00:00:00Z | ZonedDateTime: 2016-01-27T19:00:00.030-05:00[America/Montreal]/2016-02-03T19:00-05:00[America/Montreal] | LocalDate: 2016-01-27/2016-02-03 | period: P7D | days: 7

天数

如果你想要一整天的计数,使用Days class from ThreeTen-Extra。请注意,在下面的输出中,我们得到 六 (6) 天的计数,而不是上面看到的七 (7)

三十加

ThreeTen-Extra 项目扩展了 java.time。 运行 由构建 java.time.

的同一批人

between 方法的行为没有明确记录。实验表明,它似乎是基于 24 小时制的时间块,而不是日期。将 030 替换为 000,并尝试将 stopMilli 中的最后一个 000 替换为 030,以查看您自己的行为。

Days daysObject = Days.between ( startZdt , stopZdt );
int daysObjectCount = daysObject.getAmount ();

转储到控制台。您在输出中看到的 P6D 字符串是根据 ISO 8601 标准中定义的格式生成的。 java.time 默认使用此标准来解析和生成日期时间值的文本表示。这些标准格式非常明智和有用,所以请浏览链接的维基百科页面。

System.out.println ( "daysObject: " + daysObject + " | daysObjectCount: " + daysObjectCount );

daysObject: P6D | daysObjectCount: 6

为了解决我的问题,我已将毫秒数归零,并将长数转换为双数,以在必要时保持准确性和舍入。

expDate.setTime(exp);
todayDate.setTime(today);
expDate.setTimeZone(tzone);
todayDate.setTimeZone(tzone);

expDate.set(Calendar.HOUR_OF_DAY, 0);
expDate.set(Calendar.MINUTE, 0);
expDate.set(Calendar.SECOND, 0);
expDate.set(Calendar.MILLISECOND, 0);

todayDate.set(Calendar.HOUR_OF_DAY, 0);
todayDate.set(Calendar.MINUTE, 0);
todayDate.set(Calendar.SECOND, 0);
todayDate.set(Calendar.MILLISECOND, 0);

double diff = ((double)expDate.getTimeInMillis()-(double)todayDate.getTimeInMillis())/86400000;

return Math.round(diff);