首页 > 解决方案 > 以任何符合 ISO-8601 的格式解析字符串的统一方法

问题描述

我需要一个统一的方法来解析任何符合 ISO-8601 的字符串。不幸的是,Joda 和 fastxml StdDateFormat 都没有资格这样做。

我想解释以下两个属性

start是符合 ISO-8601 的字符串,可以是任何符合标准的格式,例如“2020-05-15T20:30:52+03:00”或 2020-05-15”。timezoneIANA 时区,例如“Europe/Moscow”。

期望的逻辑是:

  1. 如果timezone未指定,则扩展start到完全限定的 UTC,而不管它包含什么时区信息。例如,将“2020-05-15T20:30”扩展为“2020-05-15T20:30:00.000Z”。

  2. 如果timezone指定,且时区信息与start冲突timezone,则报错。例如timezone=“亚洲/上海”和start=“2020-05-15T20:30:52+03:00”。

  3. 如果timezone已指定,并且与 中的时区信息没有冲突start,则扩展start为完全限定的 ISO-8601 格式。例如timezone= "Europe/Moscow", start= "2020-05-15T20:30+03:00",然后扩展start到 "2020-05-15T20:30:00.000+03:00"。

  4. 如果timezone已指定且start没有时区信息,则添加并将timezonestart扩展为完全限定的 ISO-8601 格式。例如timezone= "Europe/Moscow" 和start= "2020-05-15",然后扩展start为 "2020-05-15T00:00:00.000+03:00"。

此代码片段侧重于解析符合 ISO-8601 的字符串。尚未涉及任何timezone参数。它尝试使用Joda DateTimeand fasterxml StdDateFormat


import java.util.Date;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import com.fasterxml.jackson.databind.util.StdDateFormat;

public class ISOTest2 {

    public void test() {

        String [] inputs = new String [] {
                "2019-06-16T09:30:20+06:00",
                "2019-06-16T09:30Z",
                "2019-06-16T09:30:20",
                "2019-06-16T09:30",
                "2019-06-16T",
                "2019-06-16",
                "20190616T09:30:20+06:00",
                "20190618T110044Z"
        };

        StdDateFormat df = new StdDateFormat();

        for (String input : inputs) {

            System.out.println();

            DateTime output = null;

            boolean isStdDateFormatFail = false;

            try {
                Date date = df.parse(input);
                output = new DateTime(date);
                System.out.println("Valid for StdDateFormat:\t\t" + input);
                print(input, output);
            } catch (Exception e) {
                isStdDateFormatFail = true;
                System.out.println("=============");
                System.out.println("Invalid for StdDateFormat:\t\t" + input);
            }

            if (isStdDateFormatFail) {
                try {
                    output = DateTime.parse(input);
                    System.out.println("Valid for Joda:\t\t" + input);
                    print(input, output);
                } catch (Exception e) {
                    System.out.println("=============");
                    System.out.println("Invalid for Joda:\t\t" + input);
                }
            }
        }
    }

    private void print(String input, DateTime output) {

        System.out.println("=============");
        System.out.println("Input:\t\t" + input);
        System.out.println("Output:\t\t" + output);

        DateTimeZone zone = output.getZone();
        System.out.println("Zone:\t\t" + zone);
    }

    public static void main(String[] args) {

        ISOTest2 test = new ISOTest2();
        test.test();
    }
}

注意我的本地时区是“亚洲/上海”,即+08:00

预期结果

=============
Input:      2019-06-16T09:30:20+06:00
Output:     2019-06-16T09:30:20.000+06:00
Zone:       +0600

=============
Input:      2019-06-16T09:30Z
Output:     2019-06-16T09:30:00.000Z
Zone:       UTC

=============
Input:      2019-06-16T09:30:20
Output:     2019-06-16T09:30:20.000Z
Zone:       UTC

=============
Input:      2019-06-16T09:30
Output:     2019-06-16T09:30:00.000Z
Zone:       UTC

=============
Input:      2019-06-16T
Output:     2019-06-16T00:00:00.000Z
Zone:       UTC

=============
Input:      2019-06-16
Output:     2019-06-16T00:00:00.000Z
Zone:       UTC

=============
Input:      20190616T09:30:20+06:00
Output:     2019-06-16T09:30:20.000+06:00
Zone:       +0600

=============
Input:      20190618T110044Z
Output:     2019-06-18T11:00:44.000Z
Zone:       UTC

实际结果


Valid for StdDateFormat:        2019-06-16T09:30:20+06:00
=============
Input:      2019-06-16T09:30:20+06:00
Output:     2019-06-16T11:30:20.000+08:00
Zone:       Asia/Shanghai

Valid for StdDateFormat:        2019-06-16T09:30Z
=============
Input:      2019-06-16T09:30Z
Output:     2019-06-16T17:30:00.000+08:00
Zone:       Asia/Shanghai

Valid for StdDateFormat:        2019-06-16T09:30:20
=============
Input:      2019-06-16T09:30:20
Output:     2019-06-16T17:30:20.000+08:00
Zone:       Asia/Shanghai

Valid for StdDateFormat:        2019-06-16T09:30
=============
Input:      2019-06-16T09:30
Output:     2019-06-16T17:30:00.000+08:00
Zone:       Asia/Shanghai

=============
Invalid for StdDateFormat:      2019-06-16T
Valid for Joda:     2019-06-16T
=============
Input:      2019-06-16T
Output:     2019-06-16T00:00:00.000+08:00
Zone:       Asia/Shanghai

Valid for StdDateFormat:        2019-06-16
=============
Input:      2019-06-16
Output:     2019-06-16T08:00:00.000+08:00
Zone:       Asia/Shanghai

=============
Invalid for StdDateFormat:      20190616T09:30:20+06:00
Valid for Joda:     20190616T09:30:20+06:00
=============
Input:      20190616T09:30:20+06:00
Output:     20190616-01-01T09:30:20.000+06:00
Zone:       +06:00

=============
Invalid for StdDateFormat:      20190618T110044Z
=============
Invalid for Joda:       20190618T110044Z

阻塞问题

存在三个阻塞问题。

  1. fasterxml StdDateFormat将更original timezone改为local timezone隐式。例如,输入字符串为“2019-06-16T09:30:20+06:00”,输出日期为“2019-06-16T11:30:20.000+08:00”。

  2. 当 中不包含时区信息时start,两者都隐含Jodafasterxml添加本地时区。例如,输入字符串为“2019-06-16T09:30”,输出日期为“2019-06-16T17:30:00.000+08:00”。(我当地的时区是“亚洲/上海”,例如 +08:00) 我期待的是一个添加timezone它的机会。例如,另一个参数指定为“Asia/Shanghai”,start=“2019-06-16T09:30”,所需的输出类似于:“2019-06-16T09:30”+“Asia/Shanghai”=“ 2019-06-16T09:30:00.000+08:00”。

  3. fasterxml无法解析“20190616T09:30:20+06:00”。Joda 和 fastermxl 都无法解析“20190618T110044Z”。

标签: javadatetimetimezonejodatimefasterxml

解决方案


推荐阅读