【问题标题】:How can I get the UTC-converted Java timestamp of current local time?如何获取当前本地时间的 UTC 转换 Java 时间戳?
【发布时间】:2020-06-04 12:17:20
【问题描述】:

有人可以帮忙获取当前本地时间的 UTC 转换 Java 时间戳吗? 主要目标是获取当前日期和时间,转换为 UTC Timestamp,然后作为 Timestamp yyyy-MM-dd hh:mm:ss[.nnnnnnnnn] 存储在 Ignite 缓存中。

我的尝试是Timestamp.from(Instant.now())。但是,它仍然考虑我的本地时区+03:00。结果我得到了'2020-02-20 10:57:56',而不是理想的'2020-02-20 07:57:56'

如何获得 UTC 转换的时间戳?

【问题讨论】:

标签: java jdbc datetime-format


【解决方案1】:

你可以这样做:

LocalDateTime localDateTime = Instant.now().atOffset(ZoneOffset.UTC).toLocalDateTime();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
    System.out.println(localDateTime.format(formatter));

【讨论】:

  • HH (24h) i.o. hh (12h) 但很好。几乎是一个 toString(其中一个 T 而不是一个空格)。
  • 完全同意,只是将其格式化为问题中预期的格式。
  • 非常感谢您的建议!但是,它返回一个字符串类型的值,而时间戳需要将其作为日期和时间存储在 Apache Ignite 缓存中。
  • Timestamp.valueOf(localDateTime.format(formatter)) 将适用于您的情况。
  • 谢谢。即使没有.format(formatter),上述方法也有效。只需 Timestamp.valueOf(Instant.now().atOffset(ZoneOffset.UTC).toLocalDateTime()) 在 UTC 中返回 '2020-02-20 10:24:28'。再次感谢!
【解决方案2】:

不要使用时间戳

您很可能不需要Timestamp。这很好,因为 Timestamp 类的设计很差,确实是对已经设计很差的 Date 类的真正破解。这两个类也早已过时。相反,近 6 年前,我们得到了 java.time,这是现代 Java 日期和时间 API。从 JDBC 4.2 开始,这也适用于您的 JDBC 驱动程序,也适用于您的现代 JPA 实现。

使用偏移日期时间

对于时间戳,数据库中推荐的数据类型是timestamp with time zone。在这种情况下,在 Java 中使用偏移量为零(即 UTC)的OffsetDateTime。例如:

    OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
    System.out.println(now);

    PreparedStatement statement = yourDatabaseConnection
            .prepareStatement("insert into your_table (tswtz) values (?);");
    statement.setObject(1, now);
    int rowsInserted = statement.executeUpdate();

刚才System.out.println()的输出示例:

2020-02-22T13:04:06.320Z

如果您的数据库时间戳没有时区,则使用 LocalDateTime

从您的问题中,我得到的印象是您的数据库中的数据类型是timestamp,没有时区。这只是次优选项,但您可以将 LocalDateTime 传递给它。

    LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);

其余的和以前一样。示例输出:

2020-02-22T13:05:08.776

如果你确实需要一个老式的 java.sql.Timestamp

您要求使用 UTC 格式的 TimestampTimestamp总是在 UTC 中。更准确地说,它是一个独立于时区的时间点,因此将其转换为不同的时区是没有意义的。在内部,它被实现为自纪元以来的毫秒数和纳秒数。纪元被定义为 1970 年的第一个时刻UTC

Timestamp 类是一个令人困惑的类。可能让您感到困惑的一件事是当您打印它时,会隐式调用它的toString 方法。 toString 方法使用 JVM 的默认时区来呈现字符串,因此打印本地时区的时间。令人困惑。如果您在 SQL 中的数据类型是 timestamp 没有时区,那么您的 JDBC 驱动程序很可能会解释您时区中的 Timestamp 以转换为 SQL timestamp。在您的情况下这是不正确的,因为您的数据库使用 UTC(推荐的做法)。我能想到三种可能的解决方案:

  1. 某些数据库引擎允许您在会话中设置时区。我自己没有任何经验,这是我读过的;但它可能会强制执行从 Java Timestamp 到 SQL timestamp UTC 的正确转换。
  2. 您可能会在 Java 中进行不正确的转换,以补偿在 Java 和 SQL 之间执行的相反的不正确转换。这是一个 hack,而不是我想要在我的代码中包含的东西。我把它作为最后的手段。

        LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
        Timestamp ts = Timestamp.valueOf(now);
        System.out.println(ts);
    

    2020-02-22 13:05:08.776

    您注意到它只是似乎与上述 UTC 时间一致。这与您从 Vipin Sharma 的答案中得到的结果相同,除了 (1) 我的代码更简单并且 (2) 您获得了更高的精度,包括秒的小数部分。

  3. 您是否在数据库中以 UTC 格式生成当前时间戳,而不是在 Java 中生成它。

链接

【讨论】:

  • 非常感谢您提供非常详细的回答!这对我来说绝对有意义。然而,它与我的需求有点矛盾。实际上,我正在使用 Apache Ignite 数据持久性机制(这不是 SQL 本身)。另外,系统的逻辑是'database-worker'类的setter获得LocalDateTime.now()(期望用户使用不同的时区)值,它需要作为时间戳存储在Ignite的缓存中。会考虑您的建议并尝试将其整合到我的工作中。谢谢!
  • 感谢您的反馈。抱歉,我不知道 Apache Ignite。听起来你给自己带来了一个有趣的挑战。
【解决方案3】:

尽管Ignite docs say 您可以在 24 小时内通过。 文档说yyyy-MM-dd hh:mm:ss[.nnnnnnnnn],因此您可能会在代码中使用它来格式化您的日期,但这会导致中午之后的时间出错。相反,请使用yyyy-MM-dd HH:mm:ss[.nnnnnnnnn] 格式化您的日期。

注意大写的HH。如果您使用ZonedDateTime 或Joda 的DateTime 现在使用UTC 调用now(UTC),然后toString("yyyy-MM-dd HH:mm:ss") 将以UTC 格式存储正确的时间。

【讨论】:

    猜你喜欢
    • 2012-09-11
    • 2015-12-16
    • 2011-02-06
    • 2019-08-15
    • 2019-03-09
    • 2013-09-12
    • 2019-01-05
    • 2013-03-23
    • 2018-04-17
    相关资源
    最近更新 更多