Я использую Android, JUnit и SimpleDateFormat.

Мне нравится делать некоторые преобразования между String и Date в формате rfc3339 с часовым поясом.

Например: 2017-12-31T23:59:59+02:00, но не 2017-12-31T23:59:59+0200.

Android SimpleDateFormat использует шаблон Z, но не использует шаблон X (см. RFC3339_REGEX_1 ниже).

Но в моем тесте JUnit (jdk1.8) RFC3339_REGEX_1 дает мне не правильный формат (+02:00), а +0200. Тест JUnit хорошо работает с RFC3339_REGEX_2.

Итак, как я могу сделать что-то чистое, которое хорошо работает как на устройстве Android, так и в тесте JUnit?

public static final String RFC3339_REGEX_1 = "yyyy-MM-dd'T'HH:mm:ssZ"; //android
public static final String RFC3339_REGEX_2 = "yyyy-MM-dd'T'HH:mm:ssXXX"; //java
1
Anh-Tuan Mai 24 Июл 2017 в 17:35

1 ответ

Лучший ответ

Старые классы (Date, Calendar и SimpleDateFormat) имеют множество проблем и проблемы с дизайном, и они заменяются новыми API. И если я не ошибаюсь, шаблон X не работает во всех версиях (я знаю, что в JDK 6 не поддерживался, он был представлен только в JDK 7; не знаю, как это повлияет на разные версии Android).

В качестве альтернативы в Android вы можете использовать ThreeTen Backport. Чтобы он работал, вам также понадобится ThreeTenABP (см. Как его использовать здесь ).

Однако, если вам все еще нужно работать с java.util.Date, этот API может упростить вам задачу. Если вам нужен такой формат, как 2017-12-31T23:59:59+02:00, вы можете преобразовать java.util.Date в org.threeten.bp.OffsetDateTime (используя класс org.threeten.bp.DateTimeUtils для преобразования значений и org.threeten.bp.ZoneOffset для установки смещение до +02:00):

// assuming that java.util.Date has value equivalent to 2017-12-31T23:59:59+02:00
Date date = ...
// convert to OffsetDateTime (use +02:00 offset)
OffsetDateTime odt = DateTimeUtils.toInstant(date).atOffset(ZoneOffset.of("+02:00"));
System.out.println(odt); // 2017-12-31T23:59:59+02:00

Результат будет:

2017-12-31T23: 59: 59 + 02: 00

Обратите внимание, что это результат odt.toString() (который называется неявным по System.out.println), и по умолчанию он соответствует желаемому формату. Но вы также можете применить определенный формат, используя org.threeten.bp.format.DateTimeFormatter:

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
System.out.println(fmt.format(odt));

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

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


Чтобы преобразовать OffsetDateTime обратно в Date, вы также можете использовать DateTimeUtils:

// convert back to java.util.Date
date = DateTimeUtils.toDate(odt.toInstant());

Если вам не нужно работать с java.util.Date, вы можете использовать OffsetDateTime напрямую:

// create OffsetDateTime with value = 2017-12-31T23:59:59+02:00
OffsetDateTime odt = OffsetDateTime.of(2017, 12, 31, 23, 59, 59, 0, ZoneOffset.ofHours(2));

Также обратите внимание, что я использовал ZoneOffset.ofHours(2), что эквивалентно ZoneOffset.of("+02:00").

Вы также можете получить текущую дату, используя OffsetDateTime.now(ZoneOffset.ofHours(2)).


ThreeTen Backport - это резервный порт для новых классов даты и времени Java 8, и он имеет много разных типов даты и времени (например, OffsetDateTime выше), которые можно использовать в различных ситуациях. Ознакомьтесь с этим руководством для получения дополнительной информации о том, какой тип лучше всего подходит для вашего case - в руководстве рассматривается Java 8, но в ThreeTen Backport те же имена классов и методов, только пакет отличается: org.threeten.bp вместо java.time.

2
24 Июл 2017 в 19:41
Во время Joda у нас есть класс AbstractDateTime, который обеспечивает общее поведение для классов datetime, я не нашел корреспондента в threeten (обеспечивая LocalDateTime, ZonedDateTime и OffsetDateTime, но не абстрактный класс)?
 – 
Anh-Tuan Mai
25 Июл 2017 в 12:52
@ Anh-TuanMai Какое общее поведение вам нужно? В зависимости от того, что вам нужно, вы можете использовать интерфейсы Temporal или TemporalAccessor.
 – 
user7605325
25 Июл 2017 в 12:56
Мне нравится иметь что-то вроде BaseDateTime.getMillis () во времени Joda и конструктор по миллисекундам :)
 – 
Anh-Tuan Mai
25 Июл 2017 в 13:07
1
@ Anh-TuanMai Но чтобы работать с миллисекундами, обратите внимание на класс Instant
 – 
user7605325
25 Июл 2017 в 13:41
1
Спасибо, Instant класс - это разрешение.
 – 
Anh-Tuan Mai
25 Июл 2017 в 14:30