【问题标题】:Why date is changing with same number of milliseconds on different time zones?为什么日期在不同时区以相同的毫秒数变化?
【发布时间】:2013-01-28 22:50:53
【问题描述】:

我们知道 java.util.Date 的 getTime 方法返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。

我注意到一个奇怪的情况如下;

系统时区是:(UTC+02:00)伊斯坦布尔

Date currentDate = new Date();
System.out.println(currentDate .getTime());
System.out.println(currentDate);

Java 控制台输出:

1360753217219

2013 年 2 月 13 日星期三 13:00:17 VET


然后我的 javascript 插件正在使用这个长对象,如下所示;

Javascript:

console.log(new Date(1360753217219));

浏览器控制台输出:

日期 {2013 年 2 月 13 日星期三 13:00:17 GMT+0200(土耳其标准时间)}


不过,没关系!将我的本地 时区 更改为 (UTC-04:30) 加拉加斯后,情况和小时以相同的毫秒数变化如下;


Javascript:

console.log(new Date(1360753217219));

浏览器控制台输出:

日期 {2013 年 2 月 13 日星期三 06:30:17 GMT-0430(委内瑞拉标准时间)}

有人能解释一下吗?那是js的错误吗?或者更重要的是,我应该如何在 java 端处理这个问题,以便在 js 端的不同时区获得相同的日期和相同的毫秒数?

谢谢!

【问题讨论】:

  • console.log((new Date(1360753217219)).toString())console.log((new Date(1360753217219)).toUTCString()) 给你什么?
  • 对不起,我只是没发现问题。加拉加斯的 6:30 是土耳其的 13:00。你到底想做什么?
  • 毫秒是相对于 UTC 的,有什么意义?
  • 相同的“毫秒数”会导致不同时区的时间不同。我认为,下面的答案是正确的。

标签: java javascript date timezone primefaces-extensions


【解决方案1】:

毫秒与时区无关。自格林威治标准时间 1970 年 1 月 1 日起,时间以绝对值衡量。所以,这个想法是你得到毫秒,然后在事后计算出给定时区的本地时间。如果你仔细想想,这是有道理的。无论您身在何处,自 1970 年以来经过的毫秒数都是相同的。

这有点令人困惑,但不要为了调整时区而在毫秒之间徘徊。每个 Date 库都有将毫秒标记转换为特定时区的本地时间的机制。

因此,如果您的具体问题是如何在服务器和客户端之间有效地传达日期(您使用的语言并不重要),那么答案是来回传递毫秒并在其中一个上进行计算是完全安全的如果这对您在该时间所做的事情的背景很重要,请站在您谈论的全球特定时间。

【讨论】:

  • 那是“自 1970 年 1 月 1 日,GMT - OP 的困惑可能源于未能理解纪元时间在一个时区,因为它必须是。否则经过的毫秒数将不会“无论您身在何处都相同”。
【解决方案2】:

不是错误,时区就是这样工作的。

如果你现在给委内瑞拉的人打电话问他现在几点了,他会告诉你现在是 比土耳其时间早 6.5 小时(根据您的示例)。

正如您所提到的,您处理的数字表示自 1970 年 00:00:00 GMT 以来的毫秒数,在加拉加斯的同一秒,时间是 1969 年 12 月 31 日19:30 GMT-0430

因此,无论多少秒后,委内瑞拉的时间仍将比格林威治标准时间早 4:30 小时。

如果您使用相同的输入(毫秒),您将无法在不同时区获得完全相同的日期,因为这完全是错误的。

如果您想获得相同的结果,可以将时区差异(在本例中为 6.5 小时)添加到输出中。按照 Dr.Dredel 的建议,您可能不应该搞砸毫秒。

【讨论】:

  • 更改毫秒实际上会更改时间(而不是时区)。每个日期戳都有时区偏移量作为整体字符串的一部分。基于此,很容易计算出需要从人类清晰的时间戳中加/减多少小时。以毫秒为单位肯定会给您带来麻烦,而且绝对是错误的解决方法。
  • @Dr.Dredel 感谢您的评论,我相应地更正了我的回复。
【解决方案3】:

tl;博士

Instant.ofEpochMilli( 1_360_753_217_219L )         // UTC

2013-02-13T11:00:17.219Z

Instant.ofEpochMilli( 1_360_753_217_219L )    
       .atZone( ZoneId.of( "Europe/Istanbul" ) )   // Same moment, two hours *ahead* of UTC.

2013-02-13T13:00:17.219+02:00[欧洲/伊斯坦布尔]

Instant.ofEpochMilli( 1_360_753_217_219L )    
       .atZone( ZoneId.of( "America/Caracas" ) )   // Same moment, four-and-a-half hours *behind* UTC.

2013-02-13T06:30:17.219-04:30[美国/加拉加斯]

使用 java.time

您正在使用与最早版本的 Java 捆绑在一起的麻烦的旧日期时间类。它们现在是遗留的,被 java.time 类取代,这是任何平台上最好的日期时间库。

从自 1970 年初的 epoch reference dateUTC (1970-01-01T00:00:00Z) 的毫秒数开始。其他人指出,您可能不知道纪元参考日期有一个时区,而在这个纪元中,该时区是 UTC,偏移量为零。所有其他偏移量都根据这个偏移量来衡量,比 UTC 早或晚 UTC 小时数和分钟数。

Instant 类表示UTC 中时间轴上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

long input = 1_360_753_217_219L ;
Instant instant = Instant.ofEpochMilli( input ) ;

instant.toString(): 2013-02-13T11:00:17.219Z

如果您想通过特定地区的wall-clock time 的镜头看到同一时刻,请应用时区。

时区是特定区域使用的偏移量的过去、现在和未来变化的历史记录。

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId zEurope_Istanbul = ZoneId.of( "Europe/Istanbul" ) ;
ZonedDateTime zdtEurope_Istanbul = instant.atZone( zEurope_Istanbul ) ;

zdtEurope_Istanbul.toString(): 2013-02-13T13:00:17.219+02:00[欧洲/伊斯坦布尔]

您可以应用其他时区。

ZoneId zAmerica_Caracas = ZoneId.of( "America/Caracas" ) ;
ZonedDateTime zdtAmerica_Caracas = zdtEurope_Istanbul.withZoneSameInstant( zAmerica_Caracas ) ;

zdtAmerica_Caracas.toString(): 2013-02-13T06:30:17.219-04:30[美国/加拉加斯]

看到这个code live at IdeOne.com

instant 和 zdtEurope_Istanbul 和 zdtAmerica_Caracas 这三个对象都代表同一时刻,时间轴上的同一点。

您的从纪元计数表示 UTC 时间上午 11 点。伊斯坦布尔比 UTC提前 两个小时,因此同一时刻的时间是上午 11 点、下午 1 点 (13:00) 之后的两个小时。委内瑞拉比世界标准时间四个半小时,因此同一时刻的时间是早上 6:30。这些都是有道理的,都是同一时刻,但挂钟时间不同。

ISO 8601

不要使用 count-from-epoch 来交换或存储日期时间值。这是容易出错的,人类不可能有意义地阅读,并且由于各种软件系统和不同粒度(整秒、毫秒、微秒、纳秒等)使用至少几十个纪元参考日期而模棱两可。 )。

在 JVM 之外传递日期时间值时,使用标准的 ISO 8601 格式进行文本表示。 java.time 类在解析/生成字符串时默认使用标准格式。您可以在此答案的示例代码中看到这些格式。


关于java.time

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

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

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

从哪里获得 java.time 类?

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 2019-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-17
    • 1970-01-01
    相关资源
    最近更新 更多