Java 6 中对 ISO 8601 格式的通用支持

问题描述 投票:0回答:3

Java 7 通过字符

SimpleDateFormat
(而不是小写或大写
X
)在
Z
类中引入了对 ISO 8601 格式的支持。在 Java 6 中支持此类格式需要预处理,因此最好的方法就是问题。

这种新格式是

Z
(大写 Z)的超集,还有 2 个附加变体:

  1. “分钟”字段是可选的(即,2 位数字而不是 4 位数字时区有效)
  2. 冒号字符(':')可用于分隔 2 位“小时”字段和 2 位“分钟”字段。

因此,正如人们可以从 SimpleDateFormat

Java 7 文档中观察到的那样,以下 3 种格式现在是有效的(而不是 Java 6 中 
Z
只涵盖的第二种格式),当然,它们是等效的:

  1. -08
  2. -0800
  3. -08:00

正如在之前的问题中讨论的,关于支持这种“扩展”时区格式的特殊情况,始终以“:”作为分隔符,将 Java 7 功能向后移植到 Java 6 的最佳方法是将

SimpleDateformat 子类化
类并重写其
parse()
方法,即:

public Date parse(String date, ParsePosition pos)
{
    String iso = ... // Replace the X with a Z timezone string, using a regex

    if (iso.length() == date.length())
    {
        return null; // Not an ISO 8601 date
    }

    Date parsed = super.parse(iso, pos);

    if (parsed != null)
    {
        pos.setIndex(pos.getIndex()+1); // Adjust for ':'
    }

    return parsed;
}

请注意,上面的子类

SimpleDateFormat
对象必须使用相应的基于
Z
的模式进行初始化,即,如果子类是
ExtendedSimpleDateformat
并且您想要解析符合模式
yyyy-MM-dd'T'HH:mm:ssX
的日期,那么您应该使用对象实例化为

new ExtendedSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

在上述之前的问题中,建议使用正则表达式

:(?=[0-9]{2}$)
来删除“:”,在类似的问题中,建议使用正则表达式
(?<=[+-]\d{2})$
将“分钟”字段附加为
00
,如果需要的话。

显然,成功运行 2 个替换即可实现完整功能。因此,重写的

iso
方法中的
parse()
局部变量将被设置为

iso = date.replaceFirst(":(?=[0-9]{2}$)","");

iso = iso.replaceFirst("(?<=[+-]\\d{2})$", "00");

在中间进行

if
检查,以确保稍后也正确设置
pos
值,并用于之前的
length()
比较。

问题是:我们是否可以使用单个正则表达式来达到相同的效果,包括不需要不必要地检查长度以及稍后正确设置

pos
所需的信息?

该实现旨在用于读取大量可以采用任何格式(甚至完全非日期)的字符串字段的代码,仅选择符合格式的字段并返回已解析的 Java

Date
对象。

因此,准确度速度都至关重要(即,如果使用 2 次传递速度更快,则最好采用这种方法)。

java regex date simpledateformat java-6
3个回答
6
投票

看来你可以用这个:

import java.util.Calendar;
import javax.xml.bind.DatatypeConverter;

public class TestISO8601 {
    public static void main(String[] args) {
        parse("2012-10-01T19:30:00+02:00"); // UTC+2
        parse("2012-10-01T19:30:00Z");      // UTC
        parse("2012-10-01T19:30:00");       // Local
    }
    public static Date parse(final String str) {
        Calendar c = DatatypeConverter.parseDateTime(str);
        System.out.println(str + "\t" + (c.getTime().getTime()/1000));
        return c.getTime();
    }
}

5
投票

您可以在 Java 6 中使用 java.time,即现代 Java 日期和时间 API。在我看来,这是一个很好且面向未来的解决方案。它对 ISO 8601 有很好的支持。

import org.threeten.bp.OffsetDateTime;
import org.threeten.bp.format.DateTimeFormatter;

public class DemoIso8601Offsets {
    public static void main(String[] args) {
        System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+0200", 
                DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssXX")));
        System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02", 
                DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX")));
        System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02:00"));
        System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00Z"));
    }
}

该程序的输出是:

2012-10-01T19:30+02:00
2012-10-01T19:30+02:00
2012-10-01T19:30+02:00
2012-10-01T19:30Z

它要求您将 ThreeTen Backport 库添加到您的项目设置中。

  • 在 Java 8 及更高版本以及较新的 Android 设备(从 API 级别 26 开始)中,现代 API 是内置的。
  • 在 Java 6 和 7 中获取 ThreeTen Backport,即新类的向后移植(ThreeTen for JSR 310;请参阅底部的链接)。
  • 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。它被称为 ThreeTenABP。并确保使用子包从
    org.threeten.bp
    导入日期和时间类。

从代码中可以看出,

+02
+0200
需要一个格式化程序,您可以在其中指定偏移量的格式,而
+02:00
(以及
Z
)符合默认格式,不需要请指定。

我们可以使用相同的格式化程序解析所有偏移格式吗?

读取混合数据时,您不想专门处理每种偏移格式。最好在格式模式字符串中使用可选部分:

    DateTimeFormatter allInOne 
            = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[XXX][XX][X]");
    System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+0200", allInOne));
    System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02", allInOne));
    System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02:00", allInOne));
    System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00Z", allInOne));

输出与上面相同。

[XXX][XX][X]
中的方括号表示可能存在格式
+02:00
+0200
+02

链接


0
投票

相同的方法适用于不同的毫秒和不同的偏移量:

String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S][XXX][XX][X]";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);

Date convertDate(String dateString) {
    return Date.from(OffsetDateTime.parse(dateString, formatter).toInstant());
}
© www.soinside.com 2019 - 2024. All rights reserved.