Как проанализировать RFC 3339 datetimes с Java?
Я пытаюсь проанализировать дату, возвращаемую как значение из HTML5 datetime
поле ввода. Попробуйте это в Opera, чтобы увидеть пример. Возвращаемая дата выглядит следующим образом:2011-05-03T11:58:01Z
.
Я хотел бы разобрать это в Java Date или Calendar объект.
В идеале решение должно иметь следующие вещи:
- нет внешних библиотек (jars)
- обрабатывает все приемлемые форматы RFC 3339
- строка должна быть легко проверено, чтобы узнать, является ли это допустимой датой RFC 3339
7 ответов
только что обнаружил, что google реализовал синтаксический анализатор Rfc3339 в клиентской библиотеке Google HTTP
проверено. Он хорошо работает для разбора варьируется sub секунд фрагмента времени.
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");
}
Итак, в принципе это будет сделано с использованием разных SimpleDateFormat шаблоны.
вот список шаблонов для индивидуальные декларации в RFC 3339:
- дата-fullyear:
yyyy
- дата-месяц:
MM
- дата-mday:
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
С нуля (с использованием регулярных выражений, для простоты или разбора вручную, для эффективности).
tl; dr
Instant.parse( "2011-05-03T11:58:01Z" )
ISO 8601
на самом деле RFC 3339 это всего лишь профиль фактического стандарта,ISO 8601.
RFC отличается тем, что он намеренно нарушает ISO 8601, чтобы позволить отрицательное смещение нулевых часов (-00:00
) и дает это семантическое значение "смещение неизвестно". Эта семантика кажется мне очень плохой идеей. Я советую придерживаться более разумных правил ISO 8601. В ISO 8601, имея отсутствие смещения вообще означает, что смещение неизвестно-очевидное значение, тогда как правило RFC является заумным.
современные java.время классы используют форматы ISO 8601 по умолчанию при разборе / генерации строк.
ваша входная строка представляет момент в UTC. The 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
, теперь режим обслуживания, советует миграцию в java.время классы.
чтобы узнать больше, см. В Oracle Учебник. И search Stack Overflow для многих примеров и объяснений. Спецификация JSR 310.
вы можете обменять java.время объекты непосредственно с вашей базой данных. Используйте драйвер JDBC соответствуют JDBC 4.2 или позже. Нет необходимости в строках, нет необходимости в java.sql.*
классы.
где получить java.время занятий?
-
Java SE 8, Java SE 9, и позже
- встроенный.
- часть стандартного API Java в комплекте реализация.
- Java 9 добавляет некоторые незначительные функции и исправления.
-
Java SE 6 и Java SE 7
- большая часть java.функциональность времени обратно портирована на Java 6 & 7 в ThreeTen-Backport.
-
Android
- версии Android bundle реализации java.классы время.
- для более раннего Android ( ThreeTenABP проект приспосабливается ThreeTen-Backport (упоминалось выше). См.как использовать ThreeTenABP....
на ThreeTen-Extra проект расширяет java.время с дополнительными занятиями. Этот проект является полигон для возможных будущих дополнений к Java.время. Здесь вы можете найти полезные классы, такие как Interval
, YearWeek
, YearQuarter
и больше.
здесь простой способ сделать это. Это может удовлетворить ваши потребности.
может быть, не самый элегантный способ, но, конечно, работает тот, который я недавно сделал:
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
cal.setTime(sdf.parse(dateInString.replace("Z", "").replace("T", "-")));
с форматом, который у вас есть, например, 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());