【问题标题】:Why some dates being shown as BST and some as GMT when formatting Date? [duplicate]为什么在格式化日期时有些日期显示为 BST 而有些显示为 GMT? [复制]
【发布时间】:2017-12-05 15:03:26
【问题描述】:

我有以下 2 个输入字符串:

String string1 = "2017-01-30T13:00:00+0000"
String string2 = "2018-06-23T16:00:00+0000"

对于 string1,当我这样做时,我会得到以下日期:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date startDate = formatter.parse(string1);

结果 - Mon Jan 30 13:00:00 GMT 2017

但是当我对 string2 执行相同操作时,我在调试器中得到以下信息:

Sat Jun 23 17:00:00 BST 2018

为什么我在这里得到不同的时区?

【问题讨论】:

  • 可能是因为它使用您的默认语言环境,而英国会根据日期在时区之间切换。
  • formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
  • 使用 UTC 比使用 GMT 更好 - 意图更清晰。请注意,并不是英国“在时区之间切换”——而是欧洲/伦敦时区会随着时间在 GMT 和 BST 之间切换。
  • @JonSkeet 感谢您在技术上更正确
  • 您正在使用早已过时且臭名昭著的麻烦 SimpleDateFormat 类。有什么特别的原因吗?我建议你把它扔掉,改用java.time, the modern Java date and time API also known as JSR-310。使用起来要好得多。并且不会表现出您所经历的令人惊讶的行为。

标签: java date parsing timezone simpledateformat


【解决方案1】:

tl;博士

OffsetDateTime.parse( "2017-01-30T13:00:00+0000" )

java.time

在您使用的麻烦的旧遗留类的许多设计缺陷中,Date::toString 方法在生成字符串时应用了您的 JVM 的当前默认时区。因此,虽然内部值实际上是 UTC,但令人困惑的是,它似乎有另一个时区。此外,您当前的默认时区在两个日期之间经历了夏令时转换。

改为使用现代 java.time 类。将您的输入字符串解析为 OffsetDateTime 对象。

OffsetDateTime odt = OffsetDateTime.parse( "2017-01-30T13:00:00+0000" ) ; 

Java 8 和 Java 9 中的一个错误可能会在解析您的 UTC 偏移时在小时和分钟之间缺少冒号。作为一种解决方法,请添加冒号。

OffsetDateTime odt = OffsetDateTime.parse( "2017-01-30T13:00:00+0000".replace( "+0000" , "+00:00" ) ; 

调用 toString 以生成标准 ISO 8601 格式的字符串。结果没有被动态应用的任何时区掺假,不像Date

如果需要,分配ZoneId 以获取ZonedDateTime

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

ZoneId z = ZoneId.of( "Europe/London" ) ; 
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

complete code for both inputs run live at IdeOne.com

String input2017 = "2017-01-30T13:00:00+0000".replace( "+0000" , "+00:00" ) ;  // Workaround Java 8 bug where omitted colon in offset-from-UTC fails to parse. Fixed in Java 9.
OffsetDateTime odt2017 = OffsetDateTime.parse( input2017 ) ; 

String input2018 = "2018-06-23T16:00:00+0000".replace( "+0000" , "+00:00" ) ;  // Workaround Java 8 bug where omitted colon in offset-from-UTC fails to parse. Fixed in Java 9.
OffsetDateTime odt2018 = OffsetDateTime.parse( input2018 ) ; 

ZoneId z = ZoneId.of( "Europe/London" ) ; 
ZonedDateTime zdt2017 = odt2017.atZoneSameInstant( z ) ;
ZonedDateTime zdt2018 = odt2018.atZoneSameInstant( z ) ;

转储到控制台。

System.out.println( "input2017: " + input2017 ) ;
System.out.println( "odt2017: " + odt2017 ) ;
System.out.println( "zdt2017: " + zdt2017 ) ;
System.out.println( "" ) ;  // Blank line.

System.out.println( "input2018: " + input2018 ) ;
System.out.println( "odt2018: " + odt2018 ) ;
System.out.println( "zdt2018: " + zdt2018 ) ;

请注意,在 2018 年 6 月,Europe/London 的时间由于Daylight Saving Time (DST) 而向前跳了一个小时。在冬季,伦敦区采用 UTC,但在夏季,比 UTC提前一小时

2017

输入2017:2017-01-30T13:00:00+00:00

odt2017: 2017-01-30T13:00Z

zdt2017: 2017-01-30T13:00Z[欧洲/伦敦]

2018

输入2018:2018-06-23T16:00:00+00:00

odt2018: 2018-06-23T16:00Z

zdt2018: 2018-06-23T17:00+01:00[欧洲/伦敦]

搜索 Stack Overflow 以获取更多信息,因为这个主题已经被处理过很多次了。


关于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 类?

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

【讨论】:

  • @OleV.V.错字已修复。谢谢。
猜你喜欢
  • 2015-08-02
  • 1970-01-01
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-05
  • 2021-03-29
  • 1970-01-01
相关资源
最近更新 更多