Java 7 通过字符
SimpleDateFormat
(而不是小写或大写 X
)在 Z
类中引入了对 ISO 8601 格式的支持。在 Java 6 中支持此类格式需要预处理,因此最好的方法就是问题。
这种新格式是
Z
(大写 Z)的超集,还有 2 个附加变体:
因此,正如人们可以从 SimpleDateFormat
Z
只涵盖的第二种格式),当然,它们是等效的:
正如在之前的问题中讨论的,关于支持这种“扩展”时区格式的特殊情况,始终以“:”作为分隔符,将 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 次传递速度更快,则最好采用这种方法)。
看来你可以用这个:
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();
}
}
您可以在 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 库添加到您的项目设置中。
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
。
java.time
。java.time
。java.time
向后移植到 Java 6 和 7(ThreeTen for JSR-310)。相同的方法适用于不同的毫秒和不同的偏移量:
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());
}