SimpleDateFormat-解析日期时出现奇怪的结果

时间:2018-12-04 13:57:55

标签: java date datetime time simpledateformat

我目前对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运行此示例。

我的PC位于CET(+1000),因此时区相等。因此预期结果将是:

  

2018年12月4日星期二22:22:01

但是我得到以下输出:

  

2018年12月4日星期二13:22:01

有人知道这里发生了什么吗?

2 个答案:

答案 0 :(得分:4)

您给它2018-12-04T22:22:01+1000,在UTC中为2018-12-04T12:22:01。 CET比UTC早1小时,因此您可以获得13小时。

答案 1 :(得分:1)

tl; dr

您最初的问题是错字+1000与错字+0100。但是,以下所有建议仍然适用。您正在使用可怕的旧类,应该避免。

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 [欧洲/布鲁塞尔]

避免使用旧的日期时间类

您正在使用与最早的Java版本捆绑在一起的可怕的旧日期时间类。几年前由 java.time 类取代。

使用适当的时区

仅供参考,CET不是实时区域。

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

ZoneId z = ZoneId.of( "America/Montreal" ) ;  

您可能指的是时区,例如Europe/BrusselsEurope/ParisEurope/BerlinAfrica/TunisEurope/Oslo

ISO 8601

您的输入字符串2018-12-04T22:22:01+1000为标准格式,由ISO 8601定义。

最后一部分+1000offset-from-UTC,意味着比UTC提前十个小时。因此,此值用于太平洋地区某个地区的人们使用的挂钟时间,例如时区Australia/Lindeman

请勿缩写偏移量表示法

该字符串+1000是偏移量的缩写,省略了小时和分钟(以及秒,如果有)之间的冒号分隔符。尽管标准允许这种遗漏,但我建议始终包括以下冒号:2018-12-04T22:22:01+10:00。以我的经验,某些库和协议在遇到此类字符串时会中断。而且,COLON的包含使该字符串对人类更具可读性。

OffsetDateTime

实际上,java.time.OffsetDateTime类用于默认情况下解析这些标准字符串,在这方面有一个错误,当省略COLON时无法解析。讨论于:

解决方法:

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对象将该值调整为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]

这些对象的所有三个(odtinstantzdt)都指向相同的同时时刻,即时间轴上的同一点。他们唯一的区别是挂钟时间。如果在澳大利亚,法国和冰岛的电话会议中的三个人(始终在UTC中)都同时抬起头来从各自墙上挂着的各自的时钟中读取当前时刻,则他们将为同一时刻读取三个不同的值。

查看所有此代码run live at that IdeOne.com page


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore