Разбор LocalTime с високосной секундой

Я пытаюсь понять, как создать кастом DateTimeFormatterдля моего приложения. Мне в основном нужно обрабатывать время, которое написано так: "ЧЧММСС.FFFFFF".

Мне удалось получить 99%, используя:

import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
public static final DateTimeFormatter MY_TIME;
static {
    MY_TIME = new DateTimeFormatterBuilder()
            .appendValue(HOUR_OF_DAY, 2)
            .appendValue(MINUTE_OF_HOUR, 2)
            .optionalStart()
            .appendValue(SECOND_OF_MINUTE, 2)
            .optionalStart()
            .appendFraction(MICRO_OF_SECOND, 0, 6, true)
            .toFormatter().withResolverStyle(ResolverStyle.STRICT);
}

Я могу обрабатывать входные данные просто отлично:

String text = "101530";
LocalTime lt = LocalTime.parse(text, MY_TIME);

или даже

String text = "070907.0705";
LocalTime lt = LocalTime.parse(text, MY_TIME);

а также

String text = "0000";
LocalTime lt = LocalTime.parse(text, MY_TIME);

Но по какой-то причине я не могу понять API для обработки високосной секунды, поэтому следующее всегда терпит неудачу:

String text = "235960";
LocalTime lt = LocalTime.parse(text, MY_TIME);

Как я должен построить свой DateTimeFormatterBuilder так что обрабатывается високосная секунда?


Обновление: мне очень нравится ResolverStyle.STRICT, поскольку он отклоняет неверные данные, такие как:

  • "251213" или,
  • "126100"

Поэтому я не могу использовать ResolverStyle.LENIENT в этом случае я просто хочу дополнительный особый случай для високосной секунды.

4 ответа

Решение

Поскольку это имеет ограниченное применение для моих пользователей (редактирование неверного времени), я могу обработать этот особый случай, используя просто:

if (text.length() >= 6 && "60".equals(text.substring(4, 6))) {
    String newText = text.substring(0, 4) + "59" + text.substring(6);
    return LocalTime.parse(newText, MY_TIME);
}
return LocalTime.parse(text, MY_TIME);

Кажется, это общепринятый хак:

Местное время, показывающее високосную секунду, не имеет особого смысла без отображения календарной даты и часового пояса. В противном случае вы не знаете, является ли строка для анализа действительно допустимой (java.time-package не поможет вам проверить его) или просто подразумевает снисходительный подход (то есть разбор "60" в качестве следующей секунды).

О високосных возможностях java.time:

java.time действительно допустит любую фиктивную дату в сочетании с високосной секундой, хотя в большинстве случаев это неверно. И DateTimeFormatter принимает только ноль смещения UTC, но не выражения, такие как "2012-07-01T08:59:60+0900".

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

Вывод: этот подход хорош для вас, если вы хотите игнорировать информацию о високосной секунде, но в противном случае допускаете ее при вводе.

Альтернативы:

Если вы действительно заинтересованы в разборе, проверке и оценке возможных високосных секунд, то я рекомендую использовать мою библиотеку Time4J. Но для этого требуется действительная календарная дата (високосные секунды вставляются только в очень немногие даты).

     ChronoFormatter<Moment> formatter = 
         ChronoFormatter.ofMomentPattern( 
             "uuuu-MM-dd'T'HH:mm:ss XXX", 
             PatternType.CLDR, 
             Locale.ROOT, 
             ZonalOffset.UTC 
         )
     Moment m = formatter.parse("2012-07-01T08:59:60+09:00");

     // this conversion throws away the leap second
     Instant i = m.toTemporalAccessor();

Для получения дополнительной информации см. Также мою статью о DZone.

Соответствующее описание из java.time.Instant JavaDoc:

[...] этот Java API определяет свою собственную временную шкалу, Java Time-Scale.
...
Реализации шкалы времени Java с использованием API-интерфейса JSR-310 не обязаны предоставлять какие-либо часы с точностью до одной секунды или монотонно или плавно. Поэтому реализациям не требуется фактически выполнять UTC-SLS или иначе знать о дополнительных секундах.
...
Шкала времени Java используется для всех классов даты и времени. Это включает в себя Instant, LocalDate, LocalTime, OffsetDateTime, ZonedDateTime и Duration.

Короче не стоит ожидать java.time API, чтобы быть в курсе високосных секунд.

Обработка високосной секунды работает только для appendInstant: см. DateTimeFormatter:parsedLeapSecond

Мгновенный анализ обрабатывает специальное время "високосного" 23:59:60. Дополнительные секунды происходят в 23:59:60 в часовом поясе UTC, но в другое местное время в других часовых поясах. Чтобы избежать этой потенциальной неоднозначности, обработка високосных секунд ограничена DateTimeFormatterBuilder.appendInstant (), так как этот метод всегда анализирует момент со смещением зоны UTC.

Другие вопросы по тегам