【问题标题】:Generic support for ISO 8601 format in Java 6Java 6 中对 ISO 8601 格式的通用支持
【发布时间】:2012-10-13 22:58:57
【问题描述】:

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

这种新格式是 Z(大写 Z)的超集,还有 2 个额外的变体:

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

因此,从Java 7 documentation of SimpleDateFormat 可以看出,以下 3 种格式现在是有效的(而不是在 Java 6 中 Z 仅涵盖第二种格式),当然,它们是等效的:

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

正如earlier question 中所讨论的,关于支持这种“扩展”时区格式的特殊情况,始终使用 ':' 作为分隔符,将 Java 7 功能反向移植到 Java 6 的最佳方法是将 @ 子类化987654334@ 类并覆盖其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");

在上述earlier question 中,建议使用正则表达式:(?=[0-9]{2}$) 去掉':',在similar question 中建议使用正则表达式(?<=[+-]\d{2})$ 将“分钟”字段附加为00 ,如果需要的话。

显然,成功运行 2 个替换可用于实现全部功能。因此,被覆盖的parse() 方法中的iso 局部变量将被设置为

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

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

在两者之间使用if 检查以确保稍后也正确设置pos 值,并且还用于之前的length() 比较。

问题是:我们是否可以使用单个正则表达式来达到相同的效果,包括不需要检查长度以及在几行之后正确设置pos所需的信息?

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

因此,accuracyspeed 都至关重要(即,如果使用 2 次通道更快,则首选此方法)。

【问题讨论】:

  • 你检查过JDK 7中的相应代码吗?
  • 还没有,因为我没有使用它,但可能这不会提供太大帮助,因为在 SimpleDateFormat 类中,模式在处理之前被编译成语法,因此与任何正则表达式都没有对应关系.谢谢,无论如何+1。 :-)
  • 你考虑过javax.xml.datatype.DatatypeFactory吗?它支持 8601 格式的日期字符串。见download.java.net/jdk7/archive/b123/docs/api/javax/xml/datatype/…
  • 你为什么不试试 Joda Time 让它返回一个 Date 对象?
  • 取代旧的日期时间类(DateCalendar 等)和 Joda-Time 的 java.time 类的使用要容易得多。大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7,并在 ThreeTenABP 中进一步适应 Android。

标签: java regex date simpledateformat java-6


【解决方案1】:

看来你可以用这个:

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();
    }
}

【讨论】:

    【解决方案2】:

    您可以在 Java 6 中使用现代 Java 日期和时间 API java.time。在我看来,这似乎是一个不错的解决方案,也是面向未来的解决方案。它对 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,即新类的后向端口(对于 JSR 310,ThreeTen;请参阅底部的链接)。
    • 在(较旧的)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 格式。

    链接

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      相关资源
      最近更新 更多