在 Java 中获取 UTC 时间戳

时间:2021-01-22 21:34:32

标签: java date datetime timezone utc

old Stack Overflow posting 表明在 Java 中获取 UTC 时间戳的方法如下:

Instant.now()   // Capture the current moment in UTC.

不幸的是,这对我不起作用。我有一个非常简单的程序(复制如下),它展示了不同的行为。

在 Windows 上:时间是本地时间,标有 GMT 的偏移量

在 Linux 上:时间又是当地时间,并且为当地时区正确标记了


问题:我们如何在 Java 程序中显示 UTC 时间戳?


我的示例源代码如下:

import java.time.Instant;
import java.util.Date;

public class UTCTimeDisplayer {
    public static void main(String[] args) {
        System.out.println(System.getProperty("os.name"));
        Date currentUtcTime = Date.from(Instant.now());
        System.out.println("Current UTC time is " + currentUtcTime);
    }
}  

Windows 输出:

C:\tmp>java UTCTimeDisplayer
Windows 10
Current UTC time is Fri Jan 22 14:28:59 GMT-06:00 2021

Linux 输出:

/tmp> java UTCTimeDisplayer
Linux
Current UTC time is Fri Jan 22 14:31:10 MST 2021

6 个答案:

答案 0 :(得分:5)

您的代码:

Date.from(Instant.now())

您将糟糕的遗留类与其替代品、现代 java.time 类混合在一起。

不要。

切勿使用 Date。当然不需要和java.time.Instant混用。

为了解释您的特定示例,请了解在 Date 类的许多糟糕设计选择中,其 Date#toString 方法的反特性隐式应用了 JVM 的当前默认时区 生成文本时。

您在具有不同当前默认时区的两个不同 JVM 上运行您的代码。所以你得到了不同的输出。

Sun、Oracle 和 JCP 放弃了旧的日期时间类。我们都应该如此。我建议您不要花时间尝试理解 DateCalendarSimpleDateFormat 等。

你问:

<块引用>

问题:我们如何在 Java 程序中显示 UTC 时间戳?

Instant.now().toString()

看到那个code run live at IdeOne.com

<块引用>

2021-01-22T21:50:18.887335Z

你说:

<块引用>

在 Windows 上:...

在 Linux 上:……

您将通过 Instant.now().toString() 在 Windows、Linux、BSD、macOS、iOS、Android、AIX 等平台上获得相同一致的结果。


这是我制作的表格,用于指导您从遗留课程过渡。

enter image description here

答案 1 :(得分:3)

java.util.Date 对象不是像 modern date-time types 那样的真实日期时间对象;相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即 January 1, 1970, 00:00:00 GMT(或 UTC)。当您打印 java.util.Date 的对象时,它的 toString 方法返回 JVM 时区中的日期时间,从这个毫秒值计算出来。如果您需要在不同的时区打印日期时间,则需要将时区设置为 SimpleDateFormat 并从中获取格式化的字符串。

我建议您简单地使用 Instant.now(),您可以将其转换为其他 java.time 类型。

java.util 的日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到 modern date-time API

但是,如果您仍想使用 java.util.Date,请使用上面提到的 SimpleDateFormat

演示:

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        Date currentUtcTime = Date.from(Instant.now());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
        sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
        System.out.println("Current UTC time is " + sdf.format(currentUtcTime));
    }
}

输出:

Current UTC time is 2021-01-22 21:53:07 UTC

答案 2 :(得分:2)

<块引用>

建议在Java中获取UTC时间戳的方法如下:

<块引用>

Instant.now() // Capture the current moment in UTC.

这个以及该主题中的大多数答案误导

Instant 代表时间上的瞬间。现在是 'solarflares' 时间:绝对没有一个关于它的 iota 代表人类大脑发明的任何东西,UTC 是一个时区:人类的发明。宇宙、太阳、天文学——他们不知道 UTC 是什么,也不关心——这就是 Instant 的全部意义所在。瞬间没有诸如“小时”或“天”或“时区”之类的人类概念。立即问它发生在哪一天是没有意义的。它不能告诉你;一些事件发生了:例如,如果我问一个 19 世纪的俄罗斯人什么时候发生,他们可能会给出完全不同的答案。 Instant 不知道要应用哪种本地化,因此不会让您问这个问题 - 这是一件好事,对象不应该公开它给出的任何答案都是 gobbledygook 或至少需要了解各种令人惊讶的方法警告。

至关重要的是,如果您告诉我“...在 UTC 中”,您肯定可以准确地说出是哪月、哪天等。 Instant 不会这样做,这就是为什么说 java.time.Instant 代表 UTC 中的某个时刻会产生误导的原因。 没有。它代表一个时刻(不属于任何特定时区)。

是的,内部 Instant,就像 Date 一样,只是对 System.currentTimeMillis() 返回的内容的一个轻量级包装:“自纪元以来的毫秒”,但要了解它的关键是“UTC”不是意味着什么,因此,当您将 Instant 实例提供给其他方法(例如 System.out.println,通过 JDBC 提供给数据库等)时,该方法绝对没有义务假设 UTC 在语义上是相关的。

当您想将人类的计时概念(年、日、月、小时、分钟、毫秒,以及,是的,时区)与或多或少绝对*时间的概念混合在一起时,正确的答案是 {{ 1}}。请注意,任何不基于 java.time.ZonedDateTime 的事物的时间表示在定义上都是被破坏的,就像在大多数编程语言中一样 - 事实证明,时间比大多数用库来表示它所实现的要复杂得多。 java.time.* 实际上是编写时间库的第 4 次尝试这一事实应该充分表明很难做到正确。

java.time

THAT 就是您想要的 - 这不仅仅是您想要的实现细节,而是准确描述您意思的代码:现在,在 UTC 时区,存储在一个对象中,该对象在语义上不仅存储正确的时间,而且还存储并紧紧纠缠在其身份中,即它专门在 UTC 中并且不会被重新解释,移至本地区域或任何其他此类恶作剧 - 至少,除非您明确要求这样做。

<块引用>

ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);

请注意,Date 是旧 API,因此必然已损坏。在这种情况下,Date 是一个撒谎的骗子——它不代表日期,它代表一个瞬间;它的名字很糟糕。 (第二个 API 是 Calendar,也被破坏了。例如,这也是一个撒谎的骗子:它根本不代表日历。它代表了分区日期时间和瞬间的一些奇怪的合并,并且不适合将两者都表示为结果)。每当您访问 Date API 时,都会出现奇怪的情况,并且在这些 API 中不可能出现诸如“我只想要在某个特定时刻使用 UTC 的时间概念”这样简单的事情。您现在依赖于链上和下游所有各种库的几乎没有定义的行为 - 您实际上被困在祈祷他们做正确的事情,或者钻研异国情调的环境以试图哄骗这些库做您想做的事。< /p>

TL;DR:使用 Date currentUtcTime = Date.from(Instant.now());

*) 请注意 ZonedDateTime 是不是绝对的。例如,如果您有时间 java.time,以 January 20th, 2023, 8 in the morning, at Europe/Amsterdam 对象的形式,那么从现在到那一刻之间经过的秒数肯定看起来不会改变也不会改变当例如由于夏令时,阿姆斯特丹经历了一个小时的变化。但是,如果荷兰政府颁布法令,从今以后荷兰将不再移动时钟并将永远停留在夏令时(很可能 - 欧盟指令已经到位,现在只是一个问题的时间),那么在木槌落地的那一刻,您的约会正好偏移 1 小时。

这有望提供对差异的重要洞察:即时,代表事件(因此我喜欢称其为“太阳耀斑时间”,以尽可能将其与人类时间保持概念分开),甚至不理解非常这种决定对事物产生影响的概念。另一方面, ZonedDateTime 本质上是绑定在其中的 - 因此 ZonedDateTime 中的 Zone

如果您想存储理发预约并使用 ZonedDateTime 来完成,您迟到或早到一个小时。

答案 3 :(得分:2)

一个 Instant 对象和一个 Date 对象本身 只包含一个时间点,但没有时区信息。 此外,Date 类的 toString() 方法 隐式选择系统环境提供的时区, 这不是你想要的。

因此,您需要明确选择时区(在您的情况下为 UTC)。 例如像这样:

Instant instant = Instant.now();
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC);
System.out.println("Current UTC time is " + offsetDateTime);

这将(独立于操作系统)打印

Current UTC time is 2021-01-22T22:37:21.950354100Z

其中尾随 Z 表示零时区偏移量(即 UTC)。

答案 4 :(得分:1)

Instant.now() 本质上是自纪元以来的时间段(UTC 时间 1970 年 1 月 1 日午夜),但您使用的是日期来表示该时刻。日期是毫秒精度的即时反映,但如 https://docs.oracle.com/javase/8/docs/api/java/util/Date.html 处的文档中所述,应使用日历来呈现日期,因为日期的呈现取决于主机。本质上 Date 包装了瞬间,但根据其他因素显示。

如果您想输出瞬间,现在最简单的方法是使用 OffsetDateTime,这样您就可以选择在所需时区中显示瞬间 - 在您的情况下为 UTC。使用 OffsetDateTime.now()OffsetDateTime.ofInstant(),但如果您在应用程序逻辑中使用 Instant,那么就坚持使用 Instant。

答案 5 :(得分:0)

有时您的程序必须使用较旧的 Java 版本,因此这里是 1.5 的示例:

    java.text.SimpleDateFormat tfGMT = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    java.util.Calendar cUTC = java.util.Calendar.getInstance (java.util.TimeZone.getTimeZone ("GMT+0"));
    tfGMT.setCalendar (cUTC);
    java.util.Date d= new java.util.Date ();
    String s= tfGMT.format (d);
    System.out.printf ("now=%s [unix ts=%d.%03d]\n", s, d.getTime()/1000, d.getTime()%1000);

请注意,前三行不必在每次调用时都重复,但请记住,SimpleDateFormat 不是线程安全的。 (简单的解决方案:为每个线程创建一个。)

示例用法(它表明设置 TZ 不会影响 UTC-timestamp):

$ TZ=GMT+3 java5 now_utc; TZ=GMT-3 java5 now_utc
now=2021-01-24 12:56:14 [unix ts=1611492974.264]
now=2021-01-24 12:56:14 [unix ts=1611492974.726]
相关问题