Joda-time踩坑记---未带时区的字符串日期

2,037 阅读2分钟

背景

校验形如"2019-12-12"这样的字符串日期是否有效 使用joda-time及其版本

<dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.9.9</version>
</dependency>

代码如下:

public boolean validDateFormat(String dateStr) {
    DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd");
    boolean flag = true;
    try {
        df.parseDateTime(dateStr);
    } catch (Exception e) {
        LOGGER.error("validDateFormat error, dateStr={}", dateStr, e);
        flag = false;
    }
    return flag;
  }

问题

通过验证2019-12-12、2018-01-24返回期待的true;验证2019-02-34返回期待的false。但是当验证1989-04-16时,并未返回期待的true,而是返回了false。异常日志为:

org.joda.time.IllegalInstantException: Cannot parse "1989-04-16": Illegal instant due to time zone offset transition (Asia/Shanghai)
	at org.joda.time.format.DateTimeParserBucket.computeMillis(DateTimeParserBucket.java:471) ~[joda-time-2.9.9.jar:2.9.9]
	at org.joda.time.format.DateTimeParserBucket.computeMillis(DateTimeParserBucket.java:411) ~[joda-time-2.9.9.jar:2.9.9]
	at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:928) ~[joda-time-2.9.9.jar:2.9.9]

经过验证1900-2500年所有时间之后,发现并不是所有时间都会报错,只有1940-06-03、1941-03-16、1986-05-04、1987-04-12、1988-04-10、1989-04-16、1990-04-15、1991-04-14几个时间会有如上错误抛出。(此处数据参考:胡扬封的joda-time踩坑记

原因

参考官网文档FAQ:FAQ

由上图可以知道:如果字符串类型的日期没有时区时,需要使用parseLocalDateTime()。

解决方案

使用最新版本

<!-- joda-time -->
<dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.10.5</version>
</dependency>

使用parseLocalDateTime()

public boolean validDateFormat(String dateStr) {
    DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd");
    boolean flag = true;
    try {
        // 使用parseLocalDateTime
        df.parseLocalDateTime(dateStr);
    } catch (Exception e) {
        LOGGER.error("validDateFormat error, dateStr={}", dateStr, e);
        flag = false;
    }
    return flag;
  }

使用时区

public boolean validDateFormat(String dateStr) {
    // 使用UTC时区
    DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd").withZoneUTC();
    boolean flag = true;
    try {
        df.parseLocalDateTime(dateStr);
    } catch (Exception e) {
        LOGGER.error("validDateFormat error, dateStr={}", dateStr, e);
        flag = false;
    }
    return flag;
  }