时区有冒号时如何解决解析异常?

问题描述

我从网络服务接收 DateTime 作为 String。此 DateTime 字符串的示例是:"DateTime":"2021-06-06T04:54:41-04:00"

这个 2021-06-06T04:54:41-04:00 或多或少符合 ISO-8601 格式,所以我使用这个模式来解析它:yyyy-MM-dd'T'HH:mm:ssZ。但是,响应 DateTime 的时区部分中的冒号导致了问题。 2021-06-06T04:54:41-04:00 给出解析异常,但 2021-06-06T04:54:41-0400 解析正常。

下面的代码应该能更好地解释它:

public void stringToDate() {

        String pattern = "yyyy-MM-dd'T'HH:mm:ssZ";  //ISO - 8601 Format
        TimeZone timeZoneEST = TimeZone.getTimeZone("US/Eastern");
        SimpleDateFormat sdf = new SimpleDateFormat(pattern,new Locale("en","US"));
        sdf.setLenient(false);
        sdf.setTimeZone(timeZoneEST);
        
        String timeFromWebService = "2021-06-06T04:54:41-04:00";
        try {
            Date parsedDate = sdf.parse(timeFromWebService); // not working because of colon in timezone part
            System.out.println(parsedDate);
        } catch (ParseException e) {
            e.printstacktrace();
        }
        
        try {
            Thread.sleep(1000);  //sleep to avoid interleaving output from stacktrace (above) and syso (below)
        } catch (InterruptedException e1) {
            // Todo Auto-generated catch block
            e1.printstacktrace();
        }
        
        String timeFromWebServiceModified = "2021-06-06T04:54:41-0400";  //removed colon from timezone part
        try {
            Date parsedDate = sdf.parse(timeFromWebServiceModified); // working because colon is removed in timezone part
            System.out.println(parsedDate);
        } catch (ParseException e) {
            e.printstacktrace();
        }
        
    }

我想在不修改响应 DateTime 的情况下处理这个解析。关于如何解析原始 DateTime 的任何建议。任何关于使用什么模式的建议都会非常有帮助。

解决方法

java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*

使用现代 API java.time 的解决方案:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d'T'H:m:s[XXX][XX][X]",Locale.ENGLISH);
        
        //Test
        Stream.of(
                    "2021-06-06T04:54:41-04:00","2021-06-06T04:54:41-0400","2021-06-06T04:54:41-04","2021-06-06T04:54:41Z"                  
        ).forEach(s -> System.out.println(OffsetDateTime.parse(s,dtf)));
    }
}

输出:

2021-06-06T04:54:41-04:00
2021-06-06T04:54:41-04:00
2021-06-06T04:54:41-04:00
2021-06-06T04:54:41Z

ONLINE DEMO

检查How to use OffsetDateTime in JDBC?

modern Date-Time API 中详细了解 java.timeTrail: Date Time*

使用旧 API 的解决方案:

SimpleDateFormat 没有指定可选模式的功能,我们这样做的方式是使用方括号和 DateTimeFormatter。在这种情况下,您可以创建多个 SimpleDateFormat 实例并尝试使用每个实例。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        SimpleDateFormat sfdArr[] = {
                            new SimpleDateFormat("y-M-d'T'H:m:sXXX",Locale.ENGLISH),new SimpleDateFormat("y-M-d'T'H:m:sXX",new SimpleDateFormat("y-M-d'T'H:m:sX",Locale.ENGLISH)
        };
        
        String []strDateTimeArr = {
                    "2021-06-06T04:54:41-04:00","2021-06-06T04:54:41Z"                  
        };
        
        for(String s : strDateTimeArr) {
            Date date = null;
            for(SimpleDateFormat sdf : sfdArr) {
                try {
                    date = sdf.parse(s);
                }catch(ParseException e) {
                    //...
                }
            }
            System.out.println(date);
        }
    }
}

ONLINE DEMO


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project