“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.Date
、Calendar
和 SimpleDateFormat
。
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 8、Java SE 9、Java SE 10、Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 添加了一些小功能和修复。 Java SE 6 和 Java 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 的试验场。您可能会在这里找到一些有用的类,例如Interval
、YearWeek
、YearQuarter
和more。
【讨论】:
【参考方案3】:java.util.Date
对象仅表示自称为“纪元”的标准基准时间(即 1970 年 1 月 1 日 00:00)以来的毫秒数: 00 GMT(或 UTC)。由于它不保存任何时区信息,它的 toString
函数应用 JVM 的时区以返回格式为 EEE MMM dd HH:mm:ss zzz yyyy
的 String
,从这个 毫秒 值。要以不同的格式和时区获取 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 形式返回日期”——它实际上是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章