繁体   English   中英

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

[英]Date parsing exception when in the Brazilian DST period

进入巴西 DST 时间段时,时钟向前 1 小时。 2014 年,夏令时开始于 19/10,因此时间 19/10/2014 00:00:00 变为 19/10/2015 的 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 时区据称支持 DST 更改。 此问题的预期修复是什么? 在 DST 期间开始和结束时,我必须手动更改 jvm 时区吗? 目前,“修复”正在 DST 期间开始时将 jvm 时区更改为 GMT-2。

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

java.util.Calendar代表一个瞬间。 那一刻必须存在。 当本地时间值落入春季前向 DST 间隙时,这些值无法表示为真实的时间瞬间。 换句话说,在巴西正确配置的时钟永远不会在 19/10/2014 显示 00:45:00。 因此例外。 请参阅DST 标签 wiki以获取可视化表示。

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

一旦你把它作为LocalDateTime ,那么你就可以决定从那里去哪里。 如果时间无效(落入 spring-forward 转换的间隙)或不明确(由于回退转换),您可以检测这些场景并决定如何在您的应用程序中处理它们。

tl;博士

使用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类,多年前取代了现在遗留的可怕的日期时间类。

您的输入字符串缺少时区或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。 只有您可以决定这样的调整是否适合您的业务逻辑。

日期是来自用户输入还是存储的信息? 请注意,将 GMT-3 设置为 JVM 与“America/Sao_Paulo”不同。 我不相信格林威治标准时间遵守夏令时。 来回切换 JVM 设置看起来不是一个好的解决方案。 如果它只是存储的信息,您可以提前 1 小时或向后更新值,不确定这里的情况。 设置 GMT-3 时区是我看到的在美国/圣保罗时区结束日期无效的唯一解释。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM