Преобразование строки, совместимой с ISO 8601, в java.util.Date

Я пытаюсь преобразовать форматированную строку ISO 8601 в java.util.Date .

Я нашел шаблон yyyy-MM-dd'T'HH:mm:ssZ который должен соответствовать ISO8601, если используется с Locale (сравнить образец).

Однако, используя java.text.SimpleDateFormat , я не могу преобразовать правильно отформатированную String 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)); 

что, очевидно, не так уж хорошо. Я что-то упустил или есть лучшее решение?


Ответ

Благодаря комментарию Хуанзе, я нашел магию Йода-Время , она также описана здесь .

Таким образом, решение

 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" ) ; 

Для меня это приятно.

К сожалению, форматы часовых поясов, доступные 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» для обозначения UTC вместо «+00: 00».

Более простым решением является использование конвертера типа данных в JAXB, поскольку JAXB должен иметь возможность анализировать строку даты ISO8601 в соответствии со спецификацией XML Schema. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") предоставит вам объект Calendar и вы можете просто использовать getTime () на нем, если вам нужен объект Date .

Вероятно, вы могли бы использовать Joda-Time , но я не знаю, почему вы должны это беспокоиться.

Хорошо, на этот вопрос уже ответили, но я все равно откажусь от ответа. Это может помочь кому-то.

Я искал решение для Android (API 7).

  • Джода не может быть и речи – она ​​огромна и страдает от медленной инициализации. Это также казалось серьезным излишеством в этой конкретной цели.
  • Ответы, связанные с javax.xml , не будут работать в Android API 7.

Закончилось внедрение этого простого classа. Он охватывает только наиболее распространенную форму строк 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. Если вы так же изумились, как и я, посмотрите на эту загадку . Для других механизмов Java вы можете кэшировать экземпляр в приватном статическом поле (используя ThreadLocal, чтобы быть streamобезопасным).

Способ, который поддерживается документацией 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 .

java.time

API java.time (встроенный в Java 8 и более поздние версии ) делает это немного проще.

Если вы знаете, что ввод находится в формате UTC , например Z (для Zulu) в конце, class Instant может анализироваться.

 java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" )); 

Если ваш ввод может быть другим значением смещения от UTC, а не UTC, обозначенным Z (Zulu) в конце, используйте class OffsetDateTime для parsingа.

 OffsetDateTime odt = OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" ); 

Затем извлеките Instant и конвертируйте в java.util.Date , вызывая.

 Instant instant = odt.toInstant(); // Instant is always in UTC. java.util.Date date = java.util.Date.from( instant ); 

Библиотека Jackson-databind также имеет class ISO8601DateFormat, который делает это (фактическая реализация в ISO8601Utils .

 ISO8601DateFormat df = new ISO8601DateFormat(); Date d = df.parse("2010-07-28T22:25:51Z"); 

ТЛ; др

 OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" ) 

Использование java.time

Новый пакет java.time в Java 8 и более поздних версиях был вдохновлен Joda-Time.

Класс OffsetDateTime представляет собой момент на временной шкале со смещением-от-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 ); 

При необходимости настройтесь на часовой пояс. Часовой пояс представляет собой историю значений смещения-от-UTC для региона, с набором правил для обработки аномалий, таких как переход на летнее время (DST). Поэтому примените часовой пояс, а не просто смещение, когда это возможно.

 ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of( "America/Montreal" ) ); 

О java.time

Рамка java.time встроена в Java 8 и более поздние версии . Эти classы вытесняют неприятные старые устаревшие classы времени, такие как java.util.Date , Calendar и SimpleDateFormat .

Проект Joda-Time , теперь в режиме обслуживания , советует перейти на classы java.time .

Чтобы узнать больше, ознакомьтесь с учебным пособием Oracle . И поиск Stack Overflow для многих примеров и объяснений. Спецификация – JSR 310 .

Вы можете обменивать объекты java.time непосредственно с вашей базой данных. Используйте JDBC-драйвер, совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в java.sql.* .

Где можно получить classы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API с интегрированной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональных возможностей java.time включена обратно в Java 6 и 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии реализаций пакетов Android classов java.time.
    • Для более ранних Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). См. Раздел Как использовать ThreeTenABP ….

Проект ThreeTen-Extra расширяет java.time с дополнительными classами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные classы, такие как Interval , YearWeek , YearQuarter и другие .


Для 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); 

Решение DatatypeConverter не работает на всех виртуальных машинах. Для меня работает следующее:

 javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime() 

Я обнаружил, что joda не работает из коробки (специально для примера, который я дал выше, с часовым поясом на дату, которая должна быть действительной)

Я думаю, мы должны использовать

 DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") 

по дате 2010-01-01T12:00:00Z

Еще один очень простой способ parsingа временных меток 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()); } } 

java.time

Обратите внимание, что в Java 8 вы можете использовать class 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());

Это сработало для меня.

Также вы можете использовать следующий class –

 org.springframework.extensions.surf.util.ISO8601DateFormat Date date = ISO8601DateFormat.parse("date in iso8601"); 

Ссылка на Java Doc – Иерархия для пакета org.springframework.extensions.surf.maven.plugin.util

Apache Jackrabbit использует формат ISO 8601 для сохраняющихся дат, и для их анализа используется вспомогательный class:

org.apache.jackrabbit.util.ISO8601

Поставляется с jackrabbit-jcr-commons .

Как отмечают другие, Android не имеет хорошего способа поддержки синтаксического анализа / форматирования дат ISO 8601 с использованием classов, включенных в SDK. Я написал этот код несколько раз, поэтому я, наконец, создал Gist, который включает в себя class DateUtils, который поддерживает форматирование и синтаксический анализ дат ISO 8601 и RFC 1123. В Gist также есть тестовый пример, показывающий, что он поддерживает.

https://gist.github.com/mraccola/702330625fad8eebe7d3

Обходной путь для 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'
Нам нужно нормализовать дату ISO8601 в формате, который читается на Java 6 с помощью SimpleDateFormat .

 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); } } 

Метод выше для замены [ Z с +0000 ] или [ +01:00 с +0100 ], когда ошибка возникает в Java 6 (вы можете обнаружить версию Java и заменить try / catch на оператор if).

SimpleDateFormat для JAVA 1.7 имеет classную модель для ISO 8601.

Класс SimpleDateFormat

Вот что я сделал:

 Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH).format(System.currentTimeMillis()); 

В Java есть десяток различных способов анализа времени-даты, как показывают превосходные ответы. Но несколько удивительно, что ни один из classов времени 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-09-13».

Если вам нужно их обработать, я бы предложил сначала использовать регулярное выражение, чтобы обнюхать синтаксис.

Здесь есть хороший список примеров ISO 8601 с большим количеством угловых случаев: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ Я не зная какого-либо Java-classа, который мог бы справиться со всеми из них.

Используйте строку, такую ​​как LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

У меня была аналогичная потребность: мне нужно было разобрать любую дату, совместимую с ISO8601, не зная точного формата заранее, и я хотел получить легкое решение, которое также будет работать на Android.

Когда я искал свои потребности, я наткнулся на этот вопрос и заметил, что AFAIU, никакой ответ полностью соответствует моим потребностям. Поэтому я разработал jISO8601 и нажал на центральную часть maven.

Просто добавьте в pom.xml :

  fr.turri jISO8601 0.2  

и тогда вам хорошо идти:

 import fr.turri.jiso8601.*; ... Calendar cal = Iso8601Deserializer.toCalendar("1985-03-04"); Date date = Iso8601Deserializer.toDate("1985-03-04T12:34:56Z"); 

Надеюсь, что это поможет.

Чтобы просто форматировать дату, подобную этой, мне работало в приложении на основе Java 6. В DateFormat class DateFormat JacksonThymeleafISO8601DateFormat, который вставляет отсутствующий двоеточие:

https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b3012add6098b3/src/main/java/org/thymeleaf/standard/serializer/StandardJavaScriptSerializer.java

Я использовал его для совместимости формата даты ECMAScript.

Делай это так:

 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

Базовая функция Предоставлено: @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 

Этот class имеет статические методы для преобразования

  • Объект ISO8601 для даты (Local TimeZone)
  • Дата по строке ISO8601
  • Летнее время автоматически вычисляется

Примеры строк 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; } 

}

Мне это показалось лучше всего:

 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; } 

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("
"+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("
"+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:

https://github.com/RichardHightower/json-parsers-benchmark

  • Как проверить, содержит ли строка только цифры в Java
  • javax.net.ssl.SSLException: Получено фатальное предупреждение: protocol_version
  • изменение размера изображения в java
  • В чем разница между шаблоном Singleton и статическим classом в Java?
  • Печать Runtime exec () OutputStream для консоли
  • Сравните два объекта с операторами .equals () и ==
  • Массив или список в Java. Что быстрее?
  • Обтекание ByteBuffer с помощью InputStream
  • Ошибка Java: незаконный запуск выражения
  • AbstractMethodError с использованием UriBuilder на JAX-RS
  • Eclipse: изменить имя существующего пакета с classами внутри
  • Давайте будем гением компьютера.