将符合ISO 8601标准的字符串转换为java.util.Date
将符合ISO 8601标准的字符串转换为java.util.Date
我正在尝试将一个符合ISO 8601格式的字符串转换为java.util.Date
。我发现模式yyyy-MM-dd\'T\'HH:mm:ssZ
在使用Locale时符合ISO8601标准(参考示例)。\n然而,使用java.text.SimpleDateFormat
时,我无法转换格式正确的字符串2010-01-01T12:00:00+01:00
。我必须先将它转换为没有冒号的2010-01-01T12:00:00+0100
。\n因此,目前的解决方案是:\n
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));
\n显然,这不是很好的解决方案。我是不是漏掉了什么,或者还有更好的解决方案吗?\n
\n答案\n感谢JuanZe的评论,我找到了Joda-Time的魔力,也可以在这里找到描述。\n所以,解决方案是:\n
DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis(); String jtdate = "2010-01-01T12:00:00+01:00"; System.out.println(parser2.parseDateTime(jtdate));
\n或者更简单地,通过构造函数使用默认解析器:\n
DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;
\n对我来说,这很好。
问题出现的原因是在Android API 7中,无法使用Joda或javax.xml解决ISO 8601字符串转换为java.util.Date的问题。为了解决这个问题,作者实现了一个简单的帮助类,该类仅支持ISO 8601字符串的最常见形式。
作者提供了一个名为ISO8601的帮助类,其中包含了三个方法:fromCalendar、now和toCalendar。fromCalendar方法将Calendar对象转换为ISO 8601字符串,now方法获取当前日期和时间的ISO 8601字符串表示,toCalendar方法将ISO 8601字符串转换为Calendar对象。
还有一个性能问题,即每次实例化SimpleDateFormat对象是为了避免Android 2.1中的一个bug。对于其他Java引擎,可以将实例缓存到一个私有静态字段中以提高性能。
最后,作者解释了为什么将这个问题放在此页面上。他说,对于大多数Java开发人员来说,Android并不完全等同于Java。然而,在大多数情况下,两者的工作方式是相同的,因此许多Android开发人员在寻找解决方案时会搜索"java"。
值得注意的是,有一个读者提到了需要在代码中添加.SSS来处理小数秒的问题,并询问了为什么要执行s = s.substring(0, 22) + s.substring(23)操作的意义。然而,作者没有给出回答。
这篇文章介绍了如何将ISO 8601格式的字符串转换为java.util.Date对象的问题,并提供了一个简单的帮助类来解决这个问题。同时,还提到了在Android中处理SimpleDateFormat时的一个bug,并解释了为什么将该问题放在此页面上。
问题的出现原因:
这个问题的出现是因为需要将符合ISO 8601规范的字符串转换为java.util.Date对象。在Java 7中,可以使用SimpleDateFormat类来实现这个转换,但是需要注意使用正确的格式化字符串。
解决方法:
根据Java 7的官方文档,可以使用以下代码将ISO 8601格式的字符串转换为java.util.Date对象:
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);
这里有两个示例,分别使用了不同的格式化字符串来解析ISO 8601格式的字符串。更多示例可以在SimpleDateFormat文档的“示例”部分找到。
值得注意的是,从Java 8开始,可以使用新的java.time框架来处理日期和时间相关的操作。这个框架受到了Joda-Time的启发,并取代了java.util.Date、.Calendar和SimpleDateFormat类。因此,如果使用的是Java 8或更高版本,可以考虑使用java.time框架来进行ISO 8601格式的转换。
在Java 8中,可以使用以下代码将ISO 8601格式的字符串转换为java.time.LocalDateTime对象:
String string = "2001-07-04T12:08:56.235-07:00"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); LocalDateTime dateTime = LocalDateTime.parse(string, formatter);
通过使用SimpleDateFormat类或java.time框架中的相关类,可以将符合ISO 8601规范的字符串转换为java.util.Date或java.time.LocalDateTime对象。在Java 7中,可以使用SimpleDateFormat类的parse方法,并提供正确的格式化字符串来实现转换。而在Java 8及更高版本中,可以使用java.time框架中的相关类和方法来实现转换。
Converting ISO 8601-compliant String to java.util.Date
在Java 6及之前的版本中,可用于SimpleDateFormat的时区格式不符合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 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中)。
以下是反向操作的代码示例:
Calendar c = GregorianCalendar.getInstance(); c.setTime(aDate); return javax.xml.bind.DatatypeConverter.printDateTime(c);
哇,这是一个非常不明显的地方放置了一个如此有用的东西。我已经搜索了几天了。
实际上,这并不那么简单,因为您必须初始化jaxb datatypeConverter。我最终使用了DatatypeFactory自己作为DataTypeConverterImpl在内部使用的方式。真是头疼。
如果这一切都是真的,那么您应该更新维基百科:en.wikipedia.org/wiki/ISO_8601#Time_zone_designators,因为它表明您(至少部分)是错误的。