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

发布于 2024-08-19 22:59:30 字数 1425 浏览 12 评论 0 原文

我正在尝试将 ISO 8601 格式的字符串转换为 java.util.Date< /代码>。

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

但是,使用java.text.SimpleDateFormat,我无法转换格式正确的字符串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));

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


回答

感谢JuanZe的评论,我发现了Joda-Time的魔力,它也在此处进行了描述

所以,解决方案是

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

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

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

对我来说,这很好。

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

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

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.

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", "+0$100");
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-Time magic, it is also described here.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

余生再见 2024-08-26 22:59:30

不幸的是,时区格式可用于 SimpleDateFormat (Java 6 及更早版本)不符合 ISO 8601。 SimpleDateFormat 可以理解“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 模式规范解析 ISO8601 日期字符串。 javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") 将为您提供一个 Calendar 对象,您只需使用 getTime()如果您需要一个 Date 对象,就可以使用它。

您也可以使用 Joda-Time ,但我不知道您为什么要费心(2022 年更新;可能是因为 Android 的 javax.xml 包中缺少整个 javax.xml.bind 部分)。

Unfortunately, the time zone formats available to SimpleDateFormat (Java 6 and earlier) are not ISO 8601 compliant. SimpleDateFormat understands time zone strings like "GMT+01:00" or "+0100", the latter according to 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.

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".

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 Calendar object and you can simply use getTime() on it, if you need a Date object.

You could probably use Joda-Time as well, but I don't know why you should bother with that (Update 2022; maybe because the entire javax.xml.bind section is missing from Android's javax.xml package).

游魂 2024-08-26 22:59:30

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

您可以在 示例部分找到更多示例“noreferrer”>SimpleDateFormat javadoc

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

The way that is blessed by Java 7 documentation:

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 Examples at SimpleDateFormat javadoc.

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

笨死的猪 2024-08-26 22:59:30

好吧,这个问题已经有了答案,但我还是放弃我的答案。它可能对某人有帮助。

我一直在寻找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;
    }
}

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

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

  • 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.xml won't work on Android API 7.

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

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 bug in 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).

挖鼻大婶 2024-08-26 22:59:30

java.time

java.time API(内置于 Java 8 及更高版本中),使这变得更容易一些。

如果您知道输入采用 UTC 格式,例如 Z (对于祖鲁语)最后, Instant< /code> 类可以解析。

Date date = Date.from(Instant.parse("2014-12-12T10:39:40Z"));

如果您的输入可能是另一个 offset-from-UTC 值而不是 UTC 由末尾的 Z (Zulu) 指示,使用 OffsetDateTime 要解析的类。

OffsetDateTime odt = OffsetDateTime.parse("2010-01-01T12:00:00+01:00");

然后提取 Instant,并转换为 java.util .Date 通过调用 来自

Instant instant = odt.toInstant(); // Instant is always in UTC.
Date date = Date.from(instant);

java.time

The java.time API (built into Java 8 and later), makes this a little easier.

If you know the input is in UTC, such as the Z (for Zulu) on the end, the Instant class can parse.

Date date = Date.from(Instant.parse("2014-12-12T10:39:40Z"));

If your input may be another offset-from-UTC values rather than UTC indicated by the Z (Zulu) on the end, use the OffsetDateTime class to parse.

OffsetDateTime odt = OffsetDateTime.parse("2010-01-01T12:00:00+01:00");

Then extract an Instant, and convert to a java.util.Date by calling from.

Instant instant = odt.toInstant(); // Instant is always in UTC.
Date date = Date.from(instant);
从﹋此江山别 2024-08-26 22:59:30

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

Starting from Java 8, there is a completely new officially supported way to do this:

    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);
烟─花易冷 2024-08-26 22:59:30

tl;dr

OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )

或者,对于 Java 12+,请使用 Instant.parse,如 Arvind Kumar Avinash 的回答< /a>.

使用 java.time

新的 java.time< Java 8 及更高版本中的 /a> 包受到 Joda-Time 的启发。

OffsetDateTime 类表示时间线上的一个时刻,具有 offset-from-UTC< /a> 但不是时区。

OffsetDateTime odt = OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" );

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

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

要通过 UTC 镜头查看相同的值,请提取 Instant 或将 +01:00 的偏移量调整为 <代码>00:00

Instant instant = odt.toInstant();  

...或者...

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );

如果需要,调整到时区。 时区相对于 UTC 的偏移 区域值,以及一组用于处理异常的规则,例如夏令时 (DST)。因此,尽可能应用时区而不仅仅是偏移量。

ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of( "America/Montreal" ) );

对于仅日期值,请使用 LocalDate

LocalDate ld = LocalDate.of( 2010 , Month.JANUARY , 1 ) ;

或者:

LocalDate ld = LocalDate.parse( "2010-01-01" ) ;

关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如java.util.Date日历, & SimpleDateFormat

要了解更多信息,请参阅 Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范为 JSR 310

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

您可以直接与数据库交换java.time对象。使用符合 JDBC 驱动程序 /jeps/170" rel="nofollow noreferrer">JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* 类。 Hibernate 5 和 Hibernate 5 JPA 2.2 支持 java.time。

从哪里获取 java.time 类?


tl;dr

OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )

Or for Java 12+, use Instant.parse as seen in the Answer by Arvind Kumar Avinash.

Using java.time

The new java.time package in Java 8 and later was inspired by Joda-Time.

The OffsetDateTime class represents a moment on the timeline with an offset-from-UTC but not a time zone.

OffsetDateTime odt = OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" );

Calling toString generates a string in standard ISO 8601 format:

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

To see the same value through the lens of UTC, extract an Instant or adjust the offset from +01:00 to 00:00.

Instant instant = odt.toInstant();  

…or…

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );

Adjust into a time zone if desired. A time zone is a history of offset-from-UTC values for a region, with a set of rules for handling anomalies such as Daylight Saving Time (DST). So apply a time zone rather than a mere offset whenever possible.

ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of( "America/Montreal" ) );

For a date-only value, use LocalDate.

LocalDate ld = LocalDate.of( 2010 , Month.JANUARY , 1 ) ;

Or:

LocalDate ld = LocalDate.parse( "2010-01-01" ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?


忆依然 2024-08-26 22:59:30

Jackson-databind 库 还具有 ISO8601DateFormat 类 执行此操作(实际实现在 ISO8601Utils

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

The Jackson-databind library also has ISO8601DateFormat class that does that (actual implementation in ISO8601Utils.

ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
入怼 2024-08-26 22:59:30

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

For Java version 7

You can follow Oracle documentation:
http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X - is used for ISO 8601 time zone

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);
生死何惧 2024-08-26 22:59:30

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

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

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

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

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)

翻了热茶 2024-08-26 22:59:30

我认为我们应该使用

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

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

I think we should use

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

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

鲸落 2024-08-26 22:59:30

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

ISO8601Utils.parse("2010-01-01T12:00:00Z" , ParsePosition(0))

你可以简单地使用这个 kotlin 扩展函数

fun String.getDateFromString() : Date? = ISO8601Utils.parse(this , 
ParsePosition(0))

After I searched a lot to convert ISO8601 to date I suddenly found a java class that is ISO8601Util.java and this was part of com.google.gson.internal.bind.util.
So you can use it to convert dates.

ISO8601Utils.parse("2010-01-01T12:00:00Z" , ParsePosition(0))

and you can simply use this kotlin extension function

fun String.getDateFromString() : Date? = ISO8601Utils.parse(this , 
ParsePosition(0))
还如梦归 2024-08-26 22:59:30

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

Another very simple way to parse ISO8601 timestamps is to use 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());
  }
}
寒冷纷飞旳雪 2024-08-26 22:59:30

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:未知模式字符“X”
我们需要使用 SimpleDateFormat 将 ISO8601 日期标准化为 Java 6 中可读的格式。

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

上述方法在出错时将 [Z 替换为 +0000] 或 [+01:00 替换为 +0100]发生在Java 6中(您可以检测Java版本并用if语句替换try/catch)。

The workaround for Java 7+ is using SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

This code can parse ISO8601 format like:

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

But on Java6, SimpleDateFormat doesn't understand X character and will throw
IllegalArgumentException: Unknown pattern character 'X'
We need to normalize ISO8601 date to the format readable in Java 6 with 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);
    }
}

Method above to replace [Z with +0000] or [+01:00 with +0100] when error occurs in Java 6 (you can detect Java version and replace try/catch with if statement).

少年亿悲伤 2024-08-26 22:59:30

Java 8+

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

Date date = Date.from(ZonedDateTime.parse("2010-01-01T12:00:00+01:00").toInstant());

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

Java 8+

Simple one liner that I didn't found in answers:

Date date = Date.from(ZonedDateTime.parse("2010-01-01T12:00:00+01:00").toInstant());

Date doesn't contain timezone, it will be stored in UTC, but will be properly converted to your JVM timezone even during simple output with System.out.println(date).

梦断已成空 2024-08-26 22:59:30

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

对于 Java 8,我建议:

ZonedDateTime zp = ZonedDateTime.parse(string);
Date date = Date.from(zp.toInstant());

这将处理 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 类。

Java has a dozen different ways to parse a date-time, as the excellent answers here demonstrate. But somewhat amazingly, none of Java's time classes fully implement ISO 8601!

With Java 8, I'd recommend:

ZonedDateTime zp = ZonedDateTime.parse(string);
Date date = Date.from(zp.toInstant());

That will handle examples both in UTC and with an offset, like "2017-09-13T10:36:40Z" or "2017-09-13T10:36:40+01:00". It will do for most use cases.

But it won't handle examples like "2017-09-13T10:36:40+01", which is a valid ISO 8601 date-time.
It also won't handle date only, e.g. "2017-09-13".

If you have to handle those, I'd suggest using a regex first to sniff the syntax.

There's a nice list of ISO 8601 examples here with lots of corner cases: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ I'm not aware of any Java class that could cope with all of them.

甜味拾荒者 2024-08-26 22:59:30

java.time

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

java.time

Note that in Java 8, you can use the java.time.ZonedDateTime class and its static parse(CharSequence text) method.

眼眸里的快感 2024-08-26 22:59:30

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

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

早些时候我正在使用
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());

它对我来说效果很好。

I faced the same problem and solved it by the following code .

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

Earlier I was using
SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

But later i found the main cause of the exception was the yyyy-MM-dd'T'HH:mm:ss.SSSZ ,

So i used

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

It worked fine for me .

颜漓半夏 2024-08-26 22:59:30

您还可以使用以下类 -

org.springframework.extensions.surf.util.ISO8601DateFormat


Date date = ISO8601DateFormat.parse("date in iso8601");

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

Also you can use the following class -

org.springframework.extensions.surf.util.ISO8601DateFormat


Date date = ISO8601DateFormat.parse("date in iso8601");

Link to the Java Doc - Hierarchy For Package org.springframework.extensions.surf.maven.plugin.util

野稚 2024-08-26 22:59:30

Java 12

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

Date dt = Date.from(Instant.parse(your-date-time-string));

使用 ISO 8601 格式的日期时间字符串.oracle.com/en/java/javase/11/docs/api/java.base/java/time/Instant.html#parse(java.lang.CharSequence)" rel="nofollow noreferrer">即时#解析 并使用 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);
  }
}

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

通过跟踪:日期时间<了解有关现代日期时间 API 的更多信息/a>

Java 12

Since Java 12, Instant#parse can parse a date-time string containing time-zone offset.

Date dt = Date.from(Instant.parse(your-date-time-string));

Parse your ISO 8601 formatted date-time strings using Instant#parse and convert the result into java.util.Date using Date#from. See this code run at Ideone.com.

Demo:

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

See this code run at Ideone.com.

Learn more about the modern Date-Time API from Trail: Date Time.

那小子欠揍 2024-08-26 22:59:30

正如其他人提到的,Android 没有一个好的方法来支持使用 SDK 中包含的类来解析/格式化 ISO 8601 日期。我已经多次编写了这段代码,所以我最终创建了一个 Gist,其中包含一个 DateUtils 类,该类支持格式化和解析 ISO 8601 和 RFC 1123 日期。 Gist 还包括一个测试用例,显示它支持什么。

https://gist.github.com/mraccola/702330625fad8eebe7d3

As others have mentioned Android does not have a good way to support parsing/formatting ISO 8601 dates using classes included in the SDK. I have written this code multiple times so I finally created a Gist that includes a DateUtils class that supports formatting and parsing ISO 8601 and RFC 1123 dates. The Gist also includes a test case showing what it supports.

https://gist.github.com/mraccola/702330625fad8eebe7d3

羁拥 2024-08-26 22:59:30

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

SimpleDateFormat 类

这是我所做的:

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

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

Class SimpleDateFormat

Here is what I did:

Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
         Locale.ENGLISH).format(System.currentTimeMillis());
夢归不見 2024-08-26 22:59:30

使用类似字符串
LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

Use string like
LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

别想她 2024-08-26 22:59:30

令我惊讶的是,没有一个 Java 库支持所有 ISO 8601 日期格式 https://en.wikipedia .org/wiki/ISO_8601。 Joda DateTime 支持其中的大多数,但不是全部,因此我添加了自定义逻辑来处理所有这些。这是我的实现。

import java.text.ParseException;
import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTime;

public class ISO8601DateUtils {
	
	/**
	 * It parses all the date time formats from https://en.wikipedia.org/wiki/ISO_8601 and returns Joda DateTime.
	 * Zoda DateTime does not support dates of format 20190531T160233Z, and hence added custom logic to handle this using SimpleDateFormat.
	 * @param dateTimeString ISO 8601 date time string
	 * @return
	 */
	public static DateTime parse(String dateTimeString) {
		try {
			return new DateTime( dateTimeString );
		} catch(Exception e) {
			try {
				Date dateTime = DateUtils.parseDate(dateTimeString, JODA_NOT_SUPPORTED_ISO_DATES);
				return new DateTime(dateTime.getTime());
			} catch (ParseException e1) {
				throw new RuntimeException(String.format("Date %s could not be parsed to ISO date", dateTimeString));
			}
		}
	}
  
  	private static String[] JODA_NOT_SUPPORTED_ISO_DATES = new String[] {
			// upto millis
			"yyyyMMdd'T'HHmmssSSS'Z'",
			"yyyyMMdd'T'HHmmssSSSZ",
			"yyyyMMdd'T'HHmmssSSSXXX",
			
			"yyyy-MM-dd'T'HHmmssSSS'Z'",
			"yyyy-MM-dd'T'HHmmssSSSZ",
			"yyyy-MM-dd'T'HHmmssSSSXXX",
			
			// upto seconds
			"yyyyMMdd'T'HHmmss'Z'",
			"yyyyMMdd'T'HHmmssZ",
			"yyyyMMdd'T'HHmmssXXX",
			
			"yyyy-MM-dd'T'HHmmss'Z'", 
			"yyyy-MM-dd'T'HHmmssZ",
			"yyyy-MM-dd'T'HHmmssXXX",
			
			// upto minutes
			"yyyyMMdd'T'HHmm'Z'",
			"yyyyMMdd'T'HHmmZ",
			"yyyyMMdd'T'HHmmXXX",

			"yyyy-MM-dd'T'HHmm'Z'",
			"yyyy-MM-dd'T'HHmmZ",
			"yyyy-MM-dd'T'HHmmXXX",
			
			//upto hours is already supported by Joda DateTime
	};
}

I am surprised that not even one java library supports all ISO 8601 date formats as per https://en.wikipedia.org/wiki/ISO_8601. Joda DateTime was supporting most of them, but not all and hence I added custom logic to handle all of them. Here is my implementation.

import java.text.ParseException;
import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTime;

public class ISO8601DateUtils {
	
	/**
	 * It parses all the date time formats from https://en.wikipedia.org/wiki/ISO_8601 and returns Joda DateTime.
	 * Zoda DateTime does not support dates of format 20190531T160233Z, and hence added custom logic to handle this using SimpleDateFormat.
	 * @param dateTimeString ISO 8601 date time string
	 * @return
	 */
	public static DateTime parse(String dateTimeString) {
		try {
			return new DateTime( dateTimeString );
		} catch(Exception e) {
			try {
				Date dateTime = DateUtils.parseDate(dateTimeString, JODA_NOT_SUPPORTED_ISO_DATES);
				return new DateTime(dateTime.getTime());
			} catch (ParseException e1) {
				throw new RuntimeException(String.format("Date %s could not be parsed to ISO date", dateTimeString));
			}
		}
	}
  
  	private static String[] JODA_NOT_SUPPORTED_ISO_DATES = new String[] {
			// upto millis
			"yyyyMMdd'T'HHmmssSSS'Z'",
			"yyyyMMdd'T'HHmmssSSSZ",
			"yyyyMMdd'T'HHmmssSSSXXX",
			
			"yyyy-MM-dd'T'HHmmssSSS'Z'",
			"yyyy-MM-dd'T'HHmmssSSSZ",
			"yyyy-MM-dd'T'HHmmssSSSXXX",
			
			// upto seconds
			"yyyyMMdd'T'HHmmss'Z'",
			"yyyyMMdd'T'HHmmssZ",
			"yyyyMMdd'T'HHmmssXXX",
			
			"yyyy-MM-dd'T'HHmmss'Z'", 
			"yyyy-MM-dd'T'HHmmssZ",
			"yyyy-MM-dd'T'HHmmssXXX",
			
			// upto minutes
			"yyyyMMdd'T'HHmm'Z'",
			"yyyyMMdd'T'HHmmZ",
			"yyyyMMdd'T'HHmmXXX",

			"yyyy-MM-dd'T'HHmm'Z'",
			"yyyy-MM-dd'T'HHmmZ",
			"yyyy-MM-dd'T'HHmmXXX",
			
			//upto hours is already supported by Joda DateTime
	};
}

慵挽 2024-08-26 22:59:30

当想要从 UTC 转换为我们想要的格式时。它会根据我们停留的区域/位置而变化

//utcDate = "2021-06-05T02:46:29Z"
fun converterUtcToReadableDateTime(utcDate: String): String {
    val offsetDateTime = OffsetDateTime.ofInstant(Instant.parse(utcDate), ZoneId.systemDefault())
    val patternDate = "dd MMM yyyy h:mm a"
    return DateTimeFormatter.ofPattern(patternDate).format(offsetDateTime)
}

fun converterUtcToReadableDate(utcDate: String): String {
    val offsetDateTime = OffsetDateTime.ofInstant(Instant.parse(utcDate), ZoneId.systemDefault())
    val patternDate = "d MMM yyyy"
    return DateTimeFormatter.ofPattern(patternDate).format(offsetDateTime)
}

fun converterUtcToReadableTime(utcDate: String): String {
    val offsetDateTime = OffsetDateTime.ofInstant(Instant.parse(utcDate), ZoneId.systemDefault())
    val patternDate = "h:mm a"
    return DateTimeFormatter.ofPattern(patternDate).format(offsetDateTime)
}

When want to convert from UTC to format that we want. It will change depend on the zone/location we stay

//utcDate = "2021-06-05T02:46:29Z"
fun converterUtcToReadableDateTime(utcDate: String): String {
    val offsetDateTime = OffsetDateTime.ofInstant(Instant.parse(utcDate), ZoneId.systemDefault())
    val patternDate = "dd MMM yyyy h:mm a"
    return DateTimeFormatter.ofPattern(patternDate).format(offsetDateTime)
}

fun converterUtcToReadableDate(utcDate: String): String {
    val offsetDateTime = OffsetDateTime.ofInstant(Instant.parse(utcDate), ZoneId.systemDefault())
    val patternDate = "d MMM yyyy"
    return DateTimeFormatter.ofPattern(patternDate).format(offsetDateTime)
}

fun converterUtcToReadableTime(utcDate: String): String {
    val offsetDateTime = OffsetDateTime.ofInstant(Instant.parse(utcDate), ZoneId.systemDefault())
    val patternDate = "h:mm a"
    return DateTimeFormatter.ofPattern(patternDate).format(offsetDateTime)
}
菊凝晚露 2024-08-26 22:59:30

这样做:

public static void main(String[] args) throws ParseException {

    String dateStr = "2016-10-19T14:15:36+08:00";
    Date date = javax.xml.bind.DatatypeConverter.parseDateTime(dateStr).getTime();

    System.out.println(date);

}

这是输出:

Wed Oct 19 15:15:36 CST 2016

Do it like this:

public static void main(String[] args) throws ParseException {

    String dateStr = "2016-10-19T14:15:36+08:00";
    Date date = javax.xml.bind.DatatypeConverter.parseDateTime(dateStr).getTime();

    System.out.println(date);

}

Here is the output:

Wed Oct 19 15:15:36 CST 2016

情痴 2024-08-26 22:59:30

一个小测试,展示如何解析 ISO8601 中的日期,并且 LocalDateTime 不处理 DST。

 @Test
    public void shouldHandleDaylightSavingTimes() throws ParseException {

        //ISO8601 UTC date format
        SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

        // 1 hour of difference between 2 dates in UTC happening at the Daylight Saving Time
        Date d1 = utcFormat.parse("2019-10-27T00:30:00.000Z");
        Date d2 = utcFormat.parse("2019-10-27T01:30:00.000Z");

        //Date 2 is before date 2
        Assert.assertTrue(d1.getTime() < d2.getTime());
        // And there is 1 hour difference between the 2 dates
        Assert.assertEquals(1000*60*60, d2.getTime() - d1.getTime());

        //Print the dates in local time
        SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm z Z", Locale.forLanguageTag("fr_CH"));
        localFormat.setTimeZone(TimeZone.getTimeZone("Europe/Zurich"));

        //Both dates are at 02h30 local time (because of DST), but one is CEST +0200 and the other CET +0100 (clock goes backwards)
        Assert.assertEquals("2019-10-27 02:30 CEST +0200", localFormat.format(d1));
        Assert.assertEquals("2019-10-27 02:30 CET +0100", localFormat.format(d2));

        //Small test that shows that LocalDateTime does not handle DST (and should not be used for storing timeseries data)
        LocalDateTime ld1 = LocalDateTime.ofInstant(d1.toInstant(), ZoneId.of("Europe/Zurich"));
        LocalDateTime ld2 = LocalDateTime.ofInstant(d2.toInstant(), ZoneId.of("Europe/Zurich"));

        //Note that a localdatetime does not handle DST, therefore the 2 dates are the same
        Assert.assertEquals(ld1, ld2);

        //They both have the following local values
        Assert.assertEquals(2019, ld1.getYear());
        Assert.assertEquals(27, ld1.getDayOfMonth());
        Assert.assertEquals(10, ld1.getMonthValue());
        Assert.assertEquals(2, ld1.getHour());
        Assert.assertEquals(30, ld1.getMinute());
        Assert.assertEquals(0, ld1.getSecond());

    }

A little test that shows how to parse a date in ISO8601 and that LocalDateTime does not handle DSTs.

 @Test
    public void shouldHandleDaylightSavingTimes() throws ParseException {

        //ISO8601 UTC date format
        SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

        // 1 hour of difference between 2 dates in UTC happening at the Daylight Saving Time
        Date d1 = utcFormat.parse("2019-10-27T00:30:00.000Z");
        Date d2 = utcFormat.parse("2019-10-27T01:30:00.000Z");

        //Date 2 is before date 2
        Assert.assertTrue(d1.getTime() < d2.getTime());
        // And there is 1 hour difference between the 2 dates
        Assert.assertEquals(1000*60*60, d2.getTime() - d1.getTime());

        //Print the dates in local time
        SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm z Z", Locale.forLanguageTag("fr_CH"));
        localFormat.setTimeZone(TimeZone.getTimeZone("Europe/Zurich"));

        //Both dates are at 02h30 local time (because of DST), but one is CEST +0200 and the other CET +0100 (clock goes backwards)
        Assert.assertEquals("2019-10-27 02:30 CEST +0200", localFormat.format(d1));
        Assert.assertEquals("2019-10-27 02:30 CET +0100", localFormat.format(d2));

        //Small test that shows that LocalDateTime does not handle DST (and should not be used for storing timeseries data)
        LocalDateTime ld1 = LocalDateTime.ofInstant(d1.toInstant(), ZoneId.of("Europe/Zurich"));
        LocalDateTime ld2 = LocalDateTime.ofInstant(d2.toInstant(), ZoneId.of("Europe/Zurich"));

        //Note that a localdatetime does not handle DST, therefore the 2 dates are the same
        Assert.assertEquals(ld1, ld2);

        //They both have the following local values
        Assert.assertEquals(2019, ld1.getYear());
        Assert.assertEquals(27, ld1.getDayOfMonth());
        Assert.assertEquals(10, ld1.getMonthValue());
        Assert.assertEquals(2, ld1.getHour());
        Assert.assertEquals(30, ld1.getMinute());
        Assert.assertEquals(0, ld1.getSecond());

    }
帅的被狗咬 2024-08-26 22:59:30

我有类似的需求:我需要能够解析任何符合 ISO8601 的日期,而无需提前知道确切的格式,并且我想要一个也适用于 Android 的轻量级解决方案。

当我用谷歌搜索我的需求时,我偶然发现了这个问题,并注意到 AFAIU,没有答案完全符合我的需求。所以我开发了 jISO8601 并将其推送到 Maven Central 上。

只需添加 pom.xml:

<dependency>
  <groupId>fr.turri</groupId>
  <artifactId>jISO8601</artifactId>
  <version>0.2</version>
</dependency>

然后就可以了:

import fr.turri.jiso8601.*;
...
Calendar cal = Iso8601Deserializer.toCalendar("1985-03-04");
Date date = Iso8601Deserializer.toDate("1985-03-04T12:34:56Z");

希望它有帮助。

I had a similar need: I needed to be able to parse any date ISO8601 compliant without knowing the exact format in advance, and I wanted a lightweight solution which would also work on Android.

When I googled my needs I stumbled upon this question, and noticed that AFAIU, no answer completely fit my needs. So I developed jISO8601 and pushed it on maven central.

Just add in you pom.xml:

<dependency>
  <groupId>fr.turri</groupId>
  <artifactId>jISO8601</artifactId>
  <version>0.2</version>
</dependency>

and then you're good to go:

import fr.turri.jiso8601.*;
...
Calendar cal = Iso8601Deserializer.toCalendar("1985-03-04");
Date date = Iso8601Deserializer.toDate("1985-03-04T12:34:56Z");

Hopes it help.

红ご颜醉 2024-08-26 22:59:30

要格式化这样的日期,以下内容在基于 Java 6 的应用程序中对我有用。 thymeleaf 项目中有一个 DateFormatJacksonThymeleafISO8601DateFormat ,它插入缺少的冒号:

https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b3012add6098b3/src/main/java/org/thymeleaf /standard/serializer/StandardJavaScriptSerializer.java

我用它来实现 ECMAScript 日期格式兼容性。

To just format a date like this the following worked for me in a Java 6 based application. There is a DateFormat class JacksonThymeleafISO8601DateFormat in the thymeleaf project which inserts the missing colon:

https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b3012add6098b3/src/main/java/org/thymeleaf/standard/serializer/StandardJavaScriptSerializer.java

I used it for ECMAScript date format compatibilty.

感情废物 2024-08-26 22:59:30

我无法使用 Java 8 功能,因此只有 java.util.Date 可用。我已经依赖 gson 库,但不想直接使用 ISO8601UtilsISO8601Utils 是一个内部 API,gson 的作者警告不要使用它

我使用 gson 的公共 API 解析了 ISO8601 日期:

fun parseISO8601DateToLocalTimeOrNull(date: String): Date? {
    return try {
        GsonBuilder()
            .create()
            .getAdapter(Date::class.java)
            .fromJson("\"$date\"")
     } catch (t: Throwable) {
        null
     }
}

在幕后,适配器仍然使用 ISO8601Utils。但如果您使用适配器,您可以确定不同的兼容版本的 gson 不会破坏您的项目。

我担心适配器的创建可能会很慢,因此我使用 debuggable=false 测量了 Pixel 3a 上的执行时间。parseISO8601DateToLocalTimeOrNull 解析日期大约需要 0.5 毫秒。

I couldn't use Java 8 features, so only java.util.Date was available. I already had a dependency on gson library but didn't want to use ISO8601Utils directly. ISO8601Utils is an internal API, gson's authors warns not to use it.

I parsed a ISO8601 date using gson's public API:

fun parseISO8601DateToLocalTimeOrNull(date: String): Date? {
    return try {
        GsonBuilder()
            .create()
            .getAdapter(Date::class.java)
            .fromJson("\"$date\"")
     } catch (t: Throwable) {
        null
     }
}

Under the hood, the adapter still uses ISO8601Utils. But if you're using the adapter you can be sure that a different compatible version of gson won't break your project.

I worried that creation of adapter may be slow so I measured execution time on Pixel 3a with debuggable=false.parseISO8601DateToLocalTimeOrNull took ~0.5 milliseconds to parse a date.

梦忆晨望 2024-08-26 22:59:30

基本功能由@wrygiel 提供。

该函数可以将 ISO8601 格式转换为可以处理偏移值的 Java Date。根据 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

该类有静态方法将

  • ISO8601 字符串转换为 Date(Local TimeZone) 对象
  • Date 为 ISO8601 字符串
  • 夏令时会自动计算

Sample ISO8601 Strings

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

}

Base Function Courtesy : @wrygiel.

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

±[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

Sample ISO8601 Strings

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

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文