Java 8 DateTimeFormatter с необязательной частью без секунд - PullRequest
0 голосов
/ 27 июня 2018

Уже существует вопрос Java 8 DateTimeFormatter с необязательной частью , но ответ на него не будет работать, если необязательная часть имеет только часы и минуты без секунд:

DateTimeFormatter patternWithOptional = new DateTimeFormatterBuilder()
    .appendPattern("M/d/yyyy[ h:mm]")
    .toFormatter();
TemporalAccessor tmp = patternWithOptional.parseBest("4/11/2020 1:20", LocalDateTime::from, LocalDate::from);
System.out.println(tmp);
System.out.println(tmp.getClass().getSimpleName());
// prints 2020-04-11, without time
// class is LocalDate in this case

Это потому, что LocalDateTime.from(TemporalAccessor temporal) использует TemporalQueries.LOCAL_TIME для запроса temporal. TemporalQueries.LOCAL_TIME, в свою очередь, требует, чтобы ChronoField.NANO_OF_DAY был доступен из temporal.

System.out.println(patternWithOptional.parse("4/11/2020 1:20"));
// prints:
{HourOfAmPm=1, MinuteOfHour=20},ISO resolved to 2020-04-11

Так как HourOfAmPm и MinuteOfHour доступны, самый простой способ достичь того, что мне нужно, что я мог придумать:

private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
    .appendPattern("M/d/yyyy[ h:mm]")
    .toFormatter();

public static void main(String[] args) {
    // demo
    System.out.println(parse("3/20/1995"));      // without time
    System.out.println(parse("4/11/2020 1:20")); // with time
}

private static LocalDateTime parse(String s) {
    TemporalAccessor parsed = FORMATTER.parse(s);
    return LocalDateTime.of(
        LocalDate.from(parsed),
        LocalTime.of(
            parsed.isSupported(ChronoField.HOUR_OF_AMPM)
                ? (int) ChronoField.HOUR_OF_AMPM.getFrom(parsed)
                : 0,
            parsed.isSupported(ChronoField.MINUTE_OF_HOUR)
                ? (int) ChronoField.MINUTE_OF_HOUR.getFrom(parsed)
                : 0
        ) // minor note: this part could also be used as a lambda argument for parseBest method.
    );
}

Есть ли более простой способ сделать это без явных isSupported проверок?

1 Ответ

0 голосов
/ 27 июня 2018

В строке шаблона вашего формата вам нужно использовать прописные буквы H для часа дня:

            .appendPattern("M/d/yyyy[ H:mm]")

С этим изменением ваш первый фрагмент кода

2020-04-11T01:20
LocalDateTime

Нижний регистр h для часа в течение AM или PM. Таким образом, ваше время могло означать 01:20 или 01:20 вечера, то есть 13:20. Ява не могла принять решение и поэтому решила не интерпретировать время суток, а только дать вам LocalDate.

Если вы всегда хотите LocalDateTime, вы также можете использовать parseDefaulting:

    DateTimeFormatter patternWithOptional = new DateTimeFormatterBuilder()
            .appendPattern("M/d/yyyy[ H:mm]")
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
            .toFormatter();
    System.out.println(LocalDateTime.parse("4/11/2020 1:20", patternWithOptional));
    System.out.println(LocalDateTime.parse("4/11/2020", patternWithOptional));

Выход:

2020-04-11T01:20
2020-04-11T00:00

С другой стороны, если вы делаете хотите либо LocalDate, либо LocalDateTime в зависимости от того, доступно ли время дня в строке, вам не нужно DateTimeFormatterBuilder:

    DateTimeFormatter patternWithOptional = DateTimeFormatter.ofPattern("M/d/yyyy[ H:mm]");
...