как отформатировать строку даты из ввода может иметь несколько форматов строки - PullRequest
0 голосов
/ 15 марта 2020

Наличие строки даты ввода может быть возможно как

"2020-01-25T21:59:27Z"

или

"Sat Jan 25 20:06:07 +0000 2020"

или Long

, и ожидайте, что одна будет отображаться как

Jan 25, 2020

как получить желаемую отформатированную строку даты?

обновление:

@ Ole VV предоставил очень хорошее предложение, его просто нельзя применить с android lib case.

но я думаю, что нет единого формата для всех этих трех случаев, так что попробуйте один за другим. например, для ISO8601 сделать что-то вроде:

    return try { 
            val dateStr = "Sat Jan 25 20:06:07 +0000 2020". //ISO8601
            val format = SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.getDefault())
            val dsipFormat = SimpleDateFormat("MMM dd yyyy", Locale.getDefault()) // for display result
            val date = format.parse(dateStr) // parse it to date
            dsipFormat.format(date) // returning the display result
            } catch (e: Exception) {
                Log.e("+++", "+++ error: $e")
                ""
            }

Если есть лучший подход?

1 Ответ

1 голос
/ 15 марта 2020

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 с подпакетами.

Ссылки

...