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

Ice*_*e09 627 java date iso8601

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

yyyy-MM-dd'T'HH:mm:ssZ如果与Locale(比较样本)一起使用,我发现该模式符合ISO8601标准.

但是,使用java.text.SimpleDateFormat,我无法转换格式正确的String 2010-01-01T12:00:00+01:00.我必须先将它转换为2010-01-01T12: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));
Run Code Online (Sandbox Code Playgroud)

这显然不是那么好.我错过了什么或者有更好的解决方案吗?


回答

感谢JuanZe的评论,我发现了Joda-Time魔术,这里也有描述.

所以,解决方案是

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

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

DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;
Run Code Online (Sandbox Code Playgroud)

对我来说,这很好.

jar*_*bjo 451

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

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

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

  • 某些时区不是完整的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,但我不知道为什么你应该为此烦恼.

  • 这是相反的:Calendar c = GregorianCalendar.getInstance(); c.setTime(aDate); return javax.xml.bind.DatatypeConverter.printDateTime(c); (35认同)
  • JAXB解决方案是一种非常有创意的方法!它也可以,我用我的样本测试过它.但是,对于遇到问题且被允许使用JodaTime的人,我建议使用它,因为它感觉更自然.但是您的解决方案不需要额外的库(至少使用Java 6). (16认同)
  • -1我在使用这种方法和时区时遇到了麻烦 (7认同)
  • 实际上,这不是那么简单b/c你必须初始化jaxb datatypeConverter.我自己最终使用DatatypeFactory,因为DataTypeConverterImpl是在内部完成的.太头疼了. (4认同)
  • @jarnbjo你是我遇到的第一个也是唯一一个喜欢标准的1.8之前的Java日期类的人,而不是joda-time.我发现joda-time是一种字面上的快乐,特别是与标准的api相比,这是一种可憎的行为. (4认同)
  • @Simon:不,时区当然不会被忽视.你一定做错了.如果你输入多个字符并告诉我们你在做什么,有人可能会解释你是什么. (3认同)
  • 我们提出的解决方案存在问题.ISO8601允许+0000作为时区[1],XML模式定义不[2],它需要冒号(+00:00).[1] https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators [2] https://www.w3.org/TR/xmlschema-2/#dateTime-timezones (3认同)

Ant*_*nio 220

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);
Run Code Online (Sandbox Code Playgroud)

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

  • 'Z'需要引号 (13认同)
  • @ b.long Java为这种符合ISO 8601标准的格式添加了不止一个常量.Java为日期时间工作提供了一个全新的框架,其中包括对此类格式的内置默认支持.在Java 8中查看新的[`java.time`框架](http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html),灵感来自[Joda-Time ](http://www.joda.org/joda-time/),取代麻烦的java.util.Date,.Calendar和SimpleDateFormat类. (8认同)
  • 您的回答帮助我将MongoDB的ISODate转换为本地日期.问候. (6认同)
  • @kervin如果Z在引号中,格式化程序不会专门查找字符Z,而不是它可以表示的所有偏移字符串吗?如果您的日期字符串恰好是UTC格式,那么引用Z似乎只会巧合. (6认同)
  • 这是不是意味着您需要提前知道日期格式?如果你必须接受`string1`和`string2`但不知道你会得到什么呢? (2认同)

wry*_*iel 200

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

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

  • Joda是不可能的 - 它是巨大的,并且缓慢初始化.对于这一特定目的而言,这似乎也是一种重大的过度杀伤力
  • 涉及的答案javax.xml将无法在Android API 7上运行.

结束了实现这个简单的类.它涵盖最常见的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我不得不在几秒钟内添加.SSS但是效果很好.你为什么要做s = s.substring(0,22)+ s.substring(23);` - 我没有看到这一点 (6认同)
  • 这是我在寻找答案时遇到的第一页,所以看起来很合适.对于大多数Java开发人员来说,Android并不完全是Java.但是,在大多数情况下,其中一个与另一个相同,因此许多Android开发人员在搜索时会搜索"java". (5认同)
  • 也许这应该是自己的问题,有自己的答案吗? (2认同)

Ada*_*dam 100

java.time

java.time API(Java中内嵌的8和更高版本),使这是一个更容易一些.

如果您知道输入是UTC格式的,例如Z末尾的(对于Zulu),则Instant类可以解析.

java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" ));
Run Code Online (Sandbox Code Playgroud)

如果您的输入可能是另一个与UTC相关的偏移值而不是最后由(Zulu)指示的UTCZ,请使用OffsetDateTime该类进行解析.

OffsetDateTime odt = OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" );
Run Code Online (Sandbox Code Playgroud)

然后提取一个Instant,并java.util.Date通过调用转换为a from.

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
Run Code Online (Sandbox Code Playgroud)

  • 这个答案太难了.根据定义,java.util.Date没有时区.所以不需要所有与时区相关的代码:`LocalDateTime`和`ZoneId`和`atZone`.这个简单的单行代码将执行:`java.util.Date date = Date.from(ZonedDateTime.parse("2014-12-12T10:39:40Z").toInstant());` (7认同)
  • @BasilBourque这是不必要的复杂:`Date.from(Instant.parse("2014-12-12T10:39:40Z"));`就够了. (3认同)
  • @assylias您是正确的,但仅在日期字符串为UTC时才有效,ISO8601允许任何时区... (2认同)
  • @Adam我很糟糕 - 我没有意识到问题比你的例子更普遍.作为旁注,`OffsetDateTime`足以解析ISO8601(它不包含时区信息但只包含偏移量). (2认同)
  • @assylias 感谢您对让 `Instant` 进行解析的评论。虽然对于这个特定问题还不够,但这是一个值得指出的重要区别。所以我添加了第二个代码示例。哎呀,刚刚注意到这最初不是我的答案;我希望亚当批准。 (2认同)

dav*_*d_p 67

杰克逊-数据绑定库也有ISO8601DateFormat类是确实在(实际执行ISO8601Utils.

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

  • 该类现已弃用,新类是StdDateFormat.否则它的工作方式相同. (3认同)
  • 引用文档,解析格式为:`[yyyy-MM-dd | yyyyMMdd] [T(hh:mm [:ss [.sss]] | hhmm [ss [.sss]])]?[Z | [ + - ]为hh:mm]]`.换句话说,毫秒是可选的,但时区是强制性的. (2认同)
  • 啊,实际上看起来你是对的.不过,我很确定ISO8601允许你省略时区,所以它仍然是错误的.JodaTime虽然有效:`new DateTime("2015-08-11T13:10:00").toDate()` (2认同)

Bas*_*que 47

TL;博士

OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )
Run Code Online (Sandbox Code Playgroud)

使用java.time

Java 8及更高版本中的新java.time包受Joda-Time的启发.

OffsetDateTime级代表以时间轴上的时刻偏移从-UTC而不是一个时区.

OffsetDateTime odt = OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" );
Run Code Online (Sandbox Code Playgroud)

调用toString生成标准ISO 8601格式的字符串:

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

看穿UTC的透镜相同的值,提取Instant或调整从偏移+01:0000:00.

Instant instant = odt.toInstant();  
Run Code Online (Sandbox Code Playgroud)

…要么…

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );
Run Code Online (Sandbox Code Playgroud)

如果需要,调整到时区.一个时区是历史的偏移,从-UTC值的区域,拥有一套用于处理异常,如夏令时(DST)规则.因此,应尽可能应用时区而不是仅仅偏移.

ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of( "America/Montreal" ) );
Run Code Online (Sandbox Code Playgroud)

关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.

现在处于维护模式Joda-Time项目建议迁移到java.time类.

要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.

您可以直接与数据库交换java.time对象.使用符合JDBC 4.2或更高版本的JDBC驱动程序.不需要字符串,不需要课程.java.sql.*

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.


  • 我认为这没有帮助,因为它不解析 ISO 8601 日期,仅解析一个非常具体的子集。例如,`java.time.OffsetDateTime.parse ("2010-01-01")`失败 (2认同)

Ant*_*nio 47

从 Java 8 开始,有一种全新的官方支持的方式来做到这一点:

    String s = "2020-02-13T18:51:09.840Z";
    TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(s);
    Instant i = Instant.from(ta);
    Date d = Date.from(i);
Run Code Online (Sandbox Code Playgroud)

  • @OleV.V。您可以使用“ISO_OFFSET_DATE_TIME”来格式化带有偏移量的日期,例如“+01:00”(https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#predefined) (4认同)
  • 如果字符串采用即时格式,并且尾随“Z”作为偏移量,则我们不需要显式指定这一点。只是`Instant i = Instant.parse(s);`。问题中的字符串有“+01:00”,在这种情况下“DateTimeFormatter.ISO_INSTANT”不起作用(至少在我的 Java 11 上不起作用)。 (2认同)
  • 这无法解析“2020-06-01T14:34:00-05:00”,它是由 Javascript 的 toISOString() 方法生成的字符串。 (2认同)

d.d*_*lov 27

对于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);
Run Code Online (Sandbox Code Playgroud)

  • 适用于 Java 1.8 (3认同)

Jam*_*ven 20

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

javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()
Run Code Online (Sandbox Code Playgroud)

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


小智 16

我认为我们应该使用

DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
Run Code Online (Sandbox Code Playgroud)

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

  • 这****IGNORES**完全是时区.在我意识到这种情况发生之前一直在使用它,所以我切换到了JodaTime. (15认同)
  • 如果你不关心时区,与时俱进没什么意义! (7认同)
  • 为什么这是一个比其他人更好的答案,包括76个upvotes的接受答案? (5认同)
  • @ErickRobertson:它很简单,开箱即用,灵活,无需转换,大多数人不关心时区. (3认同)
  • 扔掉时区只会导致某些时候出错. (3认同)
  • -1.这只是为格式添加了一个'Z'(字面值),但并没有真正考虑时区..正如其他人已经指出的那样.不要用! (3认同)

tma*_*dry 9

解析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());
  }
}
Run Code Online (Sandbox Code Playgroud)


pav*_*ety 8

Java 8+

我在答案中没有找到的简单的衬里:

Date date = Date.from(ZonedDateTime.parse("2010-01-01T12:00:00+01:00").toInstant());
Run Code Online (Sandbox Code Playgroud)

日期不包含时区,它将存储在 UTC 中,但即使在简单输出期间也会正确转换为您的 JVM 时区System.out.println(date)


mmd*_*our 7

在我搜索了很多将 ISO8601 转换为最新版本后,我突然发现了一个 Java 类,它是ISO8601Util.java,它是com.google.gson.internal.bind.util 的一部分。所以你可以用它来转换日期。

ISO8601Utils.parse("2010-01-01T12:00:00Z" , ParsePosition(0))
Run Code Online (Sandbox Code Playgroud)

你可以简单地使用这个 kotlin 扩展功能

fun String.getDateFromString() : Date? = ISO8601Utils.parse(this , 
ParsePosition(0))
Run Code Online (Sandbox Code Playgroud)


Mar*_*ust 6

java.time

请注意,在Java 8中,您可以使用java.time.ZonedDateTime类及其静态parse(CharSequence text)方法.


Kha*_*.NT 6

Java 7+的解决方法是使用SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

此代码可以解析ISO8601格式,如:

  • 2017-05-17T06:01:43.785Z
  • 2017-05-13T02:58:21.391+01:00

但是在Java6上,SimpleDateFormat不了解X字符并且会抛出
IllegalArgumentException: Unknown pattern character 'X'
我们需要将ISO8601日期标准化为Java 6中可读的格式SimpleDateFormat.

public static Date iso8601Format(String formattedDate) throws ParseException {
    try {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);
        return df.parse(formattedDate);
    } catch (IllegalArgumentException ex) {
        // error happen in Java 6: Unknown pattern character 'X'
        if (formattedDate.endsWith("Z")) formattedDate = formattedDate.replace("Z", "+0000");
        else formattedDate = formattedDate.replaceAll("([+-]\\d\\d):(\\d\\d)\\s*$", "$1$2");
        DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
        return df1.parse(formattedDate);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的方法在Java 6中发生错误时替换[ Zwith +0000]或[ +01:00with +0100](您可以检测Java版本并用if语句替换try/catch).


小智 5

您也可以使用以下课程 -

org.springframework.extensions.surf.util.ISO8601DateFormat


Date date = ISO8601DateFormat.parse("date in iso8601");
Run Code Online (Sandbox Code Playgroud)

链接到 Java 文档 -包的层次结构 org.springframework.extensions.surf.maven.plugin.util


Abh*_*mar 5

我遇到了同样的问题并通过以下代码解决了它。

 public static Calendar getCalendarFromISO(String datestring) {
    Calendar calendar = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault()) ;
    SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
    try {
        Date date = dateformat.parse(datestring);
        date.setHours(date.getHours() - 1);
        calendar.setTime(date);

        String test = dateformat.format(calendar.getTime());
        Log.e("TEST_TIME", test);

    } catch (ParseException e) {
        e.printStackTrace();
    }

    return calendar;
}
Run Code Online (Sandbox Code Playgroud)

早些时候我正在使用 SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

但后来我发现异常的主要原因是yyyy-MM-dd'T'HH:mm:ss.SSSZ

所以我用

SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());

它对我来说很好。


Dan*_*ein 5

Java 有十几种不同的方法来解析日期时间,正如这里的优秀答案所展示的那样。但有点令人惊讶的是,Java 的时间类都没有完全实现 ISO 8601!

使用 Java 8,我建议:

ZonedDateTime zp = ZonedDateTime.parse(string);
Date date = Date.from(zp.toInstant());
Run Code Online (Sandbox Code Playgroud)

这将处理 UTC 和偏移量的示例,例如“2017-09-13T10:36:40Z”或“2017-09-13T10:36:40+01:00”。它适用于大多数用例。

但它不会处理像“2017-09-13T10:36:40+01”这样的例子,这一个有效的 ISO 8601 日期时间。
它也不会只处理日期,例如“2017-09-13”。

如果您必须处理这些,我建议首先使用正则表达式来嗅探语法。

有ISO的一个不错的名单这里8601分的例子有很多的极端情况:https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/我不知道任何可以处理所有这些的 Java 类。


Arv*_*ash 5

爪哇12

从 Java 12 开始,Instant#parse可以解析包含时区偏移的日期时间字符串。

Date dt = Date.from(Instant.parse(your-date-time-string));
Run Code Online (Sandbox Code Playgroud)

使用解析ISO 8601格式的日期时间字符串Instant#parse,并将结果转换为java.util.Date使用Date#from. 请参阅在 Ideone.com 上运行的此代码。

演示

import java.time.Instant;
import java.util.Date;
import java.util.stream.Stream;

public class Main {
  public static void main(String[] args) {
    Stream.of(
        "2010-01-01T12:00:00+01:00",
        "2010-01-01T12:00:00-01:00",
        "2010-01-01T12:00:00Z"
      )
      .map(Instant::parse)
      .map(Date::from)
      .forEach(System.out::println);
  }
}
Run Code Online (Sandbox Code Playgroud)

请参阅在 Ideone.com 上运行的此代码。

从Trail: Date Time中了解有关现代日期时间 API 的更多信息。

  • 如果您必须为遗留 API 需要一个老式的“Date”,那么这就是一种现代而优雅的解决方案。我记不清哪个 Java 版本 `Instant.parse("2010-01-01T12:00:00+01:00")` 使用除 `Z` 以外的偏移量? (2认同)
  • @OleV.V。[`Instant.parse`](https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/Instant.html#parse(java.lang Java 17 和 19 的 .CharSequence)) 仍然表示输入必须采用 UTC。引用:“该字符串必须代表 UTC 中的有效时刻”。但是,[`DateTimeFormatter.ISO_INSTANT`](https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_INSTANT ) 表示它将“解析偏移量,将瞬时转换为 UTC”。似乎 Java 12 是第一个提及解析偏移量的“ISO_INSTANT”Javadoc。 (2认同)

归档时间:

查看次数:

547488 次

最近记录:

5 年,10 月 前