使用Instant/ChronoUnit在今天午夜获取nanos-since-epoch中的时间比使用GregorianCalendar/Date提前24小时

自 Epoch 午夜(例如 2021 年 2 月 13 日 00:00:00)以来,我试图以 nanos 获取今天的日期。

使用 GregorianCalendar/Date 似乎可以得到正确的结果。

我使用 Instant/ChronoUnit 的方式是明天午夜(例如 2021 年 2 月 14 日 00:00:00)。

这里有什么问题?

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Calendar;
import java.util.TimeZone;


class scratch {

    public static void main(String[] args) {
        Calendar c = new GregorianCalendar();
        c.setTime(new Date());
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        c.setTimeZone(TimeZone.getTimeZone("UTC"));
        long dateNanos = c.getTimeInMillis() * 1000000L;

        Instant i = Instant.now().truncatedTo(ChronoUnit.DAYS);
        long instantNanos = ChronoUnit.NANOS.between(Instant.EPOCH, i);

        System.out.println("dateMillis = " + dateNanos);
        System.out.println("instantMillis = " + instantNanos);
        System.out.println("instantMillis - dateMillis = " + (instantNanos - dateNanos));
    }
}

看来trucatedTo是四舍五入了。javadocs 说它四舍五入。

例如,使用 MINUTES 单位截断将向下舍入到最接近的分钟,将秒和纳秒设置为零。

回答

这些给出了不同的结果,因为您在不同的时区“截断到当天”。请注意,当您处于不同时区时,“截断到白天”会给您不同的答案,因为“午夜”的时刻在不同时区是不同的。由于夏令时的变化,有些时区甚至没有 00:00:00 的时间。

在该Calendar版本中,您将截断到系统时区中的日期。请注意,setTimeZone调用发生在截断之后,因此它实际上并没有做任何有用的事情。

在该Instant版本中,您将截断到 UTC 中的日期,因为Instants 中没有“时区”的概念。

Instant只是“错误”,因为您希望它在系统时区中截断为一天。Instant并不是真的要这样做。你应该使用一个ZonedDateTime

ZonedDateTime now = ZonedDateTime.now(); // this gets the current ZonedDateTime, you can also specify a specific zone
System.out.println(now.truncatedTo(ChronoUnit.DAYS).toEpochSecond() * 1_000_000_000L);

另一方面,如果您想Calendar在 UTC 时区中截断,setTimeZone请在截断之前调用:

Calendar c = new GregorianCalendar();
c.setTime(new Date());
c.setTimeZone(TimeZone.getTimeZone("UTC"));
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);

但我仍然建议你java.time尽可能使用:)


您现在应该做的是决定您的意思是哪个“午夜”(哪个时区的午夜?)。

  • @marathon Really? Putting it after `setTime` works for me though... I guess that's one more reason not to use `Calendar` 😀

以上是使用Instant/ChronoUnit在今天午夜获取nanos-since-epoch中的时间比使用GregorianCalendar/Date提前24小时的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>