java .time и ThreeTenABP
Мое решение состоит в том, чтобы создать три форматера для трех возможных форматов ввода, а затем для каждого ввода попробовать преобразователи по очереди. Для простой демонстрации идеи:
DateTimeFormatter[] inputFormatters = {
DateTimeFormatter.ISO_INSTANT,
DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss xx yyyy", Locale.ROOT),
new DateTimeFormatterBuilder()
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter()
};
DateTimeFormatter displayFormatter
= DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.US);
for (String inputString : new String[] {
"2020-01-25T21:59:27Z",
"Sat Jan 25 20:06:07 +0000 2020",
"1566777888999"
}) {
// try the formatters in turn and see which one works
for (DateTimeFormatter formatter : inputFormatters) {
try {
ZonedDateTime dateTime = formatter.parse(inputString, Instant.FROM)
.atZone(ZoneId.systemDefault());
System.out.format("%-30s was parsed to %s%n",
inputString, dateTime.format(displayFormatter));
break;
} catch (DateTimeParseException ignore) {
// Ignore, try next format
}
}
}
В моем часовом поясе (Европа / Копенгаген) выход из этого фрагмента:
2020-01-25T21:59:27Z was parsed to Jan 25, 2020
Sat Jan 25 20:06:07 +0000 2020 was parsed to Jan 25, 2020
1566777888999 was parsed to Aug 26, 2019
Так как это Никогда одна и та же дата во всех часовых поясах не зависит от часового пояса.
Я рекомендую java .time, современный Java API даты и времени. Я видел, что вы пометили вопрос как простой формат, но класс SimpleDateFormat
общеизвестно проблематичен и давно устарел, поэтому я рекомендую не использовать его. И я использую тот факт, что ваш первый формат соответствует стандарту ISO 8601, и что java .time имеет встроенный форматер для него, DateTimeFormatter.ISO_INSTANT
.
Мой третий входной форматер, один для длинных значение, рассматривает последние три символа как миллисекунды секунды, а все перед ним как секунды с начала эпохи. Результат net состоит в том, что он анализирует миллисекунды с начала эпохи. DateTimeFormatterBuilder
требовалось для создания этого форматера.
Решение без lib
Я признаю, что мне не хочется писать это. Я бы действительно надеялся, что вы сможете избежать печально известного класса SimpleDateFormat
и его давно устаревших друзей, таких как Date
. Так как я понимаю, что ваше приложение не-lib, как Joda-Time, так и ThreeTenABP, похоже, не обсуждаются. Сожалею. В этом случае, поскольку SimpleDateFormat
не может проанализировать длинную строку, мой подход состоит в том, чтобы попробовать строку и определить формат и выбрать способ синтаксического анализа на основе этого.
DateFormat inputIso = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
// This format resembles the output from Date.toString
DateFormat inputDatelike
= new SimpleDateFormat("EEE MMM dd HH:mm:ss ZZZ yyyy", Locale.ROOT);
DateFormat displayFormat
= DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US);
displayFormat.setTimeZone(TimeZone.getDefault());
for (String inputString : new String[] {
"2020-01-25T21:59:27Z",
"Sat Jan 25 20:06:07 +0000 2020",
"1566777888999"
}) {
Date parsedDate;
if (Character.isDigit(inputString.charAt(0))) {
if (inputString.contains("-")) {
parsedDate = inputIso.parse(inputString);
} else {
// long number of millis
parsedDate = new Date(Long.parseLong(inputString));
}
} else {
parsedDate = inputDatelike.parse(inputString);
}
System.out.format("%-30s was parsed to %s%n",
inputString, displayFormat.format(parsedDate));
}
Вывод точно такой же, как и раньше:
2020-01-25T21:59:27Z was parsed to Jan 25, 2020
Sat Jan 25 20:06:07 +0000 2020 was parsed to Jan 25, 2020
1566777888999 was parsed to Aug 26, 2019
Обратите внимание, что здесь неверный ввод может вызвать либо NumberFormatException
, либо ParseException
, так что ловите оба. И прибегайте к этому решению только в том случае, если нет способа избежать этого.
Строка displayFormat.setTimeZone(TimeZone.getDefault());
технически излишня, но она явно указывает на то, что вывод зависит от часового пояса, и, возможно, что еще более важно, он говорит вам, где вам нужно изменить код, если вы хотите выводить данные в другом часовом поясе.
Вопрос: java .time не требует Android API-уровень 26?
java .time прекрасно работает как на старых, так и на новых Android устройствах. Для этого требуется как минимум Java 6 .
- В Java 8 и более поздних версиях, а также на более новых Android устройствах (начиная с уровня API 26) современный встроенный API -в. Только в этом случае используйте ссылку на метод
Instant::from
вместо константы Instant.FROM
. - В не Android Java 6 и 7 получите ThreeTen Backport, бэкпорт современных классов (ThreeTen для JSR 310 см. ссылки внизу.
- Вкл. (более старая версия) Android Используйте версию Android ThreeTen Backport. Это называется ThreeTenABP. И убедитесь, что вы импортируете классы даты и времени из
org.threeten.bp
с подпакетами.
Ссылки