SimpleDateFormat - 解析日期时出现奇怪的结果
SimpleDateFormat - Strange result when parsing Date
我目前对 SimpleDateFormatter 的以下简单用法一头雾水:
import java.text.ParseException;
import java.text.SimpleDateFormat;
public static void main(String[] args) throws ParseException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse("2018-12-04T22:22:01+1000"));
}
我是 运行 这个例子 JDK 1.8.0_192.
我的电脑位于 CET (+1000),所以时区相同。所以预期的结果是:
Tue Dec 04 22:22:01 CET 2018
但我得到以下输出:
Tue Dec 04 13:22:01 CET 2018
有人知道这里发生了什么吗?
你给它 2018-12-04T22:22:01+1000
,在 UTC 中是 2018-12-04T12:22:01
。虽然 CET 比 UTC 早 1 小时,所以你得到小时 13
.
tl;博士
您最初的问题是 +1000
与 +0100
的拼写错误。尽管如此,以下所有建议仍然适用。您正在使用可怕的旧 classes,应该避免。
OffsetDateTime.parse(
"2018-12-04T22:22:01+1000" , // Input in standard ISO 8601, with the COLON omitted from the offset as allowed by the standard but breaking some libraries such as `OffsetDateTime.parse`.
DateTimeFormatter.ofPattern(
"uuuu-MM-dd'T'HH:mm:ssX"
)
) // Returns a `OffsetDateTime` object.
.toInstant() // Adjust into UTC. Returns an `Instant` object. Same moment, different wall-clock time.
.atZone( // Adjust from UTC to some time zone. Same moment, different wall-clock time.
ZoneId.of( "Europe/Brussels" )
) // Returns a `ZonedDateTime` object.
.toString() // Generate text representing this `ZonedDateTime` object in standard ISO 8601 format but wisely extending the standard by appending the name of the time zone in square brackets.
18-12-04T13:22:01+01:00[Europe/Brussels]
避免遗留 date-time classes
您正在使用与 Java 的最早版本捆绑在一起的可怕的旧 date-time classes。多年前由 java.time classes.
补充
使用正确的时区
仅供参考,CET
不是实时时区。
以continent/region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 EST
或 IST
等 2-4 字母缩写,因为它们 不是 真实时区,未标准化,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
您可能指的是时区,例如 Europe/Brussels
、Europe/Paris
、Europe/Berlin
、Africa/Tunis
或 Europe/Oslo
。
ISO 8601
您输入的字符串 2018-12-04T22:22:01+1000
是标准格式,由 ISO 8601 定义。
最后一部分 +1000
是 offset-from-UTC,表示比 UTC 时间早十小时。所以这个值是为 wall-clock 人们使用的时间是太平洋的某个地区,例如时区 Australia/Lindeman
。
不要缩写偏移符号
该字符串 +1000
是偏移量的缩写,省略了小时和分钟(以及秒,如果有的话)之间的 COLON 字符定界符。虽然标准允许这种省略,但我建议始终包含冒号:2018-12-04T22:22:01+10:00
。根据我的经验,某些库和协议在遇到此类字符串时会中断。包含 COLON 使字符串更易于人类阅读。
OffsetDateTime
的确,java.time.OffsetDateTime
class默认解析这些标准字符串在这方面有一个错误,当省略COLON时解析失败。讨论于:
- Java 8 Date and Time: parse ISO 8601 string without colon in offset
- Cannot parse String in ISO 8601 format, lacking colon in offset, to Java 8 Date
解决方法:
OffsetDateTime odt =
OffsetDateTime.parse(
"2018-12-04T22:22:01+1000" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssX" )
)
;
参见代码示例 running live at IdeOne.com。
odt.toString(): 2018-12-04T22:22:01+10:00
通过提取 Instant
object 将该值调整为 UTC。 Instant
根据定义始终采用 UTC。
Instant instant = odt.toString() ;
instant.toString(): 2018-12-04T12:22:01Z
最后,我们可以调整到您自己的狭隘时区。
CET
我假设你指的是 Europe/Paris
等时区。
ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
调用 ZonedDateTime::toString
时,文本以标准 ISO 8601 格式生成,但明智地扩展了标准以在方括号中附加时区名称。
zdt.toString(): 2018-12-04T13:22:01+01:00[Europe/Paris]
这三个 object(odt
、instant
和 zdt
)指的是同一时刻,时间轴上的同一点。它们唯一的区别是 wall-clock 时间。如果在澳大利亚、法国和冰岛(始终采用 UTC)的电话会议中的三个人同时抬头从挂在当地墙上的各自时钟上读取当前时刻,他们将同时读取同一时刻的三个不同值。
查看所有代码 run live at that IdeOne.com page。
关于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 classes.
要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310。
您可以直接与您的数据库交换 java.time object。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.*
classes.
从哪里获得java.time classes?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11,以及后来的 - 标准 Java API 的一部分,带有捆绑实现。
- Java 9 添加了一些小功能和修复。
- Java SE 6 and Java SE 7
- 大多数 java.time 功能是 back-ported 到 Java ThreeTen-Backport 中的 6 和 7。
- Android
- Android 的更高版本 java.time classes.
- 对于早期的Android(<26),ThreeTenABP项目适应 ThreeTen-Backport(如上所述)。请参阅如何使用 ThreeTenABP…。
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.
我目前对 SimpleDateFormatter 的以下简单用法一头雾水:
import java.text.ParseException;
import java.text.SimpleDateFormat;
public static void main(String[] args) throws ParseException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse("2018-12-04T22:22:01+1000"));
}
我是 运行 这个例子 JDK 1.8.0_192.
我的电脑位于 CET (+1000),所以时区相同。所以预期的结果是:
Tue Dec 04 22:22:01 CET 2018
但我得到以下输出:
Tue Dec 04 13:22:01 CET 2018
有人知道这里发生了什么吗?
你给它 2018-12-04T22:22:01+1000
,在 UTC 中是 2018-12-04T12:22:01
。虽然 CET 比 UTC 早 1 小时,所以你得到小时 13
.
tl;博士
您最初的问题是 +1000
与 +0100
的拼写错误。尽管如此,以下所有建议仍然适用。您正在使用可怕的旧 classes,应该避免。
OffsetDateTime.parse(
"2018-12-04T22:22:01+1000" , // Input in standard ISO 8601, with the COLON omitted from the offset as allowed by the standard but breaking some libraries such as `OffsetDateTime.parse`.
DateTimeFormatter.ofPattern(
"uuuu-MM-dd'T'HH:mm:ssX"
)
) // Returns a `OffsetDateTime` object.
.toInstant() // Adjust into UTC. Returns an `Instant` object. Same moment, different wall-clock time.
.atZone( // Adjust from UTC to some time zone. Same moment, different wall-clock time.
ZoneId.of( "Europe/Brussels" )
) // Returns a `ZonedDateTime` object.
.toString() // Generate text representing this `ZonedDateTime` object in standard ISO 8601 format but wisely extending the standard by appending the name of the time zone in square brackets.
18-12-04T13:22:01+01:00[Europe/Brussels]
避免遗留 date-time classes
您正在使用与 Java 的最早版本捆绑在一起的可怕的旧 date-time classes。多年前由 java.time classes.
补充使用正确的时区
仅供参考,CET
不是实时时区。
以continent/region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 EST
或 IST
等 2-4 字母缩写,因为它们 不是 真实时区,未标准化,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
您可能指的是时区,例如 Europe/Brussels
、Europe/Paris
、Europe/Berlin
、Africa/Tunis
或 Europe/Oslo
。
ISO 8601
您输入的字符串 2018-12-04T22:22:01+1000
是标准格式,由 ISO 8601 定义。
最后一部分 +1000
是 offset-from-UTC,表示比 UTC 时间早十小时。所以这个值是为 wall-clock 人们使用的时间是太平洋的某个地区,例如时区 Australia/Lindeman
。
不要缩写偏移符号
该字符串 +1000
是偏移量的缩写,省略了小时和分钟(以及秒,如果有的话)之间的 COLON 字符定界符。虽然标准允许这种省略,但我建议始终包含冒号:2018-12-04T22:22:01+10:00
。根据我的经验,某些库和协议在遇到此类字符串时会中断。包含 COLON 使字符串更易于人类阅读。
OffsetDateTime
的确,java.time.OffsetDateTime
class默认解析这些标准字符串在这方面有一个错误,当省略COLON时解析失败。讨论于:
- Java 8 Date and Time: parse ISO 8601 string without colon in offset
- Cannot parse String in ISO 8601 format, lacking colon in offset, to Java 8 Date
解决方法:
OffsetDateTime odt =
OffsetDateTime.parse(
"2018-12-04T22:22:01+1000" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssX" )
)
;
参见代码示例 running live at IdeOne.com。
odt.toString(): 2018-12-04T22:22:01+10:00
通过提取 Instant
object 将该值调整为 UTC。 Instant
根据定义始终采用 UTC。
Instant instant = odt.toString() ;
instant.toString(): 2018-12-04T12:22:01Z
最后,我们可以调整到您自己的狭隘时区。
CET
我假设你指的是 Europe/Paris
等时区。
ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
调用 ZonedDateTime::toString
时,文本以标准 ISO 8601 格式生成,但明智地扩展了标准以在方括号中附加时区名称。
zdt.toString(): 2018-12-04T13:22:01+01:00[Europe/Paris]
这三个 object(odt
、instant
和 zdt
)指的是同一时刻,时间轴上的同一点。它们唯一的区别是 wall-clock 时间。如果在澳大利亚、法国和冰岛(始终采用 UTC)的电话会议中的三个人同时抬头从挂在当地墙上的各自时钟上读取当前时刻,他们将同时读取同一时刻的三个不同值。
查看所有代码 run live at that IdeOne.com page。
关于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 classes.
要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310。
您可以直接与您的数据库交换 java.time object。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.*
classes.
从哪里获得java.time classes?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11,以及后来的 - 标准 Java API 的一部分,带有捆绑实现。
- Java 9 添加了一些小功能和修复。
- Java SE 6 and Java SE 7
- 大多数 java.time 功能是 back-ported 到 Java ThreeTen-Backport 中的 6 和 7。
- Android
- Android 的更高版本 java.time classes.
- 对于早期的Android(<26),ThreeTenABP项目适应 ThreeTen-Backport(如上所述)。请参阅如何使用 ThreeTenABP…。
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.