将ISO 8601兼容的string转换为java.util.Date
我试图将一个ISO 8601格式的string转换为一个java.util.Date。
如果与Locale(比较样本)一起使用,我发现模式“yyyy-MM-dd'T'HH:mm:ssZ”符合ISO8601标准。 但是,使用java.text.SimpleDateFormat,我不能转换格式正确的string“2010-01-01T12:00:00 + 01:00”。 我必须先将它转换为“2010-01-01T12:00:00 + 0100”,不用冒号。 所以,目前的解决scheme是
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));
这显然不是那么好。 我错过了什么或有更好的解决scheme吗?
回答
感谢JuanZe的评论,我发现了Joda-Time的魔法, 这里也有描述 。 所以,解决scheme是
DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis(); String jtdate = "2010-01-01T12:00:00+01:00"; System.out.println(parser2.parseDateTime(jtdate));
或者更简单地说,通过构造函数使用默认的parsing器:
DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;
对我来说,这很好。
不幸的是, SimpleDateFormat (Java 6及更早版本)可用的时区格式不符合ISO 8601标准。 SimpleDateFormat理解时区string,如“GMT + 01:00”或“+0100”,后者根据RFC#822 。
即使Java 7根据ISO 8601添加了对时区描述符的支持,SimpleDateFormat仍然无法正确parsing完整的datestring,因为它不支持可选部分。
使用regexp重新格式化你的inputstring当然是一种可能性,但是replace规则并不像你的问题那么简单:
- 有些时区并不是UTC时间 ,所以string不一定以“:00”结尾。
- ISO8601只允许在时区中包含小时数,所以“+01”相当于“+01:00”
- ISO8601允许使用“Z”来表示UTC而不是“+00:00”。
更简单的解决scheme可能是使用JAXB中的数据types转换器,因为JAXB必须能够根据XML Schema规范parsingISO8601datestring。 如果你需要一个Date
对象, javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z")
会给你一个Calendar
对象,你可以简单地使用它的getTime()。
你也可以使用Joda-Time ,但是我不知道你为什么要这么做。
好的,这个问题已经得到解答了,但我仍然会放弃我的答案。 这可能有助于某人。
我一直在寻找Android (API 7)的解决scheme 。
- 乔达是不可能的 – 这是巨大的,并受到初始化缓慢。 对于这个特定的目的来说,这似乎也是一个重大的过火。
- 涉及
javax.xml
答案在Android API 7上不起作用。
结束了这个简单的类的实现。 它只包含最常见的ISO 8601stringforms ,但在某些情况下(当您确定input将采用此格式时),这应该足够了。
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引擎,您可以将实例caching在私有静态字段中(使用ThreadLocal,以保证线程安全)。
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);
您可以在SimpleDateFormat javadoc的示例部分find更多示例。
jackson数据库库也有ISO8601DateFormat类 ,这样做(在ISO8601Utils中的实际实现。
ISO8601DateFormat df = new ISO8601DateFormat(); Date d = df.parse("2010-07-28T22:25:51Z");
java.time
java.time API (内置于Java 8及更高版本)使得这一点更容易一些。
如果你知道input是以UTC为单位的 ,比如最后的Z
(Zulu),那么Instant
类可以parsing。
java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" ));
如果您的input可能是另一个与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版本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);
TL;博士
OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )
乔达时间
更新: Joda-Time项目现在处于维护模式 ,团队build议迁移到java.time类。 跳转到下面的Using java.time
部分。
使用Joda-Time 2.3embedded到问题中的答案是正确的,但不必要的漫长而复杂。
DateTime类构造函数接受各种ISO 8601格式的String参数。 所以不需要调用parsing方法。 你可以在一行代码中完成 。
java.util.Date date = new DateTime( "2010-01-01T12:00:00+01:00Z" ).toDate();
时区
问题的embedded式答案忽略了时区的关键问题。 如果不另外指定,则将JVM的当前默认时区隐式分配给新的DateTime对象。 请记住,JVM的当前默认时区可以随时通过JVM中任何线程中的任何代码进行更改 。 在其他机器上运行时,默认时区也可能不同。
最好指定你想要的/预期的时区。 如果您确实需要默认值,请显式调用getDefault
来自我logging您的意图。
示例代码
这个问题没有涉及时区。 所以这里是不同时区可能性的示例代码。
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so. // import org.joda.time.*; // Default time zone. DateTime dateTimeInMyDefaultTimeZone = new DateTime( "2010-01-01T12:00:00+01:00" ); // UTC DateTime dateTimeInUtc = new DateTime( "2010-01-01T12:00:00+01:00", DateTimeZone.UTC ); // Named time zone // Time Zone list… http://joda-time.sourceforge.net/timezones.html DateTimeZone kolkataTimeZone = DateTimeZone.forID( "Asia/Kolkata" ); DateTime dateTimeInKolkata = new DateTime( "2010-01-01T12:00:00+01:00", kolkataTimeZone ); // Hard-coded to that one-hour offset. Using a named time zone would be better, to handle Daylight Saving Time (DST) or other anomalies. DateTimeZone timeZoneOffsetOneHour = DateTimeZone.forOffsetHours( 1 ); DateTime dateTimeInOneHourOffset = new DateTime( "2010-01-01T12:00:00+01:00", timeZoneOffsetOneHour ); // Using a named time zone to handle Daylight Saving Time (DST) or other anomalies. // Arbitrarily picking Algiers as a one-hour offset. DateTimeZone timeZoneAlgiers = DateTimeZone.forID( "Africa/Algiers" ); DateTime dateTimeAlgiers = new DateTime( "2010-01-01T12:00:00+01:00", timeZoneAlgiers );
转储到控制台…
System.out.println( "dateTimeInMyDefaultTimeZone: " + dateTimeInMyDefaultTimeZone ); System.out.println( "dateTimeInUtc: " + dateTimeInUtc ); System.out.println( "dateTimeInKolkata: " + dateTimeInKolkata ); System.out.println( "dateTimeInOneHourOffset: " + dateTimeInOneHourOffset ); System.out.println( "dateTimeAlgiers: " + dateTimeAlgiers );
当运行…
dateTimeInMyDefaultTimeZone: 2010-01-01T03:00:00.000-08:00 dateTimeInUtc: 2010-01-01T11:00:00.000Z dateTimeInKolkata: 2010-01-01T16:30:00.000+05:30 dateTimeInOneHourOffset: 2010-01-01T12:00:00.000+01:00 dateTimeAlgiers: 2010-01-01T12:00:00.000+01:00
使用java.time
Java 8及更高版本中新的java.time包受Joda-Time的启发。
OffsetDateTime
类表示时间轴上的一个时刻,与UTC的偏移量不同,但不是时区。
OffsetDateTime odt = OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" );
调用toString
生成标准ISO 8601格式的string:
2010-01-01T12:00 + 01:00
要通过UTC镜头查看相同的值,请提取Instant
或将偏移量从+01:00
调整为00:00
。
Instant instant = odt.toInstant();
…要么…
OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );
根据需要调整为时区。 时区是一个区域的偏移UTC值的历史logging,具有处理夏时制(DST)等exception的一组规则。 因此,只要可能,应用时区而不是单纯的偏移量。
ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of( "America/Montreal" ) );
关于java.time
java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧的遗留date时间类,如java.util.Date
, Calendar
和SimpleDateFormat
。
Joda-Time项目现在处于维护模式 ,build议迁移到java.time类。
要了解更多信息,请参阅Oracle教程 。 并search堆栈溢出了很多例子和解释。 规范是JSR 310 。
从何处获取java.time类?
- Java SE 8和SE 9及更高版本
- 内置。
- 带有捆绑实现的标准Java API的一部分。
- Java 9增加了一些小function和修复。
- Java SE 6和SE 7
- 大部分的java.timefunction都被移植到了ThreeTen-Backport中的 Java 6&7中。
- Android的
- ThreeTenABP项目专门针对Android,采用了ThreeTen-Backport (上文提到)。
- 请参阅如何使用ThreeTenABP …。
ThreeTen-Extra项目将java.time扩展到其他类。 这个项目是未来可能增加java.time的一个试验场。 你可能会在这里find一些有用的类,比如Interval
, YearWeek
, YearQuarter
等等 。
DatatypeConverter解决scheme不适用于所有虚拟机。 以下为我工作:
javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()
我发现,乔达并没有开箱即用(特别是我在上面给出的date时区的例子,这应该是有效的)
我认为我们应该使用
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
对于2010-01-01T12:00:00Z
另一种parsingISO8601时间戳的非常简单的方法是使用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()); } }
java.time
请注意,在Java 8中,可以使用java.time.ZonedDateTime类及其静态parse(CharSequence text)
方法。
我面临同样的问题,并通过下面的代码解决它。
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());
它为我工作得很好。
你也可以使用下面的类 –
org.springframework.extensions.surf.util.ISO8601DateFormat Date date = ISO8601DateFormat.parse("date in iso8601");
链接到Java文档 – 程序包的层次结构org.springframework.extensions.surf.maven.plugin.util
Apache Jackrabbit使用ISO 8601格式来保存date,还有一个辅助类来parsing它们:
org.apache.jackrabbit.util.ISO8601
来与jackrabbit-jcr共同性 。
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());
Java 7+的解决方法是使用SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);
这个代码可以像parsingISO8601格式一样:
-
2017-05-17T06:01:43.785Z
-
2017-05-13T02:58:21.391+01:00
但是在Java6上, SimpleDateFormat
不能理解X
字符并且会抛出
IllegalArgumentException: Unknown pattern character 'X'
我们需要使用SimpleDateFormat
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); } }
在Java 6中发生错误(您可以检测到Java版本,并用if语句replacetry / catch)时,将[ Z
用+0000
]或[ +01:00
用+0100
]replace。
正如其他人所说的,Android没有一个好方法来支持使用SDK中包含的类来parsing/格式化ISO 8601date。 我已经多次写了这个代码,所以我最终创build了一个包含DateUtils类的Gist,它支持格式化和parsingISO 8601和RFC 1123date。 Gist还包括一个testing案例,显示它支持什么。
像这样做:
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
使用string像LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)
我有类似的需求:我需要能够parsing任何dateISO8601兼容,而不必事先知道确切的格式,我想要一个轻量级的解决scheme,也可以在Android上工作。
当我search我的需求时,我偶然发现了这个问题,并注意到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");
希望它有帮助。
为了格式化一个像这样的date,以下在基于Java 6的应用程序中为我工作。 在thymeleaf项目中有一个DateFormat
类的JacksonThymeleafISO8601DateFormat插入缺less的冒号:
我用它ECMAScriptdate格式兼容性。
Java有十几种parsingdate的方法,正如这里所展示的优秀答案。 但有点令人惊讶的是,Java的时间级别都没有完全实现ISO 8601!
对于Java 8,我build议:
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 8601date时间。
它也不会处理date,例如“2017-09-13”。
如果你必须处理这些,我build议先使用正则expression式来嗅探语法。
这里有一个ISO 8601例子的很好的例子,这里有很多的例子: https : //www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/我是没有意识到任何可以应付所有这些Java类的Java类。
基本function礼貌:@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
这个类有静态方法来转换
- ISO8601string到date(本地时区)对象
- date为ISO8601string
- 夏令时自动计算
示例ISO8601string
/* "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; }
}
这似乎最适合我:
public static Date fromISO8601_( String string ) { try { return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ssXXX").parse ( string ); } catch ( ParseException e ) { return Exceptions.handle (Date.class, "Not a valid ISO8601", e); } }
我需要将JavaScript的datestring转换为Java。 我发现上述作品与build议。 有一些使用SimpleDateFormat的例子很接近,但它们似乎并不像以下推荐的那样:
http://www.w3.org/TR/NOTE-datetime
并由PLIST和JavaScriptstring等支持,这是我所需要的。
这似乎是ISO8601string中最常见的forms,也是一个很好的子集。
他们给的例子是:
1994-11-05T08:15:30-05:00 corresponds November 5, 1994, 8:15:30 am, US Eastern Standard Time. 1994-11-05T13:15:30Z corresponds to the same instant.
我也有一个快速版本:
final static int SHORT_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30Z".length (); // 01234567890123456789012 final static int LONG_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30-05:00".length (); public static Date fromISO8601( String string ) { if (isISO8601 ( string )) { char [] charArray = Reflection.toCharArray ( string );//uses unsafe or string.toCharArray if unsafe is not available int year = CharScanner.parseIntFromTo ( charArray, 0, 4 ); int month = CharScanner.parseIntFromTo ( charArray, 5, 7 ); int day = CharScanner.parseIntFromTo ( charArray, 8, 10 ); int hour = CharScanner.parseIntFromTo ( charArray, 11, 13 ); int minute = CharScanner.parseIntFromTo ( charArray, 14, 16 ); int second = CharScanner.parseIntFromTo ( charArray, 17, 19 ); TimeZone tz ; if (charArray[19] == 'Z') { tz = TimeZone.getTimeZone ( "GMT" ); } else { StringBuilder builder = new StringBuilder ( 9 ); builder.append ( "GMT" ); builder.append( charArray, 19, LONG_ISO_8601_TIME_LENGTH - 19); String tzStr = builder.toString (); tz = TimeZone.getTimeZone ( tzStr ) ; } return toDate ( tz, year, month, day, hour, minute, second ); } else { return null; } }
…
public static int parseIntFromTo ( char[] digitChars, int offset, int to ) { int num = digitChars[ offset ] - '0'; if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); } } } } } } } } return num; } public static boolean isISO8601( String string ) { boolean valid = true; if (string.length () == SHORT_ISO_8601_TIME_LENGTH) { valid &= (string.charAt ( 19 ) == 'Z'); } else if (string.length () == LONG_ISO_8601_TIME_LENGTH) { valid &= (string.charAt ( 19 ) == '-' || string.charAt ( 19 ) == '+'); valid &= (string.charAt ( 22 ) == ':'); } else { return false; } // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 // "1 9 9 4 - 1 1 - 0 5 T 0 8 : 1 5 : 3 0 - 0 5 : 0 0 valid &= (string.charAt ( 4 ) == '-') && (string.charAt ( 7 ) == '-') && (string.charAt ( 10 ) == 'T') && (string.charAt ( 13 ) == ':') && (string.charAt ( 16 ) == ':'); return valid; }
I have not benchmarked it, but I am guess it will be pretty fast. 这似乎工作。 🙂
@Test public void testIsoShortDate() { String test = "1994-11-05T08:15:30Z"; Date date = Dates.fromISO8601 ( test ); Date date2 = Dates.fromISO8601_ ( test ); assertEquals(date2.toString (), date.toString ()); puts (date); } @Test public void testIsoLongDate() { String test = "1994-11-05T08:11:22-05:00"; Date date = Dates.fromISO8601 ( test ); Date date2 = Dates.fromISO8601_ ( test ); assertEquals(date2.toString (), date.toString ()); puts (date); }
I think what a lot of people want to do is parse JSON date strings. There is a good chance if you come to this page that you might want to convert a JavaScript JSON date to a Java date.
To show what a JSON date string looks like:
var d=new Date(); var s = JSON.stringify(d); document.write(s); document.write("<br />"+d); "2013-12-14T01:55:33.412Z" Fri Dec 13 2013 17:55:33 GMT-0800 (PST)
The JSON date string is 2013-12-14T01:55:33.412Z.
Dates are not covered by JSON spec per say, but the above is a very specific ISO 8601 format, while ISO_8601 is much much bigger and that is a mere subset albeit a very important one.
See http://www.json.org See http://en.wikipedia.org/wiki/ISO_8601 See http://www.w3.org/TR/NOTE-datetime
As it happens I wrote a JSON parser and a PLIST parser both of which use ISO-8601 but not the same bits.
/* var d=new Date(); var s = JSON.stringify(d); document.write(s); document.write("<br />"+d); "2013-12-14T01:55:33.412Z" Fri Dec 13 2013 17:55:33 GMT-0800 (PST) */ @Test public void jsonJavaScriptDate() { String test = "2013-12-14T01:55:33.412Z"; Date date = Dates.fromJsonDate ( test ); Date date2 = Dates.fromJsonDate_ ( test ); assertEquals(date2.toString (), "" + date); puts (date); }
I wrote two ways to do this for my project. One standard, one fast.
Again, JSON date string is a very specific implementation of ISO 8601….
(I posted the other one in the other answer which should work for PLIST dates, which are a different ISO 8601 format).
The JSON date is as follows:
public static Date fromJsonDate_( String string ) { try { return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse ( string ); } catch ( ParseException e ) { return Exceptions.handle (Date.class, "Not a valid JSON date", e); } }
PLIST files (ASCII non GNUNext) also uses ISO 8601 but no miliseconds so… not all ISO-8601 dates are the same. (At least I have not found one that uses milis yet and the parser I have seen skip the timezone altogether OMG).
Now for the fast version (you can find it in Boon).
public static Date fromJsonDate( String string ) { return fromJsonDate ( Reflection.toCharArray ( string ), 0, string.length () ); }
Note that Reflection.toCharArray uses unsafe if available but defaults to string.toCharArray if not.
(You can take it out of the example by replacing Reflection.toCharArray ( string ) with string.toCharArray()).
public static Date fromJsonDate( char[] charArray, int from, int to ) { if (isJsonDate ( charArray, from, to )) { int year = CharScanner.parseIntFromTo ( charArray, from + 0, from + 4 ); int month = CharScanner.parseIntFromTo ( charArray, from +5, from +7 ); int day = CharScanner.parseIntFromTo ( charArray, from +8, from +10 ); int hour = CharScanner.parseIntFromTo ( charArray, from +11, from +13 ); int minute = CharScanner.parseIntFromTo ( charArray, from +14, from +16 ); int second = CharScanner.parseIntFromTo ( charArray, from +17, from +19 ); int miliseconds = CharScanner.parseIntFromTo ( charArray, from +20, from +23 ); TimeZone tz = TimeZone.getTimeZone ( "GMT" ); return toDate ( tz, year, month, day, hour, minute, second, miliseconds ); } else { return null; } }
The isJsonDate is implemented as follows:
public static boolean isJsonDate( char[] charArray, int start, int to ) { boolean valid = true; final int length = to -start; if (length != JSON_TIME_LENGTH) { return false; } valid &= (charArray [ start + 19 ] == '.'); if (!valid) { return false; } valid &= (charArray[ start +4 ] == '-') && (charArray[ start +7 ] == '-') && (charArray[ start +10 ] == 'T') && (charArray[ start +13 ] == ':') && (charArray[ start +16 ] == ':'); return valid; }
Anyway… my guess is that quite a few people who come here.. might be looking for the JSON Date String and although it is an ISO-8601 date, it is a very specific one that needs a very specific parse.
public static int parseIntFromTo ( char[] digitChars, int offset, int to ) { int num = digitChars[ offset ] - '0'; if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); } } } } } } } } return num; }
See https://github.com/RichardHightower/boon Boon has a PLIST parser (ASCII) and a JSON parser.
The JSON parser is the fastest Java JSON parser that I know of.
Independently verified by the Gatling Performance dudes.
https://github.com/gatling/json-parsers-benchmark
Benchmark Mode Thr Count Sec Mean Mean error Units BoonCharArrayBenchmark.roundRobin thrpt 16 10 1 724815,875 54339,825 ops/s JacksonObjectBenchmark.roundRobin thrpt 16 10 1 580014,875 145097,700 ops/s JsonSmartBytesBenchmark.roundRobin thrpt 16 10 1 575548,435 64202,618 ops/s JsonSmartStringBenchmark.roundRobin thrpt 16 10 1 541212,220 45144,815 ops/s GSONStringBenchmark.roundRobin thrpt 16 10 1 522947,175 65572,427 ops/s BoonDirectBytesBenchmark.roundRobin thrpt 16 10 1 521528,912 41366,197 ops/s JacksonASTBenchmark.roundRobin thrpt 16 10 1 512564,205 300704,545 ops/s GSONReaderBenchmark.roundRobin thrpt 16 10 1 446322,220 41327,496 ops/s JsonSmartStreamBenchmark.roundRobin thrpt 16 10 1 276399,298 130055,340 ops/s JsonSmartReaderBenchmark.roundRobin thrpt 16 10 1 86789,825 17690,031 ops/s
It has the fastest JSON parser for streams, readers, bytes[], char[], CharSequence (StringBuilder, CharacterBuffer), and String.
See more benchmarks at: