8

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

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

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

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

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

正如前面关于支持这种“扩展”时区格式的特殊情况(始终使用“:”作为分隔符)的问题中所讨论的,将 Java 7 功能反向移植到 Java 6 的最佳方法是子类化SimpleDateformat该类并覆盖其parse()方法, IE:

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稍后正确设置几行所需的信息?

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

因此,准确性速度都至关重要(即,如果使用 2 次传球速度更快,则这种方法更可取)。

4

2 回答 2

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();
    }
}
于 2013-01-05T22:33:43.787 回答
3

您可以在 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,即新类的后向端口(对于 JSR 310,ThreeTen;请参阅底部的链接)。
  • 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。它被称为 ThreeTenABP。并确保从org.threeten.bp子包中导入日期和时间类。

正如您从代码中看到的那样,+02需要+0200一个格式化程序,您可以在其中指定偏移量的格式,而+02:00Z也)符合默认格式并且不需要指定。

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

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

    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]表示 format+02:00+0200可能+02存在。

链接

于 2018-08-25T13:29:47.387 回答