将符合 ISO 8601 的字符串转换为 java.util.Date
我正在尝试将 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" ) ;
对我来说,这很好。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
不幸的是,时区格式可用于 SimpleDateFormat (Java 6 及更早版本)不符合 ISO 8601。 SimpleDateFormat 可以理解“GMT+01:00”或“+0100”等时区字符串,后者根据 RFC # 822。
即使 Java 7 根据 ISO 8601 添加了对时区描述符的支持,SimpleDateFormat 仍然无法正确解析完整的日期字符串,因为它不支持可选部分。
使用正则表达式重新格式化输入字符串当然是一种可能性,但替换规则并不像您的问题中那么简单:
更简单的解决方案可能是使用 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:
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 aCalendar
object and you can simply use getTime() on it, if you need aDate
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'sjavax.xml
package).Java 7 文档所祝福的方式 :
您可以在 示例部分找到更多示例“noreferrer”>SimpleDateFormat javadoc。
UPD 02/13/2020:在 Java 8 中有一种全新的方法来做到这一点
The way that is blessed by Java 7 documentation:
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
好吧,这个问题已经有了答案,但我还是放弃我的答案。它可能对某人有帮助。
我一直在寻找Android 解决方案(API 7)。
javax.xml
的答案在 Android API 7 上不起作用。最终实现了这个简单的类。它仅涵盖 ISO 8601 字符串的最常见形式,但这在某些情况下应该足够了(当您非常确定输入将采用这种格式时) 。
性能说明:我每次都会实例化新的 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).
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).
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).
java.time
java.time API(内置于 Java 8 及更高版本中),使这变得更容易一些。
如果您知道输入采用 UTC 格式,例如
Z
(对于祖鲁语)最后,Instant< /code>
类可以解析。
如果您的输入可能是另一个 offset-from-UTC 值而不是 UTC 由末尾的
Z
(Zulu) 指示,使用OffsetDateTime
要解析的类。然后提取
Instant
,并转换为java.util .Date
通过调用来自
。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, theInstant
class can parse.If your input may be another offset-from-UTC values rather than UTC indicated by the
Z
(Zulu) on the end, use theOffsetDateTime
class to parse.Then extract an
Instant
, and convert to ajava.util.Date
by callingfrom
.从 Java 8 开始,有一种全新的官方支持的方法来执行此操作:
Starting from Java 8, there is a completely new officially supported way to do this:
tl;dr
或者,对于 Java 12+,请使用
Instant.parse
,如 Arvind Kumar Avinash 的回答< /a>.使用 java.time
新的 java.time< Java 8 及更高版本中的 /a> 包受到 Joda-Time 的启发。
OffsetDateTime
类表示时间线上的一个时刻,具有 offset-from-UTC< /a> 但不是时区。调用 toString 会生成标准 ISO 8601 格式的字符串:
要通过 UTC 镜头查看相同的值,请提取
Instant
或将+01:00
的偏移量调整为 <代码>00:00。...或者...
如果需要,调整到时区。 时区是相对于 UTC 的偏移 区域值,以及一组用于处理异常的规则,例如夏令时 (DST)。因此,尽可能应用时区而不仅仅是偏移量。
对于仅日期值,请使用
LocalDate
。或者:
关于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
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.Calling
toString
generates a string in standard ISO 8601 format:To see the same value through the lens of UTC, extract an
Instant
or adjust the offset from+01:00
to00:00
.…or…
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.
For a date-only value, use
LocalDate
.Or:
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?
Jackson-databind 库 还具有 ISO8601DateFormat 类 执行此操作(实际实现在 ISO8601Utils。
The Jackson-databind library also has ISO8601DateFormat class that does that (actual implementation in ISO8601Utils.
对于 Java 版本 7
您可以遵循 Oracle 文档:
http://docs.oracle.com/javase/7/docs/api/ java/text/SimpleDateFormat.html
X - 用于 ISO 8601 时区
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
DatatypeConverter 解决方案并不适用于所有 VM。以下内容对我有用:
我发现 joda 不能开箱即用(特别是对于我上面给出的带有日期时区的示例,该示例应该是有效的)
The DatatypeConverter solution doesn't work in all VMs. The following works for me:
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)
我认为我们应该使用
Date
2010-01-01T12:00:00Z
I think we should use
for Date
2010-01-01T12:00:00Z
在我搜索了很多以将 ISO8601 转换为最新版本后,我突然发现了一个 java 类,它是 ISO8601Util.java,它是 com.google.gson.internal.bind.util 的一部分>。
所以你可以用它来转换日期。
你可以简单地使用这个 kotlin 扩展函数
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.
and you can simply use this kotlin extension function
解析 ISO8601 时间戳的另一种非常简单的方法是使用 org.apache.commons.lang.time.DateUtils:
Another very simple way to parse ISO8601 timestamps is to use
org.apache.commons.lang.time.DateUtils
: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 中可读的格式。上述方法在出错时将 [
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 understandX
character and will throwIllegalArgumentException: Unknown pattern character 'X'
We need to normalize ISO8601 date to the format readable in Java 6 with
SimpleDateFormat
.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).Java 8+
我在答案中没有找到的简单的一个衬里:
日期不包含时区,它将存储在 UTC 中,但即使在简单输出期间也会正确转换为您的 JVM 时区
System.out.println(日期)
。Java 8+
Simple one liner that I didn't found in answers:
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)
.Java 有十几种不同的方法来解析日期时间,正如这里的优秀答案所证明的那样。但有些令人惊讶的是,Java 的时间类都没有完全实现 ISO 8601!
对于 Java 8,我建议:
这将处理 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:
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.
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.我遇到了相同问题并通过以下代码解决了它。
早些时候我正在使用
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 .
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 .
您还可以使用以下类 -
链接到 Java 文档 - 包 org.springframework.extensions.surf.maven.plugin.util 的层次结构
Also you can use the following class -
Link to the Java Doc - Hierarchy For Package org.springframework.extensions.surf.maven.plugin.util
Java 12
自 Java 12 起,
Instant#parse
可以解析包含时区偏移量的日期时间字符串。使用 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 上运行的代码。演示:
请参阅此代码在 Ideone.com 上运行。
通过跟踪:日期时间<了解有关现代日期时间 API 的更多信息/a>。
Java 12
Since Java 12,
Instant#parse
can parse a date-time string containing time-zone offset.Parse your ISO 8601 formatted date-time strings using
Instant#parse
and convert the result intojava.util.Date
usingDate#from
. See this code run at Ideone.com.Demo:
See this code run at Ideone.com.
Learn more about the modern Date-Time API from Trail: Date Time.
正如其他人提到的,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
SimpleDateFormat for JAVA 1.7 有一个很酷的 ISO 8601 格式模式。
SimpleDateFormat 类
这是我所做的:
SimpleDateFormat for JAVA 1.7 has a cool pattern for ISO 8601 format.
Class SimpleDateFormat
Here is what I did:
使用类似字符串
LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)
Use string like
LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)
令我惊讶的是,没有一个 Java 库支持所有 ISO 8601 日期格式 https://en.wikipedia .org/wiki/ISO_8601。 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.
当想要从 UTC 转换为我们想要的格式时。它会根据我们停留的区域/位置而变化
When want to convert from UTC to format that we want. It will change depend on the zone/location we stay
这样做:
这是输出:
Wed Oct 19 15:15:36 CST 2016
Do it like this:
Here is the output:
Wed Oct 19 15:15:36 CST 2016
一个小测试,展示如何解析 ISO8601 中的日期,并且 LocalDateTime 不处理 DST。
A little test that shows how to parse a date in ISO8601 and that LocalDateTime does not handle DSTs.
我有类似的需求:我需要能够解析任何符合 ISO8601 的日期,而无需提前知道确切的格式,并且我想要一个也适用于 Android 的轻量级解决方案。
当我用谷歌搜索我的需求时,我偶然发现了这个问题,并注意到 AFAIU,没有答案完全符合我的需求。所以我开发了 jISO8601 并将其推送到 Maven Central 上。
只需添加
pom.xml
:然后就可以了:
希望它有帮助。
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
:and then you're good to go:
Hopes it help.
要格式化这样的日期,以下内容在基于 Java 6 的应用程序中对我有用。 thymeleaf 项目中有一个
DateFormat
类JacksonThymeleafISO8601DateFormat
,它插入缺少的冒号: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
classJacksonThymeleafISO8601DateFormat
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.
我无法使用 Java 8 功能,因此只有
java.util.Date
可用。我已经依赖 gson 库,但不想直接使用ISO8601Utils
。ISO8601Utils
是一个内部 API,gson 的作者警告不要使用它。我使用 gson 的公共 API 解析了 ISO8601 日期:
在幕后,适配器仍然使用 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 useISO8601Utils
directly.ISO8601Utils
is an internal API, gson's authors warns not to use it.I parsed a ISO8601 date using gson's public API:
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.基本功能由@wrygiel 提供。
该函数可以将 ISO8601 格式转换为可以处理偏移值的 Java Date。根据 ISO 8601 的定义,偏移量可以以不同的格式提及。
该类有静态方法将
Sample ISO8601 Strings
}
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.
This class has static methods to convert
Sample ISO8601 Strings
}