Как я могу разобрать даты RFC 3339 с Java? - PullRequest
23 голосов
/ 18 мая 2011

Я пытаюсь разобрать возвращаемую дату как значение из поля ввода HTML5 datetime. Попробуйте в Opera, чтобы увидеть пример. Возвращенная дата выглядит следующим образом: 2011-05-03T11:58:01Z.

Я бы хотел разобрать это в Java Date или Calendar Object.

В идеале решение должно иметь следующие вещи:

  • Нет внешних библиотек (банок)
  • Обрабатывает все приемлемые форматы RFC 3339
  • Строка должна легко проверяться, чтобы увидеть, является ли она действительной датой RFC 3339

Ответы [ 7 ]

14 голосов
/ 04 апреля 2018

ТЛ; др

Instant.parse( "2011-05-03T11:58:01Z" )

ISO 8601

На самом деле, RFC 3339 является всего лишь самопровозглашенным «профилем» действующего стандарта, ISO 8601 .

RFC отличается тем, что он преднамеренно нарушает ISO 8601, допуская отрицательное смещение нулевых часов (-00:00), и придает этому смысловое значение «смещение неизвестно». Эта семантика кажется мне очень плохой идеей. Я советую придерживаться более разумных правил ISO 8601. В ISO 8601 отсутствие смещения вообще означает, что смещение неизвестно - очевидное значение, тогда как правило RFC является заумным.

Современные классы java.time по умолчанию используют форматы ISO 8601 при разборе / генерации строк.

Ваша входная строка представляет момент в UTC. Z в конце - это сокращение от Zulu и означает UTC.

Instant (не Date)

Современный класс Instant представляет момент в UTC. Этот класс заменяет java.util.Date и использует более высокое разрешение, чем наносекунды, а не миллисекунды.

Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;

ZonedDateTime (не Calendar)

Чтобы увидеть тот же самый момент через часы настенного времени, используемые людьми определенного региона (часового пояса), примените ZoneId, чтобы получить ZonedDateTime. Этот класс ZonedDateTime заменяет класс java.util.Calendar.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, same point on the timeline, different wall-clock time.

Преобразование

Я настоятельно рекомендую по возможности избегать устаревших классов даты и времени. Но если вам нужно взаимодействовать со старым кодом, который еще не обновлен до java.time , вы можете конвертировать туда и обратно. Вызовите новые методы, добавленные к old классам.

Instant заменяет java.util.Date.

java.util.Date myJUDate = java.util.Date.from( instant ) ;  // From modern to legacy.
Instant instant = myJUDate.toInstant() ;                    // From legacy to modern.

ZonedDateTime заменяет GregorianCalendar.

java.util.GregorianCalendar myGregCal = java.util.GregorianCalendar.from( zdt ) ;  // From modern to legacy.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

Если у вас есть java.util.Calendar, который на самом деле GregorianCalendar, произнесите.

java.util.GregorianCalendar myGregCal = ( java.util.GregorianCalendar ) myCal ;  // Cast to the concrete class.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

маркированные проблемы

Относительно конкретных вопросов вашего Вопроса ...

  • Нет внешних библиотек (jars)

Классы java.time встроены в Java 8, 9, 10 и более поздние версии. Реализация также включена в более позднюю версию Android. Для более ранней версии Java и более ранней версии Android см. Следующий раздел этого ответа.

  • Обрабатывает все приемлемые форматы RFC 3339

Различные классы java.time обрабатывают все известные мне форматы ISO 8601. Они даже обрабатывают некоторые форматы, которые таинственным образом исчезли из более поздних выпусков стандарта.

Для других форматов см. Методы parse и toString различных классов, таких как LocalDate, OffsetDateTime и т. Д. Кроме того, выполните поиск переполнения стека, так как имеется много примеров и обсуждений на эту тему.

  • Строка должна легко проверяться, чтобы увидеть, является ли она действительной датой RFC 3339

Для проверки входных строк, ловушка для DateTimeParseException.

try {
    Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;
} catch ( DateTimeParseException e ) {
    … handle invalid input
}

О java.time

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

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

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

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

Где получить классы java.time?

  • Java SE 8 , Java SE 9 и выше
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функций java.time перенесена на Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии Android связывают реализации классов java.time.
    • Для более ранних версий Android (<26) <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP Проект адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

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

13 голосов
/ 18 мая 2011

Итак, в принципе это можно сделать, используя различные SimpleDateFormat шаблоны.

Вот список шаблонов для отдельных объявлений в RFC 3339 :

  • дата-год: yyyy
  • дата-месяц: MM
  • дата-день: dd
  • время-час: HH
  • минутное время: mm
  • время-секунда: ss
  • time-secfrac: .SSS (S означает миллисекунду, хотя - не ясно, что произойдет, если их будет больше или меньше 3 цифр.)
  • time-numoffset: (например, +02:00, похоже, не поддерживается - вместо этого он поддерживает форматы +0200, GMT+02:00 и некоторые именованные часовые пояса с использованием z и Z.)
  • смещение по времени: 'Z' (не поддерживает другие часовые пояса) - вы должны использовать format.setTimezone(TimeZone.getTimeZone("UTC")) перед использованием.)
  • неполное время: HH:mm:ss или HH:mm:ss.SSS.
  • полный рабочий день: HH:mm:ss'Z' или HH:mm:ss.SSS'Z'.
  • полная дата: yyyy-MM-dd
  • дата-время: yyyy-MM-dd'T'HH:mm:ss'Z' или yyyy-MM-dd'T'HH:mm:ss.SSS'Z'

Как мы видим, кажется, что это не в состоянии проанализировать все. Возможно, было бы лучше реализовать RFC3339DateFormat с нуля (используя регулярные выражения, для простоты или синтаксического анализа вручную, для эффективности).

6 голосов
/ 03 апреля 2018

Только что обнаружил, что google реализовал парсер Rfc3339 в клиентской библиотеке Google HTTP

https://github.com/google/google-http-java-client/blob/dev/google-http-client/src/main/java/com/google/api/client/util/DateTime.java

Протестировано.Это хорошо работает, чтобы разобрать изменяющийся фрагмент времени секунд.

import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import com.google.api.client.util.DateTime;

DateTimeFormatter formatter = DateTimeFormatter
            .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            .withZone(ZoneId.of("UTC"));

@Test
public void test1e9Parse() {
    String timeStr = "2018-04-03T11:32:26.553955473Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void test1e3Parse() {
    String timeStr = "2018-04-03T11:32:26.553Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void testEpochSecondsParse() {

    String timeStr = "2018-04-03T11:32:26Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.000Z");
}
3 голосов
/ 10 января 2017

Возможно, не самый элегантный способ, но, безусловно, рабочий, который я недавно сделал:

Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
cal.setTime(sdf.parse(dateInString.replace("Z", "").replace("T", "-")));
3 голосов
/ 18 мая 2011

Здесь - простой способ сделать это.Это может удовлетворить ваши потребности.

2 голосов
/ 16 августа 2011

В том формате, который у вас есть, например. 2011-05-03T11: 58: 01Z, ниже код подойдет. Тем не менее, я недавно попробовал html5 datetime в Chrome и Opera, и это дает мне 2011-05-03T11: 58Z -> нет части ss, которая не может быть обработана с помощью кода ниже.

new Timestamp(javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar(date).toGregorianCalendar().getTimeInMillis());
0 голосов
/ 14 февраля 2018
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(datetimeInFRC3339format)
...