W3docs

Java LocalTime

Представление времени без даты и временной зоны в Java с помощью LocalTime.

LocalTime — это зеркальное отражение LocalDate: время суток — часы, минуты, секунды, наносекунды — без даты и без временной зоны. Оно представляет одно и то же показание часов в любой календарный день в любом городе: LocalTime.of(9, 30) — это полдевятого в любой день в любом городе.

Это правильный тип для повторяющегося времени суток — открытие магазина в 09:30, ежедневный cron-подобный процесс в 03:15, встреча, начинающаяся в 14:00 независимо от даты. Это не тот тип, который нужен для "момента, когда пользователь нажал кнопку отправки в 14:30 сегодня" — для этого нужна дата и, вероятно, временная зона. Их объединение даёт LocalDateTime, тема следующей главы.

Создание

LocalTime now = LocalTime.now();                              // current time in the JVM default zone
LocalTime nine = LocalTime.of(9, 0);                          // hour, minute
LocalTime nineThirty = LocalTime.of(9, 30, 15);               // hour, minute, second
LocalTime nineThirtyNanos = LocalTime.of(9, 30, 15, 500_000_000);   // + nanosecond (0 ..999_999_999)
LocalTime parsed = LocalTime.parse("09:30:15");               // ISO-8601 HH:mm[:ss[.nnnnnnnnn]]

Заранее определённые константы полезны для граничных условий:

LocalTime.MIDNIGHT     // 00:00
LocalTime.NOON         // 12:00
LocalTime.MIN          // 00:00:00.000000000
LocalTime.MAX          // 23:59:59.999999999

MIN и MAX особенно полезны при объединении с LocalDate для охвата целого дня: LocalDateTime.of(date, LocalTime.MIN) — это "полночь в начале даты"; LocalTime.MAX — последняя представимая наносекунда дня.

Разрешение: наносекунды

LocalTime точен до наносекунды — девять полей разрешения (1 секунда = 1 000 000 000 нс). На большинстве операционных систем реальное разрешение часов составляет миллисекунду (1 000 000 нс) или микросекунду (1 000 нс); дополнительная точность предусмотрена, чтобы тип не терял информацию при взаимодействии с системами, обладающими часами более высокого разрешения.

Прямые методы доступа:

time.getHour();           // 0-23
time.getMinute();         // 0-59
time.getSecond();         // 0-59
time.getNano();           // 0-999_999_999

Метода getMilli() нет; если нужны миллисекунды, разделите наносекунды: time.getNano() / 1_000_000.

24-часовой формат, без AM/PM

LocalTime внутренне использует 24-часовой формат. LocalTime.of(13, 0) означает "13:00", и в типе нет AM/PM. Разбор строк с "AM"/"PM" требует пользовательского DateTimeFormatter (глава Разбор дат охватывает эту тему) — стандартный parse работает только с ISO-8601 24-часовым форматом.

Арифметика и изменения

Тот же гибкий интерфейс, что и у LocalDate:

time.plusHours(2);
time.plusMinutes(30);
time.plusSeconds(45);
time.plusNanos(500_000);

time.withHour(14);                                            // replace one field
time.withMinute(0);

Поведение при переходе через полночь: каждый метод plus/minus у LocalTime молча переходит через полночь. LocalTime.of(23, 0).plusHours(2) возвращает 01:00, а не "01:00 следующего дня" — в LocalTime нет понятия "следующий день". Если нужно знать, произошёл ли переход, используйте LocalDateTime или вычислите самостоятельно:

LocalTime late = LocalTime.of(23, 0);
LocalTime later = late.plusHours(2);                          // 01:00 — silently wrapped
// To detect wrap: compare the new value's getHour with what you expected, or use LocalDateTime.

Этот переход задокументирован и является намеренным, но это острый момент, о котором легко забыть. Для расчётов "когда заканчивается эта смена?", которые могут пересекать полночь, правильный тип — LocalDateTime, а не LocalTime.

Сравнение

time.isBefore(other);
time.isAfter(other);
time.compareTo(other);
time.equals(other);

Лексикографическое упорядочивание по hour:minute:second:nano. LocalTime реализует Comparable<LocalTime>, поэтому можно отсортировать список времён или напрямую использовать его в качестве ключа TreeMap.

Разница

Duration.between и ChronoUnit.X.between работают оба:

Duration d = Duration.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end);
long seconds = ChronoUnit.SECONDS.between(start, end);

Знак: положительный, когда end после start, отрицательный в противном случае. Та же оговорка о переходе через полночь применима — Duration.between(LocalTime.of(23, 0), LocalTime.of(1, 0)) равно −22 часа, а не +2 часа; API считает 01:00 более ранним, чем 23:00 того же условного дня. Для "смена перешла через полночь" правильным инструментом является арифметика на основе LocalDateTime.

Объединение с LocalDate

Вы часто будете преобразовывать в дату-со-временем:

LocalDate date = LocalDate.of(2025, 11, 4);
LocalTime time = LocalTime.of(9, 30);

LocalDateTime dt = date.atTime(time);                         // 2025-11-04T09:30
LocalDateTime dt2 = time.atDate(date);                        // same thing

atTime / atDate — мостовые методы. Результат — LocalDateTime — по-прежнему без зоны, но теперь привязан к календарному дню. Следующая глава развивает эту тему дальше.

Практический пример: небольшой помощник планирования

Программа ниже использует LocalTime для задачи типа ежедневного расписания: определяет окно "рабочих часов", проверяет, попадает ли заданный момент внутрь него, вычисляет время до следующего открытия и демонстрирует ловушку с переходом через полночь.

java— editable, runs on the server

Что можно извлечь из запуска:

  • LocalTime выводится как 09:00, 17:30, 12:30 — каноническая ISO-8601 24-часовая форма. Никакого AM/PM в типе. Если нужно отобразить "5:30 PM" пользователю, глава Форматирование дат содержит нужный форматтер; сам тип об этом ничего не знает.
  • Проверка "попадает ли время в окно" использовала !isBefore(open) && !isAfter(close). Это идиома полуоткрытого/закрытого интервала — оба конца включены. Для "строго внутри" переключитесь на формы без отрицания.
  • Duration.between(LocalTime.of(22, 0), LocalTime.of(2, 0)) вернул PT-20H, а не PT4H. LocalTime не имеет понятия "следующий день" — когда end часовым значением меньше start, длительность становится отрицательной. Для смены, пересекающей полночь, переключите входные данные на LocalDateTime и дайте датам разрешить неоднозначность. Это единственная крупнейшая ловушка LocalTime.
  • LocalTime.of(23, 30).plusHours(2) вернул 01:30. Переход молча — без исключения, без флага, без переноса в дату. Если нужно знать "произошёл ли переход?", используйте LocalDateTime. Если же нужна именно тактовая арифметика по модулю 24 (например, повторяющееся расписание), переход является корректным поведением.
  • date.atTime(time) — это канонический мост к LocalDateTime. Зеркальный метод time.atDate(date) даёт тот же результат. Вы будете постоянно использовать их при чтении времени из одного источника и даты из другого, а затем объединяя их в единый объект, который требует нижестоящий API.

Что дальше

Следующая глава, Java LocalDateTime, объединяет LocalDate и LocalTime в третий "локальный" тип: дата и время, по-прежнему без временной зоны. Это естественная единица для "это произошло в 14:30 4 ноября", когда зона не важна или хранится отдельно.

Практика

Практика
Что вернёт `LocalTime.of(23, 0).plusHours(3)` и почему?
Что вернёт `LocalTime.of(23, 0).plusHours(3)` и почему?
Was this page helpful?