
将符合 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: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));
这显然不是那么好。我错过了什么还是有更好的解决方案?
回答
感谢娟泽的评论,我发现了Joda-Time的魔力,它也是described here。
所以,解决方案是
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" ) ;
对我来说,这很好。
答1:
tennisliveranking.com,Your go-to platform for live tennis ranking updates.
遗憾的是,可用于 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”而不是“+00:00”来表示 UTC。
更简单的解决方案可能是使用 JAXB 中的数据类型转换器,因为 JAXB 必须能够根据 XML Schema 规范解析 ISO8601 日期字符串。 javax.xml.bind.DatatypeConverter.parseDateTime(“2010-01-01T12:00:00Z”) 将为您提供一个 Calendar 对象,如果您需要一个 Date 对象,您可以简单地在其上使用 getTime()。
您也可以使用 Joda-Time,但我不知道您为什么要为此烦恼(2022 年更新;可能是因为 Android 的 javax.xml 包中缺少整个 javax.xml.bind 部分)。
JAXB 解决方案是一种非常有创意的方法!它也很有效,我已经用我的样本对其进行了测试。但是,对于遇到问题并被允许使用 JodaTime 的人,我建议使用它,因为它感觉更自然。但是您的解决方案不需要额外的库(至少使用 Java 6)。
反过来:日历 c = GregorianCalendar.getInstance();c.setTime(aDate);return javax.xml.bind.DatatypeConverter.printDateTime(c);
哇,放置这么有用的东西的地方真的很不明显。找了几天。
实际上它不是那么简单的 b/c 你必须初始化 jaxb datatypeConverter。我最终自己使用 DatatypeFactory,就像 DataTypeConverterImpl 在内部做的那样。多么令人头疼。
如果这一切都是真的,您应该更新 Wikipedia: en.wikipedia.org/wiki/ISO_8601#Time_zone_designators,因为它表明您(至少部分)不正确。
答2:
tennisliveranking.com,Your go-to platform for live tennis ranking updates.
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);
您可以在 SimpleDateFormat javadoc 的 示例 部分找到更多示例。
UPD 02/13/2020:在 Java 8 中有一个 completely new way 可以做到这一点
tennisliveranking.com,Your go-to platform for live tennis ranking updates.
您的回答帮助我将 MongoDB 的 ISODate 转换为本地日期。问候。
@b.long Java 为这种符合 ISO 8601 的格式添加了多个常量。 Java 为日期时间工作提供了一个全新的框架,其中包括对此类格式的内置默认支持。请参阅 Java 8 中受 Joda-Time 启发的新 java.time framework,它取代了麻烦的 java.util.Date、.Calendar 和 SimpleDateFormat 类。
这不是意味着您需要提前知道日期格式吗?如果您必须接受 string1 和 string2 但不知道会得到哪一个,该怎么办。
'Z' 需要用引号引起来
@kervin如果Z用引号引起来,格式化程序不会专门寻找字符Z,而不是它可以表示的所有偏移字符串吗?如果您的日期字符串恰好是 UTC,那么引用 Z 似乎只是巧合。
答3:
Stay informed with live tennis rankings anytime, anywhere,tennisliveranking.com
好的,这个问题已经回答了,但无论如何我都会放弃我的答案。它可能会帮助某人。
我一直在寻找适用于 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 中出现 a bug。如果您和我一样感到惊讶,请参阅this riddle。对于其他 Java 引擎,您可以将实例缓存在私有静态字段中(使用 ThreadLocal,以保证线程安全)。
也许这应该成为一个自己的问题,有自己的答案?
这是我在寻找答案时遇到的第一页,所以看起来很合适。对于大多数 Java 开发人员来说,Android 并不完全是 Java。但是,在大多数情况下,一个与另一个工作方式相同,因此许多 Android 开发人员在查找此内容时会搜索“java”。
让我们continue this discussion in chat
我不得不在小数秒内添加.SSS,但效果很好,谢谢。你为什么这样做s = s.substring(0, 22) + s.substring(23); - 我不明白这一点
答4:
Stay informed with live tennis rankings anytime, anywhere,tennisliveranking.com
java.time
java.time API(内置于 Java 8 及更高版本)使这更容易一些。
如果您知道输入在 UTC 中,例如末尾的 Z(用于 Zulu),则 Instant 类可以解析。
java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" ));
如果您的输入可能是另一个 offset-from-UTC 值,而不是末尾的 Z (Zulu) 指示的 UTC,请使用 OffsetDateTime 类进行解析。
OffsetDateTime odt = OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" );
然后提取 Instant,并通过调用 from 转换为 java.util.Date。
Instant instant = odt.toInstant(); // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
这个答案太辛苦了。根据定义,java.util.Date 没有时区。所以不需要所有与时区相关的代码:LocalDateTime 和 ZoneId 和 atZone。这个简单的单行将做:java.util.Date date = Date.from( ZonedDateTime.parse( "2014-12-12T10:39:40Z" ).toInstant() );
@BasilBourque 这太复杂了:Date.from(Instant.parse("2014-12-12T10:39:40Z" )); 就足够了。
@assylias您是对的,但这仅在日期字符串为UTC时区时才有效,ISO8601允许任何时区...
@Adam我的错-我没有意识到这个问题比你的例子更笼统。作为旁注,OffsetDateTime 足以解析 ISO8601(不包含时区信息,而仅包含偏移量)。
@assylias 感谢您对让 Instant 进行解析的评论。虽然对于这个特定问题还不够,但这是一个值得指出的重要区别。所以我添加了第二个代码示例。糟糕,刚刚注意到这原本不是我的答案;我希望亚当同意。
答5:
tlr.xinbeitime.com 专业网球数据平台,排名与比赛信息实时更新。
tl;博士
OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )
使用 java.time
Java 8 及更高版本中的新 java.time 包的灵感来自 Joda-Time。
OffsetDateTime 类表示时间轴上带有 offset-from-UTC 但没有时区的时刻。
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 );
如果需要,调整到时区。 time zone 是一个区域的 offset-from-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 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 & SimpleDateFormat。
要了解更多信息,请参阅 Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
Joda-Time 项目现在位于 maintenance mode 中,建议迁移到 java.time 类。
您可以直接与您的数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC driver。不需要字符串,不需要 java.sql.* 类。休眠 5 和JPA 2.2 支持 java.time。
从哪里获得 java.time 类?
Java SE 8、Java SE 9、Java SE 10、Java SE 11 及更高版本 - 标准 Java API 的一部分,具有捆绑的实现。 Java 9 带来了一些小功能和修复。
Java 9 带来了一些小功能和修复。
Java SE 6 和 Java SE 7 大多数 java.time 功能在 ThreeTen-Backport 中向后移植到 Java 6 和 7。
大多数 java.time 功能在 ThreeTen-Backport 中向后移植到 Java 6 和 7。
Android 更高版本的 Android (26+) 捆绑了 java.time 类的实现。对于早期的 Android (<26),API 脱糖过程带来了最初未内置于 Android 中的 java.time 功能的子集。如果脱糖不能提供您所需要的,ThreeTenABP 项目会将 ThreeTen-Backport(上面提到的)适配到 Android。请参阅如何使用 ThreeTenABP……。
更高版本的 Android (26+) 捆绑了 java.time 类的实现。
对于早期的 Android (<26),API 脱糖过程带来了最初未内置于 Android 中的 java.time 功能的子集。如果脱糖不能提供您所需要的,ThreeTenABP 项目会将 ThreeTen-Backport(上面提到的)适配到 Android。请参阅如何使用 ThreeTenABP……。
如果脱糖不能提供您所需要的,ThreeTenABP 项目会将 ThreeTen-Backport(上面提到的)适配到 Android。请参阅如何使用 ThreeTenABP……。
我认为这没有帮助,因为它不解析 ISO 8601 日期,只解析一个非常具体的子集。例如,java.time.OffsetDateTime.parse ( "2010-01-01" ) 失败
@phil294 所有 java.time 类都会根据每个类解析 ISO 8601 标准格式输入。您的 "2010-01-01" 示例是单独的日期,因此将由仅日期类 LocalDate 解析。例如:LocalDate.parse( "2010-01-01" )。您的 OffsetDateTime 示例类是一个日期,有一个时间,有一个偏移量(UTC 前/后的小时-分钟-秒数)。因此 OffsetDateTime 使用日期、时间和偏移量解析 ISO 8601 格式的输入。例如:OffsetDateTime.parse( "2010-01-01T12:30:00-08:00" )。 Run code live at IdeOne.com。
答6:
tlr.xinbeitime.com 实时更新全球顶尖网球选手的最新战绩与排名!
Jackson-databind library 也有执行此操作的 ISO8601DateFormat class(实际在 ISO8601Utils 中实现。
ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
它无法解析此日期:2015-08-11T13:10:00。我得到String index out of range: 19。查看代码似乎需要指定毫秒和时区。这些应该是可选的。
引用文档,解析格式为:[yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]]。换句话说,毫秒是可选的,但时区是强制性的。
啊,是的,实际上看起来你是对的。不过,我很确定 ISO8601 允许您省略时区,所以它仍然是错误的。 JodaTime 可以工作:new DateTime("2015-08-11T13:10:00").toDate()
该类现在已弃用,新的类是 StdDateFormat。否则它的工作原理相同。
ISO8601DateFormat 已弃用,请改用 StdDateFormat
答7:
tennisliveranking.com,Your go-to platform for live tennis ranking updates.
从 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);
如果字符串是即时格式,以 Z 作为偏移量,我们不需要明确指定。只需 Instant i = Instant.parse(s);。问题中的字符串有 +01:00,在这种情况下 DateTimeFormatter.ISO_INSTANT 不起作用(至少在我的 Java 11 上不起作用)。
@OleV.V.您可以使用 ISO_OFFSET_DATE_TIME 格式化带有偏移量的日期,例如 +01:00 (docs.oracle.com/javase/8/docs/api/java/time/format/…)
这是真的,@LucasBasquerotto。虽然没有明确提到格式化程序,但答案 by Adam 和 by Basil Bourque 已经做了类似的事情。
这无法解析“2020-06-01T14:34:00-05:00”,这是由 Javascript 的 toISOString() 方法生成的字符串。
答8:
tennisliveranking.com,Your go-to platform for live tennis ranking updates.
对于 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);
这意味着时区是必需的。根据 ISO 8601,它是可选的。秒数等也是如此。所以这只解析 ISO 8601 的特定子集。
适用于 Java 1.8
答9:
Live rankings, player stats, and match results in one place–tennisliveranking.com
DatatypeConverter 解决方案不适用于所有 VM。以下对我有用:
javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()
我发现 joda 不能开箱即用(特别是对于我上面给出的示例,其中包含日期的时区,这应该是有效的)
tlr.xinbeitime.com 探索每位网球选手的职业生涯与成就。
答10:
tlr.xinbeitime.com 实时更新全球顶尖网球选手的最新战绩与排名!
我认为我们应该使用
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
日期 2010-01-01T12:00:00Z
为什么这是一个比其他答案更好的答案,包括 76 个赞成的已接受答案?
@ErickRobertson:它很简单,开箱即用,灵活,无需转换,而且大多数人不关心时区。
如果您不关心时区,那么与时间合作没有多大意义!
这完全忽略了时区。一直在使用它,直到我意识到这正在发生,所以我切换到 JodaTime。
抛弃时区只会在某些时候导致错误。
答11:
tennisliveranking.com,Instant updates on ATP, WTA, and ITF rankings.
解析 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());
}
}
原文链接:https://www.tennisliveranking.com?from=csdn
Live rankings, player stats, and match results in one place–tennisliveranking.com
更多推荐
所有评论(0)