![](/img/trans.png)
[英]How can I prevent Joda Time throwing an exception during Brazilian DST transition period
[英]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 转换的间隙)或不明确(由于回退转换),您可以检测这些场景并决定如何在您的应用程序中处理它们。
使用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.