Почему я не могу получить продолжительность в минутах или часах в 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
, toMillis
toMinutes
, а также 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 от текущей продолжительности.
например:
- считать дни
- сократить дни от продолжительности
- считать часы
- уменьшите время от продолжительности и т. д. до тех пор, пока вы не получите длительность = 0