Simpledateformat синтаксический анализ с буквой "Z"

Я пытаюсь проанализировать дату, которая выглядит так:

2010-04-05T17:16:00Z

Это действительная дата на http://www.ietf.org/rfc/rfc3339.txt. Литерал "Z" подразумевает, что UTC  является предпочтительной контрольной точкой в ​​течение указанного времени.

Если я попытаюсь разобрать его с помощью SimpleDateFormat и этого шаблона:

yyyy-MM-dd'T'HH:mm:ss

Он будет проанализирован как Пн Апр 05 17:16:00 EDT 2010

SimpleDateFormat не может проанализировать строку с этими шаблонами:

yyyy-MM-dd'T'HH:mm:ssz
yyyy-MM-dd'T'HH:mm:ssZ

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

Ответ 1

В шаблоне включение компонента времени "z" указывает, что формат часового пояса должен соответствовать Общий часовой пояс "стандарт", примерами которых являются Pacific Standard Time; PST; GMT-08:00.

A 'Z' указывает, что часовой пояс соответствует стандарту часового пояса RFC 822. -0800.

Мне кажется, вам нужен DatatypeConverter...

@Test
public void testTimezoneIsGreenwichMeanTime() throws ParseException {
    final Calendar calendar = javax.xml.bind.DatatypeConverter.parseDateTime("2010-04-05T17:16:00Z");
    TestCase.assertEquals("gotten timezone", "GMT+00:00", calendar.getTimeZone().getID());
}

Ответ 2

Java не корректно обрабатывает даты ISO.

Как и Маккензи, ответьте.

Просто исправьте Z перед синтаксическим разбором.

код

String string = "2013-03-05T18:05:05.000Z";
String defaultTimezone = TimeZone.getDefault().getID();
Date date = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).parse(string.replaceAll("Z$", "+0000"));

System.out.println("string: " + string);
System.out.println("defaultTimezone: " + defaultTimezone);
System.out.println("date: " + (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).format(date));

Результат

string: 2013-03-05T18:05:05.000Z
defaultTimezone: America/New_York
date: 2013-03-05T13:05:05.000-0500

Ответ 3

Дата разбора в формате ISO8601.

В java 7 шаблон для чтения и применения суффикса часового пояса должен читать yyyy-MM-dd'T'HH:mm:ssX

Ответ 4

TL;DR

Instant.parse ( "2010-04-05T17:16:00Z" )

Стандарт ISO 8601

Ваша строка соответствует стандарту ISO 8601 (из которых упомянутый RFC 3339 является профилем).

Избегайте j.u.Date

Классы java.util.Date и .Calendar в комплекте с Java, как известно, являются неприятными. Избегайте их.

Вместо этого используйте либо библиотеку Joda-Time, либо новый пакет java.time в Java 8. Оба используют ISO 8601 в качестве значений по умолчанию для синтаксического анализа и генерации строковых представлений значений даты и времени.

java.time

Структура java.time, встроенная в Java 8 и более поздняя версия, вытесняет неприятные старые классы java.util.Date/.Calendar. Новые классы вдохновлены очень успешной структурой Joda-Time, предназначенной в качестве ее преемника, аналогичной по своей концепции, но переориентированной. Определено JSR 310. Расширено в проекте ThreeTen-Extra. См. Tutorial.

Класс Instant в java.time представляет момент на временной шкале в часовой пояс UTC.

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

String input = "2010-04-05T17:16:00Z";
Instant instant = Instant.parse ( input );

Дамп для консоли.

System.out.println ( "instant: " + instant );

instant: 2010-04-05T17: 16: 00Z

Оттуда вы можете применить часовой пояс (ZoneId), чтобы настроить этот Instant в ZonedDateTime. Поиск для обсуждения и примеров.

Если вы должны использовать объект java.util.Date, вы можете преобразовать, вызвав новые методы преобразования, добавленные в старые классы, такие как статические метод java.util.Date.from( Instant ).

java.util.Date date = java.util.Date.from( instant );

Joda времени

Пример в Joda-Time 2.5.

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" ):
DateTime dateTime = new DateTime( "2010-04-05T17:16:00Z", timeZone );

Конвертировать в UTC.

DateTime dateTimeUtc = dateTime.withZone( DateTimeZone.UTC );

При необходимости конвертируйте в java.util.Date.

java.util.Date date = dateTime.toDate();

Ответ 5

В соответствии с последней строкой в таблице шаблонов даты и времени API Java 7

X Часовой пояс ISO 8601 часовой пояс -08; -08 00; -08: 00

Для часового пояса ISO 8601 вы должны использовать:

  • Х для (-08 или Z),
  • XX для (-08 00 или Z),
  • XXX для (-08: 00 или Z);

так что для разбора вашего "2010-04-05T17: 16: 00Z" вы можете использовать "гггг-ММ-дд'ТХЧ: мм: ссХ" или "гггг-ММ-дд'Т'ХХ: мм: ссХХ" или "гггг-мм-дд'т'ч: мм: ссххх".

    System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse("2010-04-05T17:16:00Z"));
    System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX").parse("2010-04-05T17:16:00Z"));
    System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").parse("2010-04-05T17:16:00Z"));

правильно распечатает "Пн Апр 05 13:16:00 ПО ВОСТОЧНОМУ ВРЕМЕНИ 2010"

Ответ 6

"X" работает только в том случае, если частичных секунд нет: например, шаблон SimpleDateFormat

"YYYY-MM-dd'T'HH: мм: SSX"

Будет правильно разбираться

"2008-01-31T00: 00: 00Z"

но

"YYYY-MM-dd'T'HH: мм: ss.SX"

НЕ будет анализировать

"2008-01-31T00: 00: 00.000Z"

Грустно, но верно, дата-время с частичными секундами не является действительной датой ISO: http://en.wikipedia.org/wiki/ISO_8601

Ответ 7

Часовой пояс должен быть чем-то вроде "GMT + 00: 00" или 0000, чтобы правильно разбираться с SimpleDateFormat - вы можете заменить Z этой конструкцией.

Ответ 8

Проект restlet включает класс InternetDateFormat, который может анализировать даты RFC 3339.

Restlet InternetDateFormat

Хотя вы можете просто заменить конечную "Z" на "UTC" перед ее анализом.

Ответ 9

Я предоставляю другой ответ, который я нашел api-client-library Google

try {
    DateTime dateTime = DateTime.parseRfc3339(date);
    dateTime = new DateTime(new Date(dateTime.getValue()), TimeZone.getDefault());
    long timestamp = dateTime.getValue();  // get date in timestamp
    int timeZone = dateTime.getTimeZoneShift();  // get timezone offset
} catch (NumberFormatException e) {
    e.printStackTrace();
}

Руководство по установке,
https://developers.google.com/api-client-library/java/google-api-java-client/setup#download

Вот ссылка на API,
https://developers.google.com/api-client-library/java/google-http-java-client/reference/1.20.0/com/google/api/client/util/DateTime

Исходный код DateTime Класс,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/main/java/com/google/api/client/util/DateTime.java

DateTime модульные тесты,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java#L121

Ответ 10

Что касается JSR-310, то другой интересный проект может быть threetenbp.

JSR-310 предоставляет новую библиотеку даты и времени для Java SE 8. Этот проект является backport для Java SE 6 и 7.

Если вы работаете над проектом Android, вы можете проверить библиотеку ThreeTenABP.

compile "com.jakewharton.threetenabp:threetenabp:${version}"

JSR-310 был включен в Java 8 как пакет java.time. *. Это полная замена для опасных API дат и календаря как на Java, так и на Android. JSR-310 был передан на Java 6 его создателем, Стивеном Коулборном, из которого адаптирована эта библиотека.

Ответ 11

В Java 8 используйте предопределенный DateTimeFormatter.ISO_DATE_TIME

 DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
 ZonedDateTime result = ZonedDateTime.parse("2010-04-05T17:16:00Z", formatter);

Я думаю, что это самый простой способ

Ответ 12

Так как java 8 просто использует ZonedDateTime.parse("2010-04-05T17:16:00Z")