将符合 ISO 8601 的字符串转换为 java.util.Date

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2201925/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:40:55  来源:igfitidea点击:

Converting ISO 8601-compliant String to java.util.Date

javadateiso8601

提问by Ice09

I am trying to convert an ISO 8601formatted String to a java.util.Date.

我正在尝试将ISO 8601格式的字符串转换为java.util.Date.

I found the pattern yyyy-MM-dd'T'HH:mm:ssZto be ISO8601-compliant if used with a Locale (compare sample).

yyyy-MM-dd'T'HH:mm:ssZ如果与区域设置(比较示例)一起使用,我发现该模式符合 ISO8601。

However, using the java.text.SimpleDateFormat, I cannot convert the correctly formatted String 2010-01-01T12:00:00+01:00. I have to convert it first to 2010-01-01T12:00:00+0100, without the colon.

但是,使用java.text.SimpleDateFormat,我无法转换格式正确的 String 2010-01-01T12:00:00+01:00。我必须先将它转换为2010-01-01T12:00:00+0100, 没有冒号。

So, the current solution is

所以,目前的解决方案是

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", "+00");
System.out.println(ISO8601DATEFORMAT.parse(date));

which obviously isn't that nice. Am I missing something or is there a better solution?

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



Answer

回答

Thanks to JuanZe's comment, I found the Joda-Timemagic, it is also described here.

感谢 JuanZe 的评论,我找到了Joda-Time魔法,这里也有描述

So, the solution is

所以,解决办法是

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

Or more simply, use the default parser via the constructor:

或者更简单地说,通过构造函数使用默认解析器:

DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;

To me, this is nice.

对我来说,这很好。

采纳答案by jarnbjo

Unfortunately, the time zone formats available to SimpleDateFormat(Java 6 and earlier) are not ISO 8601compliant. SimpleDateFormat understands time zone strings like "GMT+01:00" or "+0100", the latter according to RFC # 822.

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

Even if Java 7 added support for time zone descriptors according to ISO 8601, SimpleDateFormat is still not able to properly parse a complete date string, as it has no support for optional parts.

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

Reformatting your input string using regexp is certainly one possibility, but the replacement rules are not as simple as in your question:

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

  • Some time zones are not full hours off UTC, so the string does not necessarily end with ":00".
  • ISO8601 allows only the number of hours to be included in the time zone, so "+01" is equivalent to "+01:00"
  • ISO8601 allows the usage of "Z" to indicate UTC instead of "+00:00".
  • 某些时区不是UTC整整几个小时,因此字符串不一定以“:00”结尾。
  • ISO8601只允许时区包含小时数,所以“+01”相当于“+01:00”
  • ISO8601 允许使用“Z”来表示 UTC 而不是“+00:00”。

The easier solution is possibly to use the data type converter in JAXB, since JAXB must be able to parse ISO8601 date string according to the XML Schema specification. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z")will give you a Calendarobject and you can simply use getTime() on it, if you need a Dateobject.

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

You could probably use Joda-Timeas well, but I don't know why you should bother with that.

您也可以使用Joda-Time,但我不知道您为什么要为此烦恼。

回答by James Scriven

The DatatypeConverter solution doesn't work in all VMs. The following works for me:

DatatypeConverter 解决方案不适用于所有 VM。以下对我有用:

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

I've found that joda does not work out of the box (specifically for the example I gave above with the timezone on a date, which should be valid)

我发现 joda 不能开箱即用(特别是对于我上面给出的带有日期时区的示例,这应该是有效的)

回答by wrygiel

Okay, this question is already answered, but I'll drop my answer anyway. It might help someone.

好的,这个问题已经回答了,但无论如何我都会放弃我的回答。它可能会帮助某人。

I've been looking for a solution for Android(API 7).

我一直在寻找适用于 Android(API 7)的解决方案

  • Joda was out of the question - it is huge and suffers from slow initialization. It also seemed a major overkill for that particular purpose.
  • Answers involving javax.xmlwon't work on Android API 7.
  • Joda 是不可能的 - 它很大并且初始化缓慢。对于该特定目的,这似乎也是一个重大的矫枉过正。
  • 涉及的答案javax.xml不适用于 Android API 7。

Ended up implementing this simple class. It covers only the most common formof ISO 8601 strings, but this should be enough in some cases (when you're quite sure that the input will be in thisformat).

最终实现了这个简单的类。它涵盖最常见的 ISO 8601 字符串形式,但在某些情况下这应该足够了(当您非常确定输入将采用这种格式时)。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * 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;
    }
}

Performance note:I instantiate new SimpleDateFormat every time as means to avoid a bugin Android 2.1. If you're as astonished as I was, see this riddle. For other Java engines, you may cache the instance in a private static field (using ThreadLocal, to be thread safe).

性能说明:我每次都实例化新的 SimpleDateFormat 以避免Android 2.1 中的错误。如果你和我一样惊讶,请看这个谜语。对于其他 Java 引擎,您可以将实例缓存在私有静态字段中(使用 ThreadLocal,以确保线程安全)。

回答by Toby

I think we should use

我认为我们应该使用

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

for Date 2010-01-01T12:00:00Z

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

回答by Alexander Klimetschek

Apache Hymanrabbituses the ISO 8601 format for persisting dates, and there is a helper class to parse them:

Apache Hymanrabbit使用 ISO 8601 格式来持久化日期,并且有一个帮助类来解析它们:

org.apache.Hymanrabbit.util.ISO8601

org.apache.Hymanrabbit.util.ISO8601

Comes with Hymanrabbit-jcr-commons.

带有Hymanrabbit-jcr-commons

回答by AKh

Base Function Courtesy : @wrygiel.

基本功能礼貌:@wrygiel。

This function can convert ISO8601 format to Java Date which can handle the offset values. As per the definition of ISO 8601the offset can be mentioned in different formats.

此函数可以将 ISO8601 格式转换为可以处理偏移值的 Java 日期。根据ISO 8601定义,可以用不同的格式提及偏移量。

±[hh]:[mm]
±[hh][mm]
±[hh]

Eg:  "18:30Z", "22:30+04", "1130-0700", and "15:00-03:30" all mean the same time. - 06:30PM UTC

This class has static methods to convert

这个类有静态方法来转换

  • ISO8601 string to Date(Local TimeZone) object
  • Date to ISO8601 string
  • Daylight Saving is automatically calc
  • ISO8601 字符串到 Date(Local TimeZone) 对象
  • 日期到 ISO8601 字符串
  • 夏令时自动计算

Sample ISO8601 Strings

ISO8601 字符串示例

/*       "2013-06-25T14:00:00Z";
         "2013-06-25T140000Z";
         "2013-06-25T14:00:00+04";
         "2013-06-25T14:00:00+0400";
         "2013-06-25T140000+0400";
         "2013-06-25T14:00:00-04";
         "2013-06-25T14:00:00-0400";
         "2013-06-25T140000-0400";*/


public class ISO8601DateFormatter {

private static final DateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private static final DateFormat DATE_FORMAT_2 = new SimpleDateFormat("yyyy-MM-dd'T'HHmmssZ");
private static final String UTC_PLUS = "+";
private static final String UTC_MINUS = "-";

public static Date toDate(String iso8601string) throws ParseException {
    iso8601string = iso8601string.trim();
    if(iso8601string.toUpperCase().indexOf("Z")>0){
        iso8601string = iso8601string.toUpperCase().replace("Z", "+0000");
    }else if(((iso8601string.indexOf(UTC_PLUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_PLUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_PLUS), UTC_PLUS);
    }else if(((iso8601string.indexOf(UTC_MINUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_MINUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_MINUS), UTC_MINUS);
    }

    Date date = null;
    if(iso8601string.contains(":"))
        date = DATE_FORMAT_1.parse(iso8601string);
    else{
        date = DATE_FORMAT_2.parse(iso8601string);
    }
    return date;
}

public static String toISO8601String(Date date){
    return DATE_FORMAT_1.format(date);
}

private static String replaceColon(String sourceStr, int offsetIndex){
    if(sourceStr.substring(offsetIndex).contains(":"))
        return sourceStr.substring(0, offsetIndex) + sourceStr.substring(offsetIndex).replace(":", "");
    return sourceStr;
}

private static String appendZeros(String sourceStr, int offsetIndex, String offsetChar){
    if((sourceStr.length()-1)-sourceStr.indexOf(offsetChar,offsetIndex)<=2)
        return sourceStr + "00";
    return sourceStr;
}

}

}

回答by david_p

The Hymanson-databind libraryalso has ISO8601DateFormat classthat does that (actual implementation in ISO8601Utils.

Hyman逊-数据绑定库也有ISO8601DateFormat类,做的是(在实际执行ISO8601Utils

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

回答by Antonio

The way that is blessed by Java 7 documentation:

受 Java 7 文档祝福的方式:

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

You can find more examples in section Examplesat SimpleDateFormat javadoc.

你可以找到部分更多的例子例子SimpleDateFormat的Javadoc中

UPD 02/13/2020:There is a completely new wayto do this in Java 8

UPD 02/13/2020:在 Java 8 中有一种全新的方法可以做到这一点

回答by tmandry

Another very simple way to parse ISO8601 timestamps is to use org.apache.commons.lang.time.DateUtils:

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

回答by Eesha

SimpleDateFormat for JAVA 1.7 has a cool pattern for ISO 8601 format.

SimpleDateFormat for JAVA 1.7 有一个很酷的 ISO 8601 格式模式。

Class SimpleDateFormat

类 SimpleDateFormat

Here is what I did:

这是我所做的:

Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
         Locale.ENGLISH).format(System.currentTimeMillis());