java - 转换Iso 8601-compliant String到java.util.Date

  显示原文与译文双语对照的内容

我试着将转换为 ISO 8601 格式化字符串到一个 java.util. 日期。

我发现模式"yyyy-MM-dd''hh: mm: ssz"是 ISO8601-compliant,如果与区域设置( 比较示例) 一起使用的话。 但是,使用 java.text. SimpleDateFormat,我无法转换格式正确的字符串"2010-01-01 t12: 00: 00+01: 00"。 我必须先把它转换成"2010-01-01 t12: 00: 00+0100",没有冒号。 所以,当前的解决方案是


SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.GERMANY);
String date ="2010-01-01T12:00:00+01:00".replaceAll("+0([0-9]){1}:00","+0$100");
System.out.println(ISO8601DATEFORMAT.parse(date));

这显然不是那么好。 我丢失了什么还是有更好的解决方案?

磅应答

多亏了juanze的注释,我找到了 Joda-Time 魔术,这里也描述了 那么,解决方案是


DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis();
String jtdate ="2010-01-01T12:00:00+01:00";
System.out.println(parser2.parseDateTime(jtdate));

对我来说,这很好。

时间:

不幸的是,时区格式可以用于 SimpleDateFormat ( Java 6和更早版本) 不 ISO 8601 合规。 ping可以理解时区字符串( 如"gmt+01: 00"或者" +0100"),后者根据 RFC # 822

即使 Java 7根据 ISO 8601添加了时区描述符支持,SimpleDateFormat仍然无法正确解析完整的日期字符串,因为它不支持可选的部件。

使用正则表达式重新格式化输入字符串当然是一种可能性,但替换规则不如你的问题简单:

  • 某些时区不是 UTC的完整小时数,因此字符串并不一定以":00"结尾。
  • ISO8601只允许包含在时区中的小时数,因此" +01"相当于"+01: 00"
  • ISO8601允许使用"z"来表示UTC而不是" +00: 00"。

更简单的解决方案可能是使用JAXB中的数据类型转换器,因为JAXB必须能够根据 XML Schema 规范解析ISO8601日期字符串。 javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") 将给你一个 Calendar 对象,你可以简单地使用 getTime(),如果你需要一个 Date 对象。

你可能也可以使用 Joda-Time编辑器,但我不知道为什么要使用它。

好了,这个问题已经回答了,但我还是要放弃我的回答。 它可能会帮助某人。

我一直在寻找一个解决方案为机器人的 ( API 7 ) 。

  • Joda超出了问题- 它是巨大的,并且初始化缓慢。 对于那个特定的目的,它似乎是一个重大的overkill 。
  • 涉及 javax.xml的答案在 Android API 7上不起作用。

实现了这个简单的类。 在某些情况下,字符串的ISO 8601它只涵盖了 最常见的形式,但是这就够用了


/**
 * Helper class for handling a most common subset of ISO 8601 strings
 * (in the following format:"2008-03-01T13:00:00+01:00"). It supports
 * parsing the"Z" timezone, but many other less-used features are
 * missing.
 */
public final class ISO8601 {
/** Transform Calendar to ISO 8601 string. */
 public static String fromCalendar(final Calendar calendar) {
 Date date = calendar.getTime();
 String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
. format(date);
 return formatted.substring(0, 22) +":" + formatted.substring(22);
 }

/** Get current date and time formatted as ISO 8601 string. */
 public static String now() {
 return fromCalendar(GregorianCalendar.getInstance());
 }

/** Transform ISO 8601 string to Calendar. */
 public static Calendar toCalendar(final String iso8601string)
 throws ParseException {
 Calendar calendar = GregorianCalendar.getInstance();
 String s = iso8601string.replace("Z","+00:00");
 try {
 s = s.substring(0, 22) + s.substring(23);//to get rid of the":"
 } catch (IndexOutOfBoundsException e) {
 throw new ParseException("Invalid length", 0);
 }
 Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(s);
 calendar.setTime(date);
 return calendar;
 }
}

在安卓性能说明: 我每次都新的SimpleDateFormat实例化为手段,以避免一个 Bug. 如果你像我一样惊讶,请看这个谜语。 对于其他的Java引擎,你可以在私有静态字段( 使用ThreadLocal作为线程安全) 中缓存该实例。

7文档 。xml所支持的方式:


DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
String string1 ="2001-07-04T12:08:56.235-0700";
Date result1 = df1.parse(string1);

DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String string2 ="2001-07-04T12:08:56.235-07:00";
Date result2 = df2.parse(string2);

SimpleDateFormat java doc, 节. 示例中你可以找到更多示例

ISO8601Utils, 了 Jackson-databind库还具有 ISO8601DateFormat类来办到( 实际进行了较详细的讨论


ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");

DatatypeConverter解决方案在所有vm中都不起作用。 以下是我的工作:


javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()

我发现joda不能在盒子中工作( 特别是我在上面给出的那个日期的例子,应该是有效的)

对于Java版本 7

你可以遵循Oracle文档: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X - 用于 ISO 8601时区


TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());

System.out.println(nowAsISO);

DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
//nowAsISO ="2013-05-31T00:00:00Z";
Date finalResult = df1.parse(nowAsISO);

System.out.println(finalResult);

我觉得我们应该用


DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")

日期 2010-01-01T12:00:00Z

另一种分析ISO8601时间戳的非常简单方法是使用 org.apache.commons.lang.time.DateUtils :


import static org.junit.Assert.assertEquals;

import java.text.ParseException;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
import org.junit.Test;

public class ISO8601TimestampFormatTest {
 @Test
 public void parse() throws ParseException {
 Date date = DateUtils.parseDate("2010-01-01T12:00:00+01:00", new String[]{"yyyy-MM-dd'T'HH:mm:ssZZ" });
 assertEquals("Fri Jan 01 12:00:00 CET 2010", date.toString());
 }
}

Joda-Time

嵌入到问题中的答案,使用 Joda-Time 2.3,是正确的但不必要的冗长和复杂。

日期时间类构造函数采用各种 ISO 8601格式的字符串参数。 所以不需要调用解析方法。 中唯一的代码行示范,你可以做。


java.util.Date date = new DateTime("2010-01-01T12:00:00+01:00Z" ).toDate();

示例代码

问题无法处理时区。 下面是不同时区可能性变化的示例代码。


//© 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
//import org.joda.time.*;

//Default time zone.
DateTime dateTimeInMyDefaultTimeZone = new DateTime("2010-01-01T12:00:00+01:00" );

//UTC
DateTime dateTimeInUtc = new DateTime("2010-01-01T12:00:00+01:00", DateTimeZone.UTC );

//Named time zone
//Time Zone list… http://joda-time.sourceforge.net/timezones.html
DateTimeZone kolkataTimeZone = DateTimeZone.forID("Asia/Kolkata" );
DateTime dateTimeInKolkata = new DateTime("2010-01-01T12:00:00+01:00", kolkataTimeZone );

//Hard-coded to that one-hour offset. Using a named time zone would be better, to handle Daylight Saving Time (DST) or other anomalies.
DateTimeZone timeZoneOffsetOneHour = DateTimeZone.forOffsetHours( 1 );
DateTime dateTimeInOneHourOffset = new DateTime("2010-01-01T12:00:00+01:00", timeZoneOffsetOneHour );

//Using a named time zone to handle Daylight Saving Time (DST) or other anomalies.
//Arbitrarily picking Algiers as a one-hour offset.
DateTimeZone timeZoneAlgiers = DateTimeZone.forID("Africa/Algiers" );
DateTime dateTimeAlgiers = new DateTime("2010-01-01T12:00:00+01:00", timeZoneAlgiers );

转到控制台。。


System.out.println("dateTimeInMyDefaultTimeZone:" + dateTimeInMyDefaultTimeZone );
System.out.println("dateTimeInUtc:" + dateTimeInUtc );
System.out.println("dateTimeInKolkata:" + dateTimeInKolkata );
System.out.println("dateTimeInOneHourOffset:" + dateTimeInOneHourOffset );
System.out.println("dateTimeAlgiers:" + dateTimeAlgiers );

运行时。。


dateTimeInMyDefaultTimeZone: 2010-01-01T03:00:00.000-08:00
dateTimeInUtc: 2010-01-01T11:00:00.000Z
dateTimeInKolkata: 2010-01-01T16:30:00.000+05:30
dateTimeInOneHourOffset: 2010-01-01T12:00:00.000+01:00
dateTimeAlgiers: 2010-01-01T12:00:00.000+01:00

java.time

在Java中 8和更高版本的设计灵感来自新 java.time 包装中 虽然相似,但每个都有其他缺乏的特性。 在可能的情况下最好使用 java.time 。 缺少,使用 Joda-Time 。


 ZonedDateTime zonedDateTime = ZonedDateTime.parse ("2010-01-01T12:00:00+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME );

在 zonedDateTime generates,调用


2010-01-01T12:00+01:00

根据需要调整时区。


ZonedDateTime zonedDateTimeMontréal = zonedDateTime.withZoneSameInstant( ZoneId.of("America/Montreal" ) );

Apache Jackrabbit 使用 ISO 8601格式来保存日期,并且有一个 helper 类来解析它们:

org.apache.jackrabbit.util.ISO8601

带有 jackrabbit-jcr-commons

...