我正在尝试将ISO 8601格式化的字符串转换为java.util.Date
。
如果与Locale(比较样本)一起使用,我发现模式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魔法,它也是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" ) ;
对我来说,这很好。
答案 0 :(得分:445)
不幸的是,SimpleDateFormat(Java 6及更早版本)可用的时区格式不符合ISO 8601。 SimpleDateFormat理解时区字符串,如“GMT + 01:00”或“+0100”,后者根据RFC # 822。
即使Java 7根据ISO 8601添加了对时区描述符的支持,SimpleDateFormat仍然无法正确解析完整的日期字符串,因为它不支持可选部分。
使用regexp重新格式化输入字符串当然是一种可能,但替换规则并不像您的问题那么简单:
更简单的解决方案是在JAXB中使用数据类型转换器,因为JAXB必须能够根据XML Schema规范解析ISO8601日期字符串。如果您需要javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z")
对象,Calendar
将为您提供Date
对象,您可以在其上使用getTime()。
你也可以使用Joda-Time,但我不知道为什么你应该为此烦恼。
答案 1 :(得分:198)
好的,这个问题已经回答了,但无论如何我都会放弃我的答案。这可能对某人有帮助。
我一直在寻找适用于Android的解决方案(API 7)。
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,以保证线程安全)。
答案 2 :(得分:196)
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上的示例部分找到更多示例。
答案 3 :(得分:88)
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
,并通过调用java.util.Date
转换为from
。
Instant instant = odt.toInstant(); // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
答案 4 :(得分:63)
Jackson-databind library也有ISO8601DateFormat class这样做(ISO8601Utils中的实际实施。
ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
答案 5 :(得分:38)
OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )
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" ) );
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 6 :(得分:27)
对于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);
答案 7 :(得分:24)
从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);
答案 8 :(得分:20)
DatatypeConverter解决方案不适用于所有VM。以下适用于我:
javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()
我发现joda不能开箱即用(特别是我上面提到的带有时区的例子,这应该是有效的)
答案 9 :(得分:14)
我认为我们应该使用
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
日期2010-01-01T12:00:00Z
答案 10 :(得分:9)
解析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());
}
}
答案 11 :(得分:6)
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: Unknown pattern character '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);
}
}
在Java 6中发生错误时,上面的方法将[Z
替换为+0000
]或[+01:00
替换为+0100
(您可以检测Java版本并替换try /赶上if语句。)
答案 12 :(得分:6)
请注意,在Java 8中,您可以使用java.time.ZonedDateTime类及其静态parse(CharSequence text)
方法。
答案 13 :(得分:4)
Java 8 +
我在答案中没有找到的一种简单的衬纸:
Date date = Date.from(ZonedDateTime.parse("2010-01-01T12:00:00+01:00").toInstant());
日期不包含时区,它将存储在UTC中,但即使在使用System.out.println(date)
进行简单输出的情况下,也可以正确地转换为JVM时区。
答案 14 :(得分:4)
我遇到了相同的问题,并通过以下代码解决了这个问题。
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());
它对我来说很好。
答案 15 :(得分:4)
在进行了大量搜索以将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))
答案 16 :(得分:4)
您还可以使用以下类 -
org.springframework.extensions.surf.util.ISO8601DateFormat
Date date = ISO8601DateFormat.parse("date in iso8601");
链接到Java Doc - Hierarchy For Package org.springframework.extensions.surf.maven.plugin.util
答案 17 :(得分:3)
正如其他人所说,Android没有很好的方法来支持使用SDK中包含的类来解析/格式化ISO 8601日期。我已多次编写此代码,因此我最终创建了一个Gist,其中包含一个DateUtils类,该类支持格式化和解析ISO 8601和RFC 1123日期。 Gist还包括一个显示它支持的测试用例。
答案 18 :(得分:3)
Apache Jackrabbit使用ISO 8601格式来保存日期,并且有一个帮助程序类来解析它们:
答案 19 :(得分:2)
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年9月13日”。
如果你必须处理这些,我建议先使用正则表达式来嗅探语法。
这里有一个很好的ISO 8601示例列表,其中包含许多极端情况:https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/我不知道任何可以处理所有这些问题的Java类。
答案 20 :(得分:2)
SimpleDateFormat for JAVA 1.7具有ISO 8601格式的酷炫模式。
这是我做的:
Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
Locale.ENGLISH).format(System.currentTimeMillis());
答案 21 :(得分:1)
这样做:
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);
}
这是输出:
2016年10月19日星期三15:15:36 CST 2016
答案 22 :(得分:1)
使用字符串
LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)
答案 23 :(得分:1)
一个小测试,显示如何在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());
}
答案 24 :(得分:1)
当想要从 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)
}
答案 25 :(得分:0)
我有类似的需求:我需要能够在不事先知道确切格式的情况下解析任何符合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");
希望有所帮助。
答案 26 :(得分:0)
要像这样格式化日期,以下内容适用于基于Java 6的应用程序。在thymeleaf项目中有一个DateFormat
类JacksonThymeleafISO8601DateFormat
,它插入了缺少的冒号:
我将它用于ECMAScript日期格式兼容性。
答案 27 :(得分:0)
令我惊讶的是,甚至没有一个Java库支持https://en.wikipedia.org/wiki/ISO_8601的所有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
};
}
答案 28 :(得分:0)
这是使用Java 7的基于Kotlin的方法。您可以传递自定义模式,也可以仅使用默认的ISO8601模式。不过,您可能需要处理解析错误。
object DateUtil {
private const val ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSX"
fun convertDateToTimestamp(date: Date, pattern: String = ISO_8601_PATTERN): String {
val df = SimpleDateFormat(pattern, Locale.getDefault()).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
return df.format(date)
}
fun convertTimestampToDate(timestamp: String, pattern: String = ISO_8601_PATTERN): Date {
val formatter = SimpleDateFormat(pattern, Locale.getDefault()).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
return formatter.parse(timestamp) ?: Date(0)
}
}
答案 29 :(得分:-1)
这似乎最适合我:
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日期字符串转换为Java。我发现以上作品的建议。有一些使用SimpleDateFormat的例子很接近,但它们似乎不是推荐的子集:
http://www.w3.org/TR/NOTE-datetime
并得到PLIST和JavaScript Strings的支持,这就是我所需要的。
这似乎是最常见的ISO8601字符串形式,也是一个很好的子集。
他们给出的例子是:
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;
}
我没有对它进行基准测试,但我猜它会很快。它似乎工作。 :)
@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);
}
答案 30 :(得分:-1)
基本功能礼貌:@wrygiel。
此函数可以将ISO8601格式转换为可以处理偏移值的Java Date。根据{{3}},可以用不同的格式提及偏移量。
±[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字符串
/* "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;
}
}
答案 31 :(得分:-2)
我认为很多人想做的是解析JSON日期字符串。如果您来到此页面,您可能希望将JavaScript JSON日期转换为Java日期。
显示JSON日期字符串的外观:
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)
JSON日期字符串是2013-12-14T01:55:33.412Z。
日期不包括JSON规范,但上面是一个非常具体的ISO 8601格式,而ISO_8601要大得多,虽然这只是一个非常重要的子集。
参见http://www.json.org 见http://en.wikipedia.org/wiki/ISO_8601 见http://www.w3.org/TR/NOTE-datetime
碰巧我写了一个JSON解析器和一个PLIST解析器,它们都使用ISO-8601而不是相同的位。
/*
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);
}
我为我的项目写了两种方法。一个标准,一个快速。
同样,JSON日期字符串是ISO 8601的一个非常具体的实现....
(我在另一个答案中发布了另一个答案,该答案适用于PLIST日期,这是一种不同的ISO 8601格式。)
JSON日期如下:
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文件(ASCII非GNUNext)也使用ISO 8601,但没有毫秒级,因此......并非所有ISO-8601日期都相同。 (至少我还没有找到一个使用milis而且我见过的解析器完全跳过了时区OMG)。
现在为快速版本(你可以在Boon找到它。)
public static Date fromJsonDate( String string ) {
return fromJsonDate ( Reflection.toCharArray ( string ), 0, string.length () );
}
请注意,Reflection.toCharArray使用unsafe(如果可用)但默认为string.toCharArray,如果不可用。
(你可以通过用string.toCharArray()替换Reflection.toCharArray(string)来取消它。
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;
}
}
isJsonDate实现如下:
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;
}
无论如何......我猜是很多人来到这里..可能正在寻找JSON日期字符串,尽管它是ISO-8601日期,但它是一个非常具体的需要非常具体的解析
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;
}
参见https://github.com/RichardHightower/boon Boon有一个PLIST解析器(ASCII)和一个JSON解析器。
JSON解析器是我所知道的最快的Java JSON解析器。
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
它为stream,readers,bytes [],char [],CharSequence(StringBuilder,CharacterBuffer)和String提供了最快的JSON解析器。
请参阅更多基准: