【问题标题】:Can't get local and utc Instant无法获取本地和 UTC Instant
【发布时间】:2020-07-03 08:02:39
【问题描述】:

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

    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()));

线程“main”java.time.DateTimeException 中的异常:ZoneOffset 的 ID 无效,格式无效:Europe/Astrakhan 在 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 时间(以秒为单位)和发送方本地时间(以秒为单位)进行响应。为什么?我不知道。这对我来说是黑匣子。

【问题讨论】:

  • 您为什么期望localTime 是本地的?你正在做getEpochSecond,一个瞬间,无论偏移量如何,都将具有与纪元相同的偏移量。瞬间描述了一个时刻,无论您身在何处。因此,如果您向其添加区域,瞬间本身不会改变。
  • 使用ZonedDateTime sysDefZdt = now.atZone(ZoneOffset.systemDefault()); 并注意Instant 是独立于区域或偏移的时间点。基本上,它只是(毫秒)秒的计数器。您可以花一点时间并以应用区域和/或偏移量的日期时间格式表示它。
  • 所以你没想到他们都是1593762925?你期待什么? “本地时间以秒为单位”是什么意思?
  • @ValeriyK。因此,您正在尝试查找自 1970-01-01T00:00:00 GMT+4 以来的秒数?为什么会有用? “1970-01-01T00:00:00 GMT+4”不是时代。这根本不是一个特殊的瞬间。
  • 我想你说你想要的是ZonedDateTime.now().withZoneSameLocal(ZoneOffset.UTC).toEpochSecond()。但我敢肯定你并不是真的想要那个,即这是一个XY problem。如果您能告诉我们更多关于您为什么想要这个“以秒为单位的本地时间”的信息,那就太好了。它有什么用?

标签: java timezone-offset


【解决方案1】:

我认为您需要使用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]

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

【讨论】:

    【解决方案2】:

    找到解决方案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。

    【讨论】:

    • 您找到的解决方案似乎是正确的,并且在 2011 年很好。如今,从 Java 8 开始,应该首选纯 java.time 解决方案,而不是 java.timejava.util 的混合解决方案。
    【解决方案3】:

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

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

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

    万一别人错了

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

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

        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 个月)您和您的用户将意识到您的设备不再工作的事实。

    【讨论】:

      猜你喜欢
      • 2017-03-09
      • 1970-01-01
      • 1970-01-01
      • 2018-07-02
      • 2020-09-27
      • 2014-09-15
      • 2014-03-03
      • 2012-10-08
      相关资源
      最近更新 更多