无法获取本地和 utc Instant

Can't get local and utc Instant

我需要以秒为单位获取本地时间和 utc 时间。我阅读了 Whosebug 中的一些帖子并找到了一些解决方案,如前所述:

    Instant time = Instant.now();
    OffsetDateTime utc = time.atOffset(ZoneOffset.UTC);
    int utcTime = (int) utc.toEpochSecond();
    int localTime = (int) time.getEpochSecond();
    System.out.println("utc " + utcTime + " local " + localTime);

但是结果不是我所期望的。现在是 utc 时间。输出:

utc   1593762925
local 1593762925

调试后发现Instant.now()已经是utc了。我找不到如何获取当前时区的时间,即我的系统时区。

我在 API 中找到了一些解决方案,但出现错误:

OffsetDateTime utc = time.atOffset(ZoneOffset.of(ZoneOffset.systemDefault().getId()));

Exception in thread "main" java.time.DateTimeException: Invalid ID for ZoneOffset, invalid format: Europe/Astrakhan at java.base/java.time.ZoneOffset.of(ZoneOffset.java:241)

UPD:我的问题是如何在本地时区和 UTC 中获取以秒为单位的当前时间? IE。自 1970-01-01T00:00:00 GMT+4 和 1970-01-01T00:00:00 GMT+0

以来的秒数

UPD2:我有一些设备需要从 1970 年开始以秒为单位的 utc 时间和以秒为单位的发件人本地时间。为什么?我不知道。它对我来说是黑盒子。

我认为您需要采用 Instant,通过应用 ZoneId.of("UTC") 创建一个 ZonedDateTimeOffsetDateTime 可能也适用),然后采用 ZonedDateTime 并用它来转换语言环境:

public static void main(String[] args) {
    Instant now = Instant.now();
    
    ZonedDateTime utcZdt = now.atZone(ZoneId.of("UTC"));
    ZonedDateTime localZdt = utcZdt.withZoneSameLocal(ZoneId.systemDefault());
    
    System.out.println(utcZdt.toEpochSecond() + " <== " + utcZdt);
    System.out.println(localZdt.toEpochSecond() + " <== " + localZdt);
}

在我的系统上,输出

1593765852 <== 2020-07-03T08:44:12.070Z[UTC]
1593758652 <== 2020-07-03T08:44:12.070+02:00[Europe/Berlin]

两个小时的差异影响纪元秒的第六位。

找到解决方案here

    TimeZone tz = TimeZone.getDefault();
    Instant instant = Instant.now();
    int offsetFromUtc = tz.getOffset(instant.getEpochSecond()) / 1000;
    

或如@deHaar 所写:

    int offsetFromUtc = Instant.now().atZone(ZoneOffset.systemDefault()).getOffset().getTotalSeconds();

它给出了 14400 秒,这对我的时区来说是正确的。我可以将其添加到 utc.

TL;DR:你的期望是错误的。你的结果是正确的。

您得到的结果是自 Unix/Java 纪元以来的秒数。这也称为 Unix 时间戳。纪元是一个时间点,与时区无关。这是所有时区的同一时间点。因此,所有时区的秒数也是相同的。

纪元是 1970-01-01T00:00:00 GMT+0。在某些时区(你的?),这个时间点将被指定为 1970-01-01T04:00:00 GMT+4。请注意时间是凌晨 4 点,而不是 00:00。

万一别人错了

UPD2: I have some device that needs response with utc time in seconds from 1970 and sender local time in seconds. Why? I don't know. It is black box for me.

当然,有可能是该设备的设计者误解了并且可能无意中发明了他们自己的计时方式。不过,这听起来不太可能,所以我至少会仔细检查和三次检查这条信息。如果结果是正确的,我会做类似的事情:

    LocalDateTime misunderstoodEpoch = LocalDate.EPOCH.atStartOfDay();
    
    ZoneId zone = ZoneId.of("Europe/Astrakhan");
    long secondsLong = ChronoUnit.SECONDS
            .between(misunderstoodEpoch.atZone(zone), ZonedDateTime.now(zone));
    int seconds = Math.toIntExact(secondsLong);
    
    System.out.println(seconds);

刚才运行时输出:

1593881344

此外,如果您的设备坚持使用 int 作为秒数,至少使用 Math.toIntExact() 进行转换。这将在 int 溢出的情况下抛出异常,以便在 2038 年 1 月(距现在仅 17 年 6 个月),您和您的用户将意识到您的设备不再工作的事实。