UTC日期解析Java中的不一致性

在解析UTC/GMT日期时发生了一些奇怪的事情。 我将日期格式设置为

 "yyyy-MM-dd'T'HH:mm:ss'Z'" 

其中Z代表UTC 。 我给出了以下日期字符串来解析:

 String startTimestampString = "2013-10-02T00:00:00Z"; 

我希望得到与输出相同的日期,但相反它显示

 2013-10-01 17:00:00.0 

现在肯定从这7小时的延迟来自哪里?

码:

 import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.TimeZone; public class DateTest { public static void main(String[] args) throws ParseException { SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); date.setTimeZone(TimeZone.getTimeZone("UTC")); System.out.println(TimeZone.getTimeZone("UTC").toString()); String startTimestampString = "2013-10-02T00:00:00Z"; long startTimestamp = date.parse(startTimestampString).getTime(); System.out.println(String.format("Long %d and timestamp %s", startTimestamp, new Timestamp(startTimestamp).toString())); } } 

输出:

 sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null] Long 1380672000000 and timestamp 2013-10-01 17:00:00.0 // ERROR timestamp should have been 2013-10-02 00:00:00.0 

java.util.Date没有时区

正如评论所说,你的问题不是理解java.util.Date工作的混乱方式。

Date对象没有时区,但似乎有一个,因为它的toString方法在生成文本表示(返回的String)时应用JVM的默认时区。

Java团队的这种糟糕的设计选择引起了很多混乱,包括StackOverflow上无数类似的问题。

日期不是字符串

这里的一个关键思想是由toString方法生成的String是一个全新的对象。 此字符串不是日期。 从您的默认时区看,该字符串是历史记录中该时刻的特定表示。 从巴黎或蒙特利尔或加尔各答时区看,历史上的同一时刻显示为两个不同的时间值。

避免使用java.util.Date

不要在java.util.Date和.Calendar以及SimpleDateFormat上浪费你的时间。 众所周知,它们很麻烦。 在Java 8中使用Joda-Time或新的java.time包 (受Joda-Time启发)。

乔达时间

Joda-Time 2.3中的示例代码。 您的格式采用标准ISO 8601格式。 Joda-Time使用ISO 8601作为其默认值,因此在您的情况下不需要解析器/格式化程序。 Joda-Time自动使用内置格式化程序来解析符合ISO 8601标准的字符串。

 String input = "2013-10-02T00:00:00Z"; DateTimeZone timeZoneParis = DateTimeZone.forID( "Europe/Paris" ); DateTime dateTimeParis = new DateTime( input, timeZoneParis ); DateTime dateTimeMontréal = dateTimeParis.withZone( DateTimeZone.forID( "America/Montreal" ) ); DateTime dateTimeIndia = dateTimeParis.withZone( DateTimeZone.forID( "Asia/Kolkata" ) ); DateTime dateTimeUtc = dateTimeParis.withZone( DateTimeZone.UTC ); DateTimeFormatter formatter = DateTimeFormat.forStyle( "FF" ).withLocale( Locale.CANADA_FRENCH ); 

转储到控制台……

 System.out.println( "dateTimeParis: " + dateTimeParis ); System.out.println( "dateTimeMontréal: " + dateTimeMontréal ); System.out.println( "dateTimeMontréal formatted: " + formatter.print( dateTimeMontréal ) ); System.out.println( "dateTimeIndia: " + dateTimeIndia ); System.out.println( "dateTimeUtc: " + dateTimeUtc ); 

跑的时候……

 dateTimeParis: 2013-10-02T02:00:00.000+02:00 dateTimeMontréal: 2013-10-01T20:00:00.000-04:00 dateTimeMontréal formatted: mardi 1 octobre 2013 20 h 00 EDT dateTimeIndia: 2013-10-02T05:30:00.000+05:30 dateTimeUtc: 2013-10-02T00:00:00.000Z