Почему я не могу получить продолжительность в минутах или часах в java.time?

Из Duration класс в новом API даты JSR 310 ( пакет java.time), доступный в Java 8 и позже, javadoc говорит:

Этот класс моделирует количество времени в секундах и наносекундах. Доступ к нему можно получить с помощью других единиц измерения, основанных на длительности, таких как минуты и часы. Кроме того, можно использовать единицу DAYS, которая обрабатывается как точно равная 24 часам, таким образом игнорируя эффекты перехода на летнее время.

Итак, почему следующий код падает?

Duration duration = Duration.ofSeconds(3000);
System.out.println(duration.get(ChronoUnit.MINUTES));

Это поднимает UnsupportedTemporalTypeException:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Minutes
    at java.time.Duration.get(Duration.java:537)

Итак, каков рекомендуемый способ извлечь минуты и часы из объекта длительности? Нужно ли нам самим рассчитывать количество секунд? Почему это было реализовано таким образом?

5 ответов

Решение

"Почему это было реализовано таким образом?"

Другие ответы касаются toXxx() методы, позволяющие запрашивать часы / минуты. Я постараюсь разобраться с тем, почему.

TemporalAmount интерфейс и get(TemporalUnit) метод был добавлен довольно поздно в процессе. Лично я не был полностью убежден, что у нас было достаточно доказательств того, как правильно работать над дизайном в этой области, но был слегка извращен, чтобы добавить TemporalAmount, Я считаю, что при этом мы слегка запутали API.

Оглядываясь назад, я считаю, что TemporalAmount содержит правильные методы, но я считаю, что get(TemporalUnit) должен был иметь другое имя метода. Причина в том, что get(TemporalUnit) По сути, это метод уровня структуры - он не предназначен для повседневного использования. К сожалению название метода get не подразумевает этого, что приводит к ошибкам, таким как вызов get(ChronoUnit.MINUTES) на Duration,

Итак, способ думать о get(TemporalUnit) это представить низкоуровневую структуру, рассматривая сумму как Map<TemporalUnit, Long> где Duration это Map размером два с ключами SECONDS а также NANOS,

Точно так же, Period рассматривается в рамках низкого уровня как Map размером три - DAYS, MONTHS а также YEARS (который, к счастью, имеет меньше шансов на ошибки).

В целом, лучший совет для кода приложения - игнорировать метод get(TemporalUnit), использование getSeconds(), getNano(), toHours() а также toMinutes() вместо.

Наконец, один способ получить "чч: мм: сс" от Duration это сделать:

LocalTime.MIDNIGHT.plus(duration).format(DateTimeFormatter.ofPattern("HH:mm:ss"))

Совсем не красиво, но работает менее одного дня.

новый to…Part методы в Java 9

Проблема JDK-8142936 теперь реализована в Java 9, добавив следующие методы для доступа к каждой части Duration,

  • toDaysPart
  • toHoursPart
  • toMinutesPart
  • toSecondsPart
  • toMillisPart
  • toNanosPart

В документации сказано:

Это возвращает значение для каждого из двух поддерживаемых модулей, SECONDS и NANOS. Все остальные юниты выдают исключение.

Итак, лучший вариант ответа - так они и разработали.

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

long hours = duration.toHours();

или минуты:

long minutes = duration.toMinutes();

Для получения часовых / минутных / секундных компонентов "нормализованным" способом вам необходимо рассчитать их вручную - приведенный ниже код по существу скопирован из Duration#toString метод:

Duration duration = Duration.ofSeconds(3000);
long hours = duration.toHours();
int minutes = (int) ((duration.getSeconds() % (60 * 60)) / 60);
int seconds = (int) (duration.getSeconds() % 60);
System.out.println(hours + ":" + minutes + ":" + seconds);

Я хотел бы добавить к ответу Бигта (+1), который указал на полезное toHours, toMinutes и другие методы конвертации. Спецификация Duration говорит:

Этот класс моделирует количество времени в секундах и наносекундах. [...]

Диапазон длительности требует хранения числа больше, чем long. Чтобы достичь этого, класс хранит длинные представляющие секунды и целое число, представляющее наносекунду секунды, которое всегда будет между 0 и 999 999 999.

Есть множество методов получения, таких как getSeconds, getNano, а также get(TemporalUnit), Учитывая, что длительность представлена ​​в виде пары (секунды, нанос), понятно, почему get(TemporalUnit) ограничивается секундами и наносами. Эти методы получения извлекают данные непосредственно из экземпляра продолжительности и без потерь.

В отличие от этого, существует множество методов, включая toDays, toHours, toMillistoMinutes, а также toNanos которые делают преобразование на значение продолжительности. Эти преобразования с потерями в том, что они включают усечение данных, или они могут вызвать исключение, если значение продолжительности не может быть представлено в запрошенном формате.

Совершенно очевидно, что методы get извлекают данные без преобразования, а методы to выполняют какие-то преобразования.

Удалить Hourse, затем получить минут

long hours = attendanceDuration.toHours(); long minutes = attendanceDuration.minusHours(hours).toMinutes();

По приведенному ниже коду я получил этот вывод:

1 день, 2 часа, 5 минут

пример кода:

private String getDurationAsString(Duration duration)
{
    StringBuilder durationAsStringBuilder = new StringBuilder();
    if (duration.toDays() > 0)
    {
        String postfix = duration.toDays() == 1 ? "" : "s";
        durationAsStringBuilder.append(duration.toDays() + " day");
        durationAsStringBuilder.append(postfix);
    }

    duration = duration.minusDays(duration.toDays());
    long hours = duration.toHours();
    if (hours > 0)
    {
        String prefix = Utils.isEmpty(durationAsStringBuilder.toString()) ? "" : ", ";
        String postfix = hours == 1 ? "" : "s";
        durationAsStringBuilder.append(prefix);
        durationAsStringBuilder.append(hours + " hour");
        durationAsStringBuilder.append(postfix);
    }

    duration = duration.minusHours(duration.toHours());
    long minutes = duration.toMinutes();
    if (minutes > 0)
    {
        String prefix = Utils.isEmpty(durationAsStringBuilder.toString()) ? "" : ", ";
        String postfix = minutes == 1 ? "" : "s";
        durationAsStringBuilder.append(prefix);
        durationAsStringBuilder.append(minutes + " minute");
        durationAsStringBuilder.append(postfix);
    }

    return durationAsStringBuilder.toString();
}

Пояснение:

Используя интерфейс Duration, вы можете легко найти нужный вам String. вам просто нужно уменьшить больший TemporalUnit от текущей продолжительности.

например:

  1. считать дни
  2. сократить дни от продолжительности
  3. считать часы
  4. уменьшите время от продолжительности и т. д. до тех пор, пока вы не получите длительность = 0
Другие вопросы по тегам