Классы Duration и Period в Java - Вопросы

Всего: 6 вопросов

1. 

Чем Duration отличается от Period в Java и когда какой класс использовать?

Duration хранит точное физическое время в секундах (long) и наносекундах (int): сутки в нём ровно 86 400 секунд, toString() выглядит как PT1H30M. Period хранит календарный промежуток в годах, месяцах и днях (P1Y2M3D): месяц длится 28–31 день, а день при переводе часов — 23 или 25 часов.

ZonedDateTime base = ZonedDateTime.of(2024, 3, 30, 12, 0, 0, 0, ZoneId.of("Europe/Berlin"));
base.plus(Period.ofDays(1));   // 2024-03-31T12:00+02:00[Europe/Berlin]
base.plus(Duration.ofDays(1)); // 2024-03-31T13:00+02:00[Europe/Berlin]

Period сохраняет время «на часах», Duration прибавляет ровно 24 часа. Оба класса реализуют TemporalAmount, неизменяемы и потокобезопасны. Duration берут для тайм-аутов и времени выполнения, Period — для возраста, сроков подписки и «через 2 месяца».

2. 

Как создать Duration и Period и почему Duration.ofDays(1) выводится как PT24H?

Конструкторов у классов нет, объекты создают статическими методами of*():

Duration.ofDays(1);                  // PT24H
Duration.ofSeconds(1, 1_000_000_000); // PT2S
Duration.ofMillis(1500);             // PT1.5S
Duration.of(1, ChronoUnit.MONTHS);   // UnsupportedTemporalTypeException: Unit must not have an estimated duration

Period.of(5, 4, 3);   // P5Y4M3D
Period.ofWeeks(2);    // P14D
Period.of(0, 14, 40); // P14M40D

Duration внутри хранит только секунды и наносекунды, поэтому toString() никогда не показывает дни. Duration.of() принимает единицы с точной длиной (от NANOS до DAYS). В Period нет недель (ofWeeks(2) сразу становится 14 днями), а значения не нормализуются. Нулевые значения — Duration.ZERO и Period.ZERO.

3. 

Как вывести Duration в формате «чч:мм:сс»?

Готового форматтера для Duration нет, DateTimeFormatter с промежутками не работает. Строку собирают из частей:

Duration d = Duration.ofSeconds(9030);
// Java 9+
String.format("%02d:%02d:%02d", d.toHours(), d.toMinutesPart(), d.toSecondsPart()); // 02:30:30
// Java 8
String.format("%02d:%02d:%02d", d.toHours(), d.toMinutes() % 60, d.getSeconds() % 60); // 02:30:30

Методы to*() переводят всю длительность в одну единицу (getSeconds() вернёт 9030, а не 30), методы to*Part() возвращают отдельную часть. Трюк LocalTime.MIDNIGHT.plus(d) годится только для длительностей меньше суток: для 100 000 секунд он покажет 03:46:40 вместо 27:46:40.

4. 

Как сравнить две Duration и почему Period.ofMonths(12).equals(Period.ofYears(1)) возвращает false?

Duration реализует Comparable<Duration>: есть equals(), compareTo() (результат сравнивают с нулём), isZero(), isNegative() и с Java 18 isPositive(). Две длительности равны, если совпадает общее число секунд и наносекунд:

Duration.ofHours(1).equals(Duration.ofMinutes(60));                // true
Period.ofMonths(12).equals(Period.ofYears(1));                      // false
Period.ofMonths(12).normalized().equals(Period.ofYears(1));         // true

Period.equals() сравнивает годы, месяцы и дни по отдельности. Метод normalized() переводит месяцы сверх 12 в годы (дни не трогает), а toTotalMonths() возвращает общее число месяцев. Метода compareTo() у Period нет: без опорной даты нельзя сказать, что больше — 1 месяц или 30 дней.

5. 

Как посчитать возраст через Period.between и почему getDays() не возвращает общее число дней?

Period.between(начало, конец) принимает две LocalDate и возвращает разницу «N лет, M месяцев, K дней»: сначала считаются полные месяцы, затем оставшиеся дни, конечная дата не входит.

Period age = Period.between(LocalDate.of(1990, 8, 25), LocalDate.of(2024, 3, 15));
System.out.println(age);            // P33Y6M19D
System.out.println(age.getYears()); // 33

LocalDate start = LocalDate.of(2024, 1, 31);
LocalDate end = LocalDate.of(2024, 3, 1);
Period.between(start, end).getDays();   // 1 (период P1M1D)
ChronoUnit.DAYS.between(start, end);    // 30

getDays() возвращает только «хвост» дней после полных месяцев. Если нужно одно число — всего дней, месяцев или лет, — используйте ChronoUnit.DAYS.between(), ChronoUnit.MONTHS.between() или ChronoUnit.YEARS.between(). Для LocalDateTime сначала вызовите toLocalDate().

6. 

В каком формате Duration и Period выводятся и разбираются методом parse()?

Оба класса используют формат ISO-8601. Duration — PT + часы H, минуты M, секунды S; Period — P + годы Y, месяцы M, недели W, дни D. Буква T отделяет время: P1M — месяц, PT1M — минута.

Duration.parse("PT90M");   // PT1H30M
Duration.parse("P2DT3H");  // PT51H
Period.parse("P1Y2W3D");   // P1Y17D
Duration.parse("1:30");    // DateTimeParseException
Period.parse("PT1H");      // DateTimeParseException

Duration.parse() принимает дни, но переводит их в часы по 24. Годы и месяцы в Duration недопустимы, а время — в Period. Строку «01:30» разбирают вручную: split(":"), затем Duration.ofHours(h).plusMinutes(m).

Страница 1 из 1