Разобрать java.time, пробуя несколько шаблонов - PullRequest
0 голосов
/ 22 марта 2019

У нас есть библиотека, где пользователи могут передавать даты в нескольких форматах.Они следуют ISO, но иногда сокращаются.

Таким образом, мы получаем такие вещи, как «19-3-12» и «2019-03-12T13: 12: 45.1234», где дробные секунды могут быть от 1 до 7 цифр.долго.Это очень большое количество комбинаций.

DateTimeFormatter.parseBest не работает, потому что он не примет «yy-md» для локальной даты. Решения здесь не будут работать, потому что это предполагает, что мы знаем шаблон - мы не знаем.

И говорить людям, что их строковые форматы «правильные», не будут работать, так как естьтонны существующих данных (в основном это файлы XML и JSON).

Мой вопрос: как я могу проанализировать строки, поступающие в эти различные шаблоны, без необходимости использовать 15 различных явных шаблонов?

Или, что еще лучше, есть ли способ проанализировать строку, и он попробует все возможное и вернет временный объект, если строка имеет смысл для любой даты [времени]?

Ответы [ 2 ]

0 голосов
/ 23 марта 2019

Без полной спецификации трудно дать точную рекомендацию. Методы, обычно используемые для переменных форматов, включают:

  1. Попробуем несколько известных форматов по очереди.
  2. Необязательные части в шаблоне формата.
  3. DateTimeFormatterBuilder.parseDefaulting() для частей, которые могут отсутствовать в проанализированной строке.
  4. Как вы знаете, parseBest.

Я предполагаю, что y-M-d всегда идут в таком порядке (никогда не M-d-y или d-M-y, например). 19-3-12 противоречит стандарту ISO 8601, поскольку стандарт требует (как минимум) четырехзначного года и двухзначного месяца. Задача с двузначным годом - угадать столетие: это 1919 или 2019 год или 2119?

Хорошая новость: наличие и отсутствие секунд и различное количество дробных цифр являются встроенными и не создают проблем.

Из того, что вы сказали нам, мне кажется, что следующее - это честный выстрел.

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendPattern("[uuuu][uu]-M-d")
            .optionalStart()
            .appendLiteral('T')
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .optionalEnd()
            .toFormatter();

    TemporalAccessor dt = formatter.parseBest("19-3-12", LocalDateTime::from, LocalDate::from);
    System.out.println(dt.getClass());
    System.out.println(dt);

Выход:

class java.time.LocalDate
2019-03-12

Я полагаю, что он должен работать с вариантами форматов, которые вы описываете. Давайте просто попробуем другой пример:

    dt = formatter.parseBest( "2019-03-12T13:12:45.1234", LocalDateTime::from, LocalDate::from);
    System.out.println(dt.getClass());
    System.out.println(dt);
class java.time.LocalDateTime
2019-03-12T13:12:45.123400

Для контроля интерпретации двухзначного года вы можете использовать один из перегруженных вариантов DateTimeFormatterBuilder.appendValueReduced(). Я рекомендую вам рассмотреть проверку диапазона сверху.

0 голосов
/ 22 марта 2019

Попытка всех возможных форматов будет работать хуже, чем попытка только 15.

Вы можете попытаться «нормализовать» один формат, но тогда вы выполняете работу, которую должны выполнять эти 15 форматов.

Я думаю, что лучший подход - тот, который описан @JB Nizet, - пробовать только шаблоны, которые соответствуют длине строки.

public Date parse(String openFormat) { 
    String[] formats = {"YYY-MM-DD"};
    switch(openFormat.length()) {
       case 24: // 2019-03-12T13:12:45.1234
             formats = new String[]{"YYY-MM-DDThh:mm:ssetcetc", }; // all the formats for length 24
             break;
       ...
       case 6: //YYY-MM-DD, DD-MM-YYYY
             formats = new String[]{YYY-MM-DD", "DD-MM-YYYY", }; // all the formats for length 6
             break;
      }
      Date myDate
      // now try the reduced number of formats, possibly only 1 or 2
      for( String format : formats) try {
          myDate = date parse ( format ) etcetc
      } catch (DateFormatException d) {
          continue;
      } 
      if (myDate == null){
         throw InvalidDate
      } else {
      return myDate
      }
 }
...