在时区之间转换,即使相同的时间更改日期(UTC到UTC)

时间:2014-06-21 10:51:34

标签: java datetime timezone jodatime

我一直很困惑为什么以下代码导致我的日期从25日更改为24日

SimpleDateFormat sd = new SimpleDateFormat("dd/MM/yyyy");
DateTimeZone customerZone = DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"));
DateTimeZone serverZone = DateTimeZone.UTC;

Date date = sd.parse("25/05/2014");

DateTime source = new DateTime(date).withZone(customerZone).withHourOfDay(5);

LocalDateTime ldate = new LocalDateTime(source, serverZone);
System.out.println(ldate.toDate()); //expected to be Sat May 25 05:00:00 

结果     " Sat May 24 05:00:00 SAST 2014"

3 个答案:

答案 0 :(得分:1)

我认为在UTC客户时区,南非的25/05/2014 00:00:00被视为24/5,10:00。最后,小时设定为上午5点。

答案 1 :(得分:1)

您尚未为SimpleDateFormat设置时区,因此它默认为您所在环境的时区,我猜是"Africa/Johannesburg",因为您的结果中有SAST。

所以当你做这个部分时:

SimpleDateFormat sd = new SimpleDateFormat("dd/MM/yyyy");
Date date = sd.parse("25/05/2014");

date对象将在SAST午夜,即UTC之前的晚上10点。其余部分来自那里,因为从那时起你正在使用UTC。

此外,在最后,您调用toDate,这会产生Date个对象。输出时,本地时区再次影响结果。

您可以考虑在setTimeZone对象上调用SimpleDateFormat。这至少会使开始部分正确。但是您也应该使用format方法输出最终字符串。

但是,更好的解决方案是使用JodaTime's DateTimeFormatter代替。然后,您根本不必使用SimpleDateFormatDate

答案 2 :(得分:1)

answer by Matt Johnson是正确的。省略时区时,将应用JVM的默认时区。我建议始终指定时区而不是依赖隐式默认值,即使通过显式调用getDefault()来完成。

Pure Joda-Time

仅供参考,这里有一些示例代码,可以更好地完成这项工作。这种方式仅使用Joda-Time。正如您的问题所示,混合使用Joda-Time和java.util.Date/Calendar会导致混乱和痛苦。此外,java.util.Date,.Calendar和SimpleDateFormat类是众所周知的麻烦,应该避免。

顺便说一下,不需要调用getTimeZone并传递TimeZone对象。 Joda-Time有一个UTC的内置常量:DateTimeZone.UTC

DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd/MM/yyyy" ); // Usually I specify a Locale as well. But in this case, no need (no names of days or months).
DateTimeZone customerTimeZone = DateTimeZone.UTC;

String input = "25/05/2014";
DateTime customerDateTime = formatter.withZone( customerTimeZone ).parseDateTime( input );

DateTime customerDateTimeAtFive = customerDateTime.withHourOfDay( 5 );  // Using customerTimeZone.

不确定为什么您通过转换为LocalDateTime故意丢失时区信息。如果目标是在服务器上处理UTC中的日期时间值,则无需丢失时区。服务器端代码应使用显式分配给UTC时区的DateTime对象。您可以通过以下方式调整时区:

DateTime serverDateTime = customerDateTimeAtFive.withZone( DateTimeZone.UTC );

但无论如何,如果你坚持(与问题中的代码相同)......

DateTimeZone serverTimeZone = DateTimeZone.UTC;
LocalDateTime localDateTime = new LocalDateTime( customerDateTimeAtFive, serverTimeZone ); // I don't see the point of using LocalDateTime, but here goes anyways.

转储到控制台。

System.out.println( "customerTimeZone: " + customerTimeZone );
System.out.println( "input: " + input );
System.out.println( "customerDateTime: " + customerDateTime );
System.out.println( "customerDateTimeAtFive: " + customerDateTimeAtFive );
System.out.println( "serverDateTime: " + serverDateTime );
System.out.println( "serverTimeZone: " + serverTimeZone );
System.out.println( "localDateTime: " + localDateTime );

跑步时。

customerTimeZone: UTC
input: 25/05/2014
customerDateTime: 2014-05-25T00:00:00.000Z
customerDateTimeAtFive: 2014-05-25T05:00:00.000Z
serverDateTime: 2014-05-25T05:00:00.000Z
serverTimeZone: UTC
localDateTime: 2014-05-25T05:00:00.000