为什么格式化日期时有些日期显示为 BST 而有些显示为 GMT?

Why some dates being shown as BST and some as GMT when formatting Date?

我有以下 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

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

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

指定 proper time zone name in the format of continent/region, such as America/Montreal, Africa/CasablancaPacific/Auckland。切勿使用 BSTESTIST 等 3-4 个字母的缩写,因为它们 不是 真实时区,未标准化,也不是甚至是独一无二的(!)。

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

input2017: 2017-01-30T13:00:00+00:00

odt2017: 2017-01-30T13:00Z

zdt2017: 2017-01-30T13:00Z[Europe/London]

2018

input2018: 2018-06-23T16:00:00+00:00

odt2018: 2018-06-23T16:00Z

zdt2018: 2018-06-23T17:00+01:00[Europe/London]

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


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time 类.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与您的数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* 类.

在哪里获取java.time类?

  • Java SE 8, Java SE 9,及以后
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and Java SE 7
  • Android
    • Android java.time 类.
    • 捆绑实施的更高版本
    • 对于较早的Android,ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.