Как обрабатывать гггг-мм и гггг в java8 с помощью DateTimeFormatter

Я использую SimpleDateFormat для форматирования или проверки дат, но я хотел бы сделать его поточно-ориентированным с помощью Java 8 DateTimeFormatter. У меня проблемы с выполнением некоторых требований.

Мое приложение принимает только три типа форматов. «гггг-ММ-дд», «гггг-ММ», «гггг»

Existing Code gives me desired output:
/*simple date format to process yyyy-MM-dd format
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd")
/*simple date format to process yyyy-MM format
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM")

/*simple date format to process yyyy format
SimpleDateFormat simpleDateFormat3 = new SimpleDateFormat("yyyy")

/* to parse input
simpleDateFormat.parse(input)
/* to format
simpleDateFormat.format(simpleDateFormat1)

Вот ввод и ожидаемый результат:

  input             expected
'2018-03-19'       '2018-03-19'
'2018-03'          '2018-03'
'2018'             '2018'
'2017-02-54'       '2017-02'
'2016-13-19'       '2016'
  1. Как я могу добиться того же результата в Java 8 DateTimeFormaenter code heretter?

    / * форматирование даты и времени java 8 DateTimeFormatter dateTimeFormatter = new DateTimeFormatter ("yyyy-MM-dd")

Приведенный выше фрагмент работает, когда все значения года, месяца и даты верны. Любая помощь будет высоко ценится.


person Prakash    schedule 11.09.2019    source источник
comment
Я не понимаю, почему у вас есть эти специфические требования, а также не понимаю, как старый опубликованный вами код может дать желаемые результаты. Если вы объясните глубже, мы сможем предложить лучшее. При любых обстоятельствах поздравляю с решением перейти на java.time, современный API даты и времени Java.   -  person Ole V.V.    schedule 12.09.2019


Ответы (2)


Я использую SimpleDateFormat для форматирования или проверки дат

Никогда не используйте SimpleDateFormat.

Ужасные классы даты и времени, связанные с самыми ранними версиями Java, много лет назад были вытеснены современными классами java.time, определенными в JSR 310.

потокобезопасность с использованием Java 8 DateTimeFormatter

Да, в отличие от устаревших классов даты и времени, классы java.time используют неизменяемый объекты и являются поточно-ориентированными по своей конструкции.

Вот ввод и ожидаемый результат:

Некоторые из ваших входов можно определить просто по их длине.

// Ten-digits long, assume ISO 8601 date.
LocalDate ld = LocalDate.parse( "2018-03-19" ) ;

// Seven digits long, assume ISO 8601 year-month.
YearMonth ym = YearMonth.parse( "2018-03" ) ;

// Four digits, assume year.
Year y = Year.parse( "2018" ) ;

Обратите внимание, что все указанные выше входные данные соответствуют ISO 8601. Классы java.time по умолчанию используют форматы ISO 8601 при синтаксическом анализе / генерации строк. Поэтому нет необходимости указывать шаблон форматирования. И поэтому нет необходимости в явном DateTimeFormatter объекте.

'2017-02-54' '2017-02'

Этот пример меня озадачивает. Если вы имеете в виду встречу с датой с недопустимым днем ​​месяца, просто используйте год и месяц, игнорируя день, я полагаю, вы сможете это сделать. Посмотрите на снисходительный режим на DateTimeFormatter. Возможно, используйте DateTimeFormatterBuilder для создания гибкого DateTimeFormatter. Но, честно говоря, я бы отклонил такие данные как ошибочные вводы. Получение надежных данных должно быть задачей издателя данных, а не работы потребителя - угадывать намерения, стоящие за ошибочными данными.

ожидается ввод

'2016-13-19' '2016'

Опять же, пытаясь угадать правильные части неверных входов в опасной игре, я бы не стал играть. Если месяц и день недействительны, как узнать, что год действителен? Хуже того, если издатель этих данных может выдать такие ошибочные данные, как вы узнаете, что явно действительный ввод 2018-03-19 действительно правильный? Если месяц 13 является ошибкой, как узнать, что ввод с месяцем 03 также не является ошибкой?

Расскажите издателю об этих проблемных данных о стандарте ISO 8601 и попросите его исправить свои ошибки. .

person Basil Bourque    schedule 11.09.2019

Как и Бэзил Бурк в другом ответе, я не обязательно убежден, что то, о чем вы просите, также послужит вам лучше всего. В любом случае, в качестве небольшого дополнения к этому хорошему ответу, я хотел бы представить подход к работе с двумя последними случаями, недействительными датами, как вы сказали.

Для проверки формата я сначала разбираю строки без проверки чисел. Это отклонит строки, которые не принадлежат ни одному из ваших трех форматов, например 2016-03-19T12:00 или 2016 and some nonsense. Об этом позаботится метод DateTimeFormatter.parseUnresolved. В случае ошибки синтаксического анализа этот метод устанавливает индекс ошибки в объекте ParsePosition, который мы ему передали, и возвращает null (поэтому не вызывает никаких исключений). Поэтому я проверяю, возвращается ли null.

private static DateTimeFormatter yearMonthFormatter = DateTimeFormatter.ofPattern("uuuu-MM")
        .withResolverStyle(ResolverStyle.STRICT);
private static DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("uuuu")
        .withResolverStyle(ResolverStyle.STRICT);

public static Temporal parse(String input) {
    // First try the three formats, uuuu-MM-dd, uuuu-MM, uuuu, in turn without resolving
    TemporalAccessor parsed = null;
    for (DateTimeFormatter formatter : Arrays.asList(DateTimeFormatter.ISO_LOCAL_DATE,
            yearMonthFormatter, yearFormatter)) {
        ParsePosition position = new ParsePosition(0);
        TemporalAccessor parseAttempt = formatter.parseUnresolved(input, position);
        if (parseAttempt != null && position.getIndex() == input.length()) {
            // Success; exit loop
            parsed = parseAttempt;
            break;
        }
    }
    if (parsed == null) { // didn’t match any of the three formats
        throw new IllegalArgumentException("Invalid format: " + input);
    }

    // Try resolving to either LocalDate, YearMonth or Year
    try {
        return LocalDate.of(parsed.get(ChronoField.YEAR),
                parsed.get(ChronoField.MONTH_OF_YEAR),
                parsed.get(ChronoField.DAY_OF_MONTH));
    } catch (DateTimeException dteRLd) {
        try {
            return YearMonth.of(parsed.get(ChronoField.YEAR),
                    parsed.get(ChronoField.MONTH_OF_YEAR));
        } catch (DateTimeException dteRYm) {
            return Year.of(parsed.get(ChronoField.YEAR));
        }
    }
}

Давайте попробуем ваши примеры:

    String[] inputs = {
            "2018-03-19",
            "2018-03",
            "2018",
            "2017-02-54",
            "2016-13-19"
    };

    for (String input : inputs) {
        Temporal parsed = parse(input);
        System.out.format("%-10s  %-10s  %s%n", input, parsed, parsed.getClass().getName());
    }

Выход:

2018-03-19  2018-03-19  java.time.LocalDate
2018-03     2018-03     java.time.YearMonth
2018        2018        java.time.Year
2017-02-54  2017-02     java.time.YearMonth
2016-13-19  2016        java.time.Year
person Ole V.V.    schedule 12.09.2019