“Java Date() 以 UTC 形式返回日期”——它实际上是啥意思?

Posted

技术标签:

【中文标题】“Java Date() 以 UTC 形式返回日期”——它实际上是啥意思?【英文标题】:"Java Date() returns date in UTC" - what does it actually mean?“Java Date() 以 UTC 形式返回日期”——它实际上是什么意思? 【发布时间】:2011-08-22 06:59:32 【问题描述】:

我的问题可能是微不足道的,但我只是在寻找澄清。我在某处读到 Java 的 Date() 实际上总是在 UTC 时间,当我创建一个 Date() 对象并使用 toString() 打印它时,它会显示本地时间。如果这不是正确的打印方式,应该是什么,这样我才能得到 UTC 时间?

【问题讨论】:

【参考方案1】:

对于格式化,您应该真正使用DateFormat 实现(例如SimpleDateFormat)。这将让您指定时区(和输出格式)。 Date 本身没有时区的概念——它代表一个瞬间,使用“自 Unix 纪元以来的毫秒数”作为存储。 toString() 方法只是使用系统默认时区将表示的任何时刻转换为本地时间。

我个人建议完全放弃内置的日期/时间 API,并使用 Joda Time 作为更明智的库。

【讨论】:

是的,我实际上是从您的帖子中提出这个问题的。对于打印,是的,我确实使用 DateFormat。但是“UTC”与 Date() 有什么关系?我找不到连接,为什么它总是显示当地时间,而我认为它应该是 UTC 时间 (GMT+0)?好吧,我知道 Date() 只是表示自 1970 年 1 月 1 日以来经过的时间...... @jasonline:Unix 纪元被定义为 1970 年 1 月 1 日午夜 UTC 所以我想这就是连接的方式,如果我住在 GMT+8 的某个地方,我不应该期望显示 UTC 时间。 @jasonline:不是来自Date.toString,不是。正如我所说,改用 Joda :)【参考方案2】:

“Java Date() 以 UTC 格式返回日期”——它的真正含义是什么?

UTC 是我们调整全球时间的基准。

自穴居人时代以来,中午或 12:00 是太阳直射头顶的时间。当然,这意味着不同地方的不同时刻。日本的中午比欧洲早几个小时,而美洲的中午甚至更晚。

在现代,人类创造了time zones,正午将适用于大片土地,如此宽阔,以至于太阳可能不会同时在所有土地上出现。所以太阳,或“solar time”,不能再用来定义时间。现代时区被定义为距基线一定数量的小时-分钟-秒。

什么基线?该基线被历史的命运任意选择为贯穿英国伦敦Royal Observatory, Greenwich的纬度。东边的时区比 UTC提前 一定数量的时区,而西边的时区比 UTC 落后一定数量。例如,印度比 UTC超前五个半小时,加勒比海的马提尼克岛比 UTC四个小时。

如果偏移量为零时分秒,那么我们说我们是“在 UTC”或“在 UTC”。

Java 的 Date() 实际上始终是 UTC 时间

是的,java.util.Date 类,尽管它的名字很不幸,但它表示一个日期,它的时间与 UTC 的偏移量为零。

顺便说一句,Date 类现在已经过时了。 java.time.Instant 类现在被用作它的替代品来表示 UTC 中的时刻。 Instant 的分辨率为nanoseconds,比Date 的milliseconds 更精细。

当我创建一个 Date() 对象并使用 toString() 打印它时,它会显示本地时间

旧的日期时间类存在很多问题。一个是糟糕的命名,如上所述。另一个是由不幸的设计决定善意的,即Date::toString 应该在生成文本时动态应用 JVM 的当前默认时区以根据 UTC 进行调整。这个糟糕的决定给 Java 程序员带来了无穷无尽的困惑和痛苦,因为它造成了java.util.Date 已分配时区的错觉。

顺便说一句……更糟糕的是,java.util.Date 确实有一个时区,但它深埋在它的源代码中,没有访问器的 getter/setter 方法。但是该区域确实会影响某些行为,例如确定此类对象之间的相等性。令人困惑?是的。正如我所说,这些遗留的日期时间类充斥着糟糕的设计决策。

如果这不是正确的打印方式,我应该使用什么格式才能获得 UTC 时间?

正确的方法是完全避免 Date 类。只使用 java.time 类。

➥ 要获取 UTC 中的当前时刻,请使用 Instant.now

Instant instant = Instant.now() ;

提示:如果您想更改时间以进行人工测试,请将备用Clock 传递给Instant.now( Clock )

要生成String,其文本表示标准ISO 8601 格式的Instant 的值,只需调用toString。幸运的是,这个类没有Date 那样注入时区。 Instant::toString 方法讲述了一个简单的事实,即 UTC 中的时刻文本。

String output = instant.toString() ;

2019-12-03T10:15:30.032163Z

请注意,Instant 等日期时间对象没有“格式”。只有表示这些对象值的文本才有格式。文本和日期时间对象是不同的并且彼此分开。日期时间对象可以生成这样的文本,并且可以解析这样的文本,但实际上并不是文本。

如果您需要灵活地生成其他格式的文本,请应用ZoneOffset 来获取OffsetDateTime 对象,或应用ZoneId 来获取ZonedDateTime 对象。应用DateTimeFormatter 对象来生成文本。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

Java SE 8Java SE 9Java SE 10Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 添加了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuarter 和more。

【讨论】:

【参考方案3】:

java.util.Date 对象仅表示自称为“纪元”的标准基准时间(即 1970 年 1 月 1 日 00:00)以来的毫秒数: 00 GMT(或 UTC)。由于它不保存任何时区信息,它的 toString 函数应用 JVM 的时区以返回格式为 EEE MMM dd HH:mm:ss zzz yyyyString,从这个 毫秒 值。要以不同的格式和时区获取 java.util.Date 对象的 String 表示,您需要使用具有所需格式和适用时区的 SimpleDateFormat,例如

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main 
    public static void main(String[] args) 
        Date date = new Date();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);

        sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
        String strDateNewYork = sdf.format(date);
        System.out.println(strDateNewYork);

        sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
        String strDateUtc = sdf.format(date);
        System.out.println(strDateUtc);
    

输出:

2021-06-20T09:43:39.517-04:00
2021-06-20T13:43:39.517Z

ONLINE DEMO

java.time

java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,改用modern Date-Time API*

另外,下面引用的是来自home page of Joda-Time的通知:

请注意,从 Java SE 8 开始,用户被要求迁移到 java.time (JSR-310) - JDK 的核心部分,它取代了这个项目。

使用现代日期时间 API java.time 的解决方案:

import java.time.Instant;

public class Main 
    public static void main(String[] args) 
        Instant now = Instant.now();
        System.out.println(now);
    

输出:

2021-06-20T13:37:42.174352Z

ONLINE DEMO

Instant 表示UTC 中时间轴上的一个瞬时点。输出中的Z 是零时区偏移的timezone designator。它代表 Zulu 并指定 Etc/UTC 时区(其时区偏移量为 +00:00 小时)。

Trail: Date Time 了解有关现代日期时间 API 的更多信息。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。

【讨论】:

以上是关于“Java Date() 以 UTC 形式返回日期”——它实际上是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章