是的,关于Java和Javascript中Date的另一个问题。
Java和浏览器(Chrome)的时区均为GMT+4(莫斯科)。
<script language="javascript">
var d = new Date(170798400000);
document.write(d);
</script>
给出:孙1975年6月1日00:00:00 GMT+0400(俄罗斯标准时间)
public class Test {
public static void main(String[] args) {
java.util.Date d = new java.util.Date(170798400000L); // the same epoch value!
System.out.println(d);
}
}
给出:5月31日星期六23:00:00 MSK 1975
如果我将历元值更改为2011-2012年(在俄罗斯取消夏令时后),输出是可以的。时区更新工具运行正常。
这是一个bug还是已记录的功能?除了格式化和重新解析(例如YYYY-MM-dd HH:MM:SS)之外,还有什么方法可以处理这个问题吗
来自javadoc:
日期(长日期)
分配Date对象并对其进行初始化,以表示自1970年1月1日00:00:00 GMT的标准基准时间以来指定的毫秒数。
来自javascript引用:
新日期(毫秒)
毫秒-表示自1970年1月1日00:00:00 UTC(Unix Epoch)以来的毫秒数的整数值。
这是一个bug还是有文档记录的功能?
这不是Javascript的错误。至少,我看不出我可以这么说。
浏览器的Javascript引擎正在返回转换为"GMT+4"的时间。显然,你想要的是MSK,它与GMT+4不同(正如你的评论中所指出的)。Javascript不了解MSK并不算是一个bug,而是缺少一个功能。也许js对时区没有那么详细的了解是"错误的",但它不是一个bug。
除了格式化和重新解析(例如YYYY-MM-dd HH:MM:SS)之外,还有什么方法可以处理这个问题吗?
追踪时区的所有任意细节需要做很多工作。据我所知,没有这样的代码库可以为javascript提供所有的工作。因此,我相信,是的,如果你想使用真正的MSK,你必须自己手动编码转换。
时间的字符串表示(如"Sun Jun 01 1975 00:00:00 GMT+0400"
)用于人类。时间值(自1970年1月1日UTC以来的毫秒)用于存储和计算。
那里没有bug。在JavaScript中,根据规范,字符串表示的内容取决于实现。在Java中,根据文档,夏令时可能会被反映出来。
来自JavaScript规范:
15.9.5.2
Date.prototype.toString ( )
此函数返回一个字符串值。String的内容依赖于实现->,但旨在以方便的、可读的形式表示当前时区中的日期
来自Java文档:
java.util.Date, public String toString()
Converts this Date object to a String of the form: dow mon dd hh:mm:ss zzz yyyy
where:
...
zzz is the time zone (and may reflect daylight saving time).`
tl;dr
Instant.ofEpochMilli( 170_798_400_000L )
1975-05-31T20:00:00Z
…和…
Instant.ofEpochMilli( 170_798_400_000L )
.atZone( ZoneId.of( "Europe/Moscow" ) )
1975-05-31T23:00+03:00[欧洲/莫斯科]
请参阅实时代码。
使用java.time
现代方法使用java 8及更高版本中的java.time类。
您正在使用Java中麻烦的旧日期时间类,这些类现在是遗留的。在这些类的许多问题中,Date::toString
方法在生成字符串时应用当前默认时区,这是出于善意但令人困惑的特性。内部值实际上总是以UTC为单位,但toString
会产生一种错觉,即Date
有时区,而实际上它没有。这就解释了你的MSK
之谜。
[更令人困惑的是,实际上在Date
中有一个时区,但与本次讨论无关。那些旧的Date
/Calendar
类一团糟。幸运的是,Java现在拥有任何平台上最好的类内日期-时间框架:Java.time。]
显然,您的输入表示自Unix纪元1970-01-01T00:00:00Z以来的毫秒数。
Instant
类表示以UTC为单位的时间线上的一个时刻,分辨率为纳秒(最多九(9)位小数)。这个类可以直接解析您的输入数字。
Instant instant = Instant.ofEpochMilli( 170_798_400_000L ) ;
instant.toString():1975-05-31T20:00:00Z
如果你想通过特定地区的挂钟时间来观察同一时刻,可以应用时区。应用ZoneId
以获得ZonedDateTime
对象。
ZoneId z = ZoneId.of( "Europe/Moscow" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
1975-05-31T23:00+03:00[欧洲/莫斯科]
JavaScript库不正确
您对JavaScript库的调用结果不正确,偏移量为+04:00。根据维基百科,莫斯科时间从1930年到1981年提前了三个小时,+03:00。java.time框架在1975年全年产生+03:00的正确结果。请参阅俄罗斯时间了解更多信息。注意:我不是俄罗斯/苏联时期的专家。
关于java.time
java.time框架是在java 8及更高版本中构建的。这些类取代了诸如java.util.Date
、Calendar
、&SimpleDateFormat
。
JodaTime项目现在处于维护模式,建议迁移到java.Time类。
要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。规范是JSR310。
从哪里获得java.time类?
- Java SE 8、Java SE 9及更高版本
- 内置
- 标准Java API的一部分,带有捆绑实现
- Java 9添加了一些小功能和修复程序
- Java SE 6和Java SE 7
- 大部分java.time功能都是向后移植到Java6&7英寸ThreeTen背包
- 安卓
- ThreeTenABP项目专门为安卓系统改编了ThreeTen Backport(如上所述)
- 请参阅如何使用ThreeTenABP…
ThreeTen Extra项目通过附加类扩展java.time。这个项目是将来可能添加到java.time的试验场。您可以在这里找到一些有用的类,如Interval
、YearWeek
、YearQuarter
等等。