我们知道java.util.Date的getTime方法返回自此Date对象表示的1970年1月1日00:00:00 GMT以来的毫秒数。
我注意到一种奇怪的情况如下;
系统时区是:(UTC + 02:00)伊斯坦布尔
Date currentDate = new Date();
System.out.println(currentDate .getTime());
System.out.println(currentDate);
Java ConsoleOutput:
1360753217219
Wed Feb 13 13:00:17 VET 2013
然后我的javascript插件正在使用下面这个长对象;
使用Javascript:
console.log(new Date(1360753217219));
浏览器控制台输出:
日期{Wed Feb 13 2013 13:00:17 GMT + 0200(土耳其标准时间)}
但是,一切都好!将我的本地时区更改为(UTC-04:30)加拉加斯后,情况和小时的变化如下,并以相同的毫秒数进行更改;
使用Javascript:
console.log(new Date(1360753217219));
浏览器控制台输出:
日期{Wed Feb 13 2013 06:30:17 GMT-0430(委内瑞拉标准时间)}
有人可以解释一下吗?这是js的bug吗?或者更重要的是,我应该如何在java方面处理这个问题,以便在js端为不同的时区获得相同的毫秒数?
谢谢!
答案 0 :(得分:8)
毫秒是时区不可知的。自格林威治标准时间1970年1月1日起,时间以绝对值计算。所以,我们的想法是你得到毫秒,然后计算出给定时区的本地时间是什么。如果你考虑一下,这是有道理的。无论你身在何处,自1970年以来的毫秒数都是相同的。
它有点令人困惑,但为了调整时区,不要用毫秒面条。每个日期库都具有将毫秒标记转换为特定于时区的本地时间的机制。
因此,如果您的具体问题是如何在服务器和客户端之间有效地传达日期(您正在使用的语言并不重要),那么答案是来回传递毫秒是非常安全的。你所谈论的全球特定时间,如果这对你当时正在做的事情的背景很重要。
答案 1 :(得分:2)
不是错误,而是时区如何运作。
如果你现在给委内瑞拉的某个人打电话并问他现在几点,他会告诉你的 6.5(根据你的例子)比土耳其时间早几个小时。
正如你所提到的,你所处理的数字代表自1970年以来的毫秒数,00:00:00 格林威治标准时间,在加拉加斯的同一秒,时间是31.12.1969 19:30 GMT-0430
所以,不过几秒钟之后,委内瑞拉的时间仍然比格林尼治标准时间早4:30小时。
如果您使用相同的输入(毫秒),则无法在不同的时区获得完全相同的日期,因为这只会是错误的。
如果您想获得相同的结果,可以将时区(本例中为6.5小时)的差异添加到输出中。按照Dr.Dredel的建议,你可能不应该把毫秒搞得一团糟。
答案 2 :(得分:1)
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捆绑在一起的麻烦的旧日期时间类。它们现在已经遗留下来,取而代之的是java.time类,它是任何平台上最好的日期时间库。
从epoch reference date(1970-01-01T00:00:00Z)1970年初UTC以来的毫秒数开始。其他人指出,你可能没有意识到一个纪元参考日期有一个时区,而这个时期这个区域是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/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
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 [America / Caracas]
这三个对象,即时& zdtEurope_Istanbul& zdtAmerica_Caracas,全部代表同一时刻,时间轴上的同一点。
您的纪元计数代表UTC上午11点。伊斯坦布尔是UTC前两小时,因此同一时刻的时间是上午11点,下午1点(13点)后两小时。委内瑞拉 UTC落后了四个半小时,所以同一时刻的时间是早上6:30。这些都是有意义的,同时但不同的挂钟时间。
不要使用count-from-epoch来交换或存储日期时间值。这是容易出错的,不可能被人类有意义地阅读,并且模糊不清,因为各种软件系统使用的不同粒度(整秒,毫秒,微秒,纳秒等)至少有十几个时期参考日期。 )。
在JVM外部传递日期时间值时,请使用标准ISO 8601格式进行文本表示。在解析/生成字符串时,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类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。