在巴西夏令时期间解析异常的日期

时间:2015-05-06 17:23:12

标签: java timezone dst

进入巴西DST时段时,时钟前进1小时。 2014年,夏令时开始于19/10,所以19/10/2014 00:00:00的时间在2015年10月19日01:00:00。 “不存在”之间的时期。

因此,在使用时区America / Sao_Paulo解析日期“19/10/2014 00:45:00”时,会抛出一个解析异常:java.text.ParseException:Unparseable date:“19/10 / 2014 00:45:00“。

String date = "19/10/2014 00:59:00";
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
sdf.setLenient(false);
sdf.setTimeZone("America/Sao_Paulo");

Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("America/Sao_Paulo"));
calendar.setTime(sdf.parse(date));

America / Sao_Paulo timezone据称支持DST更改。这个问题的预期修复是什么?在DST期间开始和结束时,我必须手动更改jvm时区吗?目前,“修复”在DST期间开始时将jvm时区更改为GMT-2。

注意:此问题源自使用spring开发的应用程序。示例日期在从String转换为java.util.Calendar时抛出异常。在上面的示例代码中,我将lenient设置为false以便能够重现错误。

3 个答案:

答案 0 :(得分:0)

java.util.Calendar代表即时。那一刻必须存在。当本地时间值落入春天向前的DST间隙时,这些值没有表示为真正的即时时间。换句话说,巴西正确配置的时钟将永远不会在2014年10月19日00:45:00显示。因此例外。有关直观表示,请参阅the DST tag wiki

由于您正在解析用户输入,我建议将字符串解析为LocalDateTime而不是Calendar。对于Java 7,您可以从Joda-Time获取此信息。对于Java 8,它内置于新的java.time包中。

将其作为LocalDateTime后,您可以决定从那里开始。如果时间无效(落入弹跳转换的间隙)或模糊(由于回退转换),您可以检测这些场景并决定如何在应用程序中处理它们。

答案 1 :(得分:0)

tl; dr

使用 java.time 将00:45调整为01:45,以解决DST转换。

LocalDateTime.parse( 
    "19/10/2014 00:45:00" , 
    DateTimeFormatter.ofPattern( "dd/MM/uuuu HH:mm:ss" )   // Returns a `DateTimeFormatter` object. 
)                                                          // Returns a `LocalDateTime` object.
.atZone( 
    ZoneId.of( "America/Sao_Paulo" )                       // Returns a `ZoneId` object.
)                                                          // Returns a `ZonedDateTime` object.
.toString()                                                // Returns a `String` object holding text in standard ISO 8601 format extended to append the name of the time zone in square brackets.

2014-10-19T01:45-02:00 [美国/圣保罗]

java.time

现代的方法使用了几年前的 java.time 类,该类取代了现在已经很旧的可怕的日期时间类。

您的输入字符串缺少时区或UTC偏移量的指示符。因此,解析为LocalDateTime

String input = "19/10/2014 00:45:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );

LocalDateTime只是带有日期的日期。因此,此类无法表示时刻,不是时间轴上的一点。要确定时刻,我们必须将LocalDateTime放在时区的上下文中,从而产生一个ZonedDateTime对象。

ZoneId z = ZoneId.of( "America/Sao_Paulo" );
ZonedDateTime zdt = ldt.atZone( z );

转储到控制台。

System.out.println( "ldt = " + ldt );
System.out.println( "zdt = " + zdt );

运行时。

ldt = 2014-10-19T00:45

zdt = 2014-10-19T01:45-02:00 [美国/圣保罗]

我们可以看到 java.time 进行了必要的调整。当天的00:45更改为01:45。

请务必了解 java.time 在此调整中使用的逻辑。学习Javadoc。只有您才能确定这种调整是否适合您的业务逻辑。

答案 2 :(得分:-1)

日期是源于用户输入还是存储信息?请注意,将GMT-3设置为JVM与" America / Sao_Paulo"不同。我不相信GMT会观察夏令时。来回切换JVM设置看起来不是一个好的解决方案。如果它只是存储的信息,您可以提前1小时向后更新值,不知道这是哪种情况。设置GMT-3时区是我在美国/ Sao_Paulo时区结束无效日期的唯一解释。