Класс LocalTime в Java: текущее время, форматирование и методы
Вызов LocalTime.of(23, 30).plusMinutes(45) вернёт не ошибку и не «24:15», а 00:15: время в LocalTime замкнуто в сутки, и после 23:59:59.999999999 отсчёт снова начинается с полуночи. При этом исходный объект не меняется — метод возвращает новый.
Класс LocalTime в Java (java.time.LocalTime) — неизменяемый потокобезопасный класс, который хранит время суток без даты и часового пояса: часы, минуты, секунды и наносекунды, например 13:45:30.123456789. Он появился в Java 8 вместе с остальным java.time API и подходит для расписаний, часов работы, будильников и любых значений вида «в 9 утра», где дата не важна. Текущее время в Java получают вызовом LocalTime.now().
Как создать LocalTime: of() и parse()
Объект LocalTime создаётся статическими фабричными методами — публичного конструктора у класса нет. LocalTime.of(часы, минуты) задаёт время с точностью до минуты, of(часы, минуты, секунды) — до секунды, of(часы, минуты, секунды, наносекунды) — полностью, а LocalTime.parse("08:15:30") разбирает строку.
import java.time.LocalTime;
public class LocalTimeExample1 {
public static void main(String[] args) {
// С часами (0-23) и минутами (0-59)
LocalTime fiveThirty = LocalTime.of(5, 30);
// С часами, минутами и секундами (0-59)
LocalTime noon = LocalTime.of(12, 0, 0);
// С часами, минутами, секундами и наносекундами (0-999 999 999)
LocalTime almostMidnight = LocalTime.of(23, 59, 59, 999_999_999);
// Из строки в формате ISO-8601
LocalTime fromString = LocalTime.parse("08:15:30");
System.out.println(fiveThirty);
System.out.println(noon);
System.out.println(almostMidnight);
System.out.println(fromString);
}
} 05:30
12:00
23:59:59.999999999
08:15:30 Обратите внимание на вывод: toString() не печатает нулевые секунды и наносекунды, поэтому LocalTime.of(12, 0, 0) выводится как 12:00. Доли секунды печатаются группами по три цифры: LocalTime.of(10, 0, 0, 1_000_000) даст 10:00:00.001.
999999 наносекунд — это не 999 миллисекунд
Четвёртый аргумент of() — именно наносекунды. Код LocalTime.of(23, 59, 59, 999999) даст 23:59:59.000999999, то есть меньше одной миллисекунды после 23:59:59. Чтобы получить «почти полночь», пишите 999_999_999 или берите константу LocalTime.MAX.
Метод of() проверяет значения сразу. Некорректное время не «перетекает» в следующий час, а вызывает исключение. Строка для parse() без форматтера должна быть в формате ISO-8601 с двузначными часами и минутами:
LocalTime.of(24, 0); // DateTimeException: Invalid value for HourOfDay (valid values 0 - 23): 24
LocalTime.of(10, 60); // DateTimeException: Invalid value for MinuteOfHour (valid values 0 - 59): 60
LocalTime.parse("9:30"); // DateTimeParseException: Text '9:30' could not be parsed at index 0
LocalTime.parse("25:00"); // DateTimeParseException: Text '25:00' could not be parsed: Invalid value for HourOfDay (valid values 0 - 23): 25 Строки в другом формате ("9:30", "07:05 PM") разбирают с шаблоном — см. раздел Форматирование LocalTime. Время можно построить и из числа секунд от полуночи — раздел toSecondOfDay() и ofSecondOfDay().
Текущее время в Java: LocalTime.now()
Чтобы получить текущее время в Java, вызовите LocalTime.now(). Метод возвращает время суток по системным часам в часовом поясе JVM по умолчанию, вместе с долями секунды; отдельные составляющие читают геттерами getHour(), getMinute(), getSecond() и getNano().
import java.time.LocalTime;
public class LocalTimeExample2 {
public static void main(String[] args) {
LocalTime now = LocalTime.now();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nanosecond = now.getNano();
System.out.println("Сейчас: " + now);
System.out.println("Часы: " + hour);
System.out.println("Минуты: " + minute);
System.out.println("Секунды: " + second);
System.out.println("Наносекунды: " + nanosecond);
}
} Сейчас: 14:07:52.381204 (зависит от момента запуска)
Часы: 14
Минуты: 7
Секунды: 52
Наносекунды: 381204000 Точность now() зависит от платформы: в Java 8 обычно миллисекунды, начиная с Java 9 — как правило микросекунды. Поэтому пользователю текущее время показывают отформатированным или усечённым методом truncatedTo(). Если нужны и время, и дата, используйте LocalDateTime.now().
У now() есть ещё два варианта. LocalTime.now(ZoneId.of("Asia/Tokyo")) вернёт текущее время в указанном часовом поясе, а LocalTime.now(clock) берёт время из объекта Clock: в продакшене это системные часы, а в тестах — фиксированные, и результат становится воспроизводимым:
import java.time.Clock;
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
public class LocalTimeNowExample {
public static void main(String[] args) {
Instant moment = Instant.parse("2024-03-15T10:00:00Z");
System.out.println(LocalTime.now(Clock.fixed(moment, ZoneOffset.UTC)));
System.out.println(LocalTime.now(Clock.fixed(moment, ZoneId.of("Asia/Tokyo"))));
}
} 10:00
19:00 Константы MIN, MAX, MIDNIGHT и NOON
В LocalTime четыре готовые константы: MIN и MIDNIGHT — начало суток 00:00 (равные по значению), NOON — полдень 12:00, а MAX — последняя наносекунда суток 23:59:59.999999999. Значения «24:00» в LocalTime не существует.
| Константа | Значение | Когда использовать |
|---|---|---|
LocalTime.MIN | 00:00 | Нижняя граница интервала, «начало дня» |
LocalTime.MIDNIGHT | 00:00 | То же значение, но название читается лучше, когда речь о полуночи |
LocalTime.NOON | 12:00 | Разделение дня на «до полудня» и «после» |
LocalTime.MAX | 23:59:59.999999999 | Верхняя граница интервала, «конец дня» |
Получение часов, минут и секунд: get() и ChronoField
Кроме геттеров getHour()/getMinute(), у LocalTime есть универсальные методы get(TemporalField) и getLong(TemporalField) с константами ChronoField. Через них получают другие представления времени: час в 12-часовом формате, номер минуты или секунды в сутках, миллисекунды.
import java.time.LocalTime;
import java.time.temporal.ChronoField;
public class LocalTimeExample3 {
public static void main(String[] args) {
LocalTime time = LocalTime.of(13, 45, 30, 123_456_789);
int hourAMPM = time.get(ChronoField.HOUR_OF_AMPM); // 0-11
int amPm = time.get(ChronoField.AMPM_OF_DAY); // 0 - AM, 1 - PM
int hourDay = time.get(ChronoField.HOUR_OF_DAY); // 0-23
int minuteDay = time.get(ChronoField.MINUTE_OF_DAY); // 0-1439
int minuteHour = time.get(ChronoField.MINUTE_OF_HOUR); // 0-59
int secondDay = time.get(ChronoField.SECOND_OF_DAY); // 0-86399
int secondMinute = time.get(ChronoField.SECOND_OF_MINUTE); // 0-59
int milli = time.get(ChronoField.MILLI_OF_SECOND); // 0-999
long nanoDay = time.getLong(ChronoField.NANO_OF_DAY); // 0-86399999999999
int nanoSecond = time.get(ChronoField.NANO_OF_SECOND); // 0-999999999
System.out.println("Часы (AM/PM): " + hourAMPM);
System.out.println("До полудня (0) или после (1): " + amPm);
System.out.println("Часы (0-23): " + hourDay);
System.out.println("Минута в сутках: " + minuteDay);
System.out.println("Минуты: " + minuteHour);
System.out.println("Секунда в сутках: " + secondDay);
System.out.println("Секунды: " + secondMinute);
System.out.println("Миллисекунды: " + milli);
System.out.println("Наносекунда в сутках: " + nanoDay);
System.out.println("Наносекунды: " + nanoSecond);
}
} Часы (AM/PM): 1
До полудня (0) или после (1): 1
Часы (0-23): 13
Минута в сутках: 825
Минуты: 45
Секунда в сутках: 49530
Секунды: 30
Миллисекунды: 123
Наносекунда в сутках: 49530123456789
Наносекунды: 123456789 Для NANO_OF_DAY нужен именно getLong(): значение до 86 399 999 999 999 не помещается в int, и time.get(ChronoField.NANO_OF_DAY) выбросит UnsupportedTemporalTypeException: Invalid field 'NanoOfDay' for get() method, use getLong() instead. Поля, которых у времени нет, тоже вызывают исключение: time.get(ChronoField.EPOCH_DAY) — UnsupportedTemporalTypeException: Unsupported field: EpochDay, ведь без даты номер дня узнать нельзя.
Сравнение времени: isBefore(), isAfter(), equals()
Два LocalTime сравнивают методами isBefore() и isAfter() (строго раньше и строго позже), а на равенство проверяют через equals(). Оператор == для этого не годится: он сравнивает ссылки. Метод compareTo() возвращает отрицательное число, ноль или положительное, поэтому LocalTime можно сортировать как Comparable.
import java.time.LocalTime;
public class CompareLocalTimeExample {
public static void main(String[] args) {
LocalTime fiveThirty = LocalTime.of(5, 30);
LocalTime noon = LocalTime.of(12, 0, 0);
LocalTime almostMidnight = LocalTime.of(23, 59, 59, 999_999_999);
System.out.println("fiveThirty.isAfter(noon)? " + fiveThirty.isAfter(noon));
System.out.println("fiveThirty.isBefore(noon)? " + fiveThirty.isBefore(noon));
System.out.println("noon.equals(almostMidnight)? " + noon.equals(almostMidnight));
System.out.println("fiveThirty.compareTo(noon): " + fiveThirty.compareTo(noon));
System.out.println("noon.compareTo(noon): " + noon.compareTo(noon));
}
} fiveThirty.isAfter(noon)? false
fiveThirty.isBefore(noon)? true
noon.equals(almostMidnight)? false
fiveThirty.compareTo(noon): -1
noon.compareTo(noon): 0 Частая практическая задача — проверить, попадает ли время в интервал. Для рабочего дня с 9:00 до 18:00 условие «не раньше начала и строго раньше конца» записывается так:
LocalTime now = LocalTime.of(14, 30);
boolean isWorking = !now.isBefore(LocalTime.of(9, 0)) && now.isBefore(LocalTime.of(18, 0));
System.out.println(isWorking); // true Если интервал переходит через полночь, например ночная тишина с 22:00 до 6:00, условие «между» не работает: начало позже конца. Здесь время подходит, если оно после начала или до конца:
LocalTime from = LocalTime.of(22, 0);
LocalTime to = LocalTime.of(6, 0);
LocalTime check = LocalTime.of(2, 0);
boolean isQuietTime = check.isAfter(from) || check.isBefore(to);
System.out.println(isQuietTime); // true Метод equals() учитывает все поля, включая наносекунды: LocalTime.of(12, 0).equals(LocalTime.of(12, 0, 0)) даёт true, а 12:00:00 и 12:00:00.000000001 уже не равны.
Изменение времени методами with()
Методы withHour(), withMinute(), withSecond() и withNano() заменяют одну составляющую времени и возвращают новый объект; универсальный with(TemporalField, long) делает то же через ChronoField. Остальные поля остаются прежними, а исходный LocalTime не меняется.
import java.time.LocalTime;
import java.time.temporal.ChronoField;
public class WithLocalTimeExample {
public static void main(String[] args) {
LocalTime noon = LocalTime.of(12, 0, 0);
LocalTime ten = noon.with(ChronoField.HOUR_OF_DAY, 10);
LocalTime eight = noon.withHour(8);
LocalTime twelveThirty = noon.withMinute(30);
LocalTime thirtyTwoSeconds = noon.withSecond(32);
// Можно использовать сцепление методов
LocalTime secondsNano = noon.withSecond(20).withNano(999_999);
System.out.println("ten: " + ten);
System.out.println("eight: " + eight);
System.out.println("twelveThirty: " + twelveThirty);
System.out.println("thirtyTwoSeconds: " + thirtyTwoSeconds);
System.out.println("secondsNano: " + secondsNano);
System.out.println("noon не изменился: " + noon);
}
} ten: 10:00
eight: 08:00
twelveThirty: 12:30
thirtyTwoSeconds: 12:00:32
secondsNano: 12:00:20.000999999
noon не изменился: 12:00 Значение вне диапазона отклоняется: noon.withHour(25) выбросит DateTimeException: Invalid value for HourOfDay (valid values 0 - 23): 25.
Методы plus() и minus()
Чтобы прибавить или вычесть время, используют plusHours(), plusMinutes(), plusSeconds(), plusNanos() и парные minusHours(), minusMinutes(), minusSeconds(), minusNanos(). Универсальные plus(long, TemporalUnit) и plus(TemporalAmount) принимают ChronoUnit или объект Duration. Каждый вызов возвращает новый объект.
import java.time.Duration;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class PlusMinusLocalTimeExample {
public static void main(String[] args) {
LocalTime fiveThirty = LocalTime.of(5, 30);
// Добавление
LocalTime sixThirty = fiveThirty.plusHours(1);
LocalTime fiveForty = fiveThirty.plusMinutes(10);
LocalTime plusSeconds = fiveThirty.plusSeconds(14);
LocalTime plusNanos = fiveThirty.plusNanos(99_999_999);
LocalTime sevenThirty = fiveThirty.plus(2, ChronoUnit.HOURS);
LocalTime seven = fiveThirty.plus(Duration.ofMinutes(90));
// Вычитание
LocalTime fourThirty = fiveThirty.minusHours(1);
LocalTime fiveTen = fiveThirty.minusMinutes(20);
LocalTime minusSeconds = fiveThirty.minusSeconds(2);
LocalTime minusNanos = fiveThirty.minusNanos(1);
LocalTime fiveTwenty = fiveThirty.minus(10, ChronoUnit.MINUTES);
System.out.println("sixThirty: " + sixThirty);
System.out.println("fiveForty: " + fiveForty);
System.out.println("plusSeconds: " + plusSeconds);
System.out.println("plusNanos: " + plusNanos);
System.out.println("sevenThirty: " + sevenThirty);
System.out.println("seven: " + seven);
System.out.println("fourThirty: " + fourThirty);
System.out.println("fiveTen: " + fiveTen);
System.out.println("minusSeconds: " + minusSeconds);
System.out.println("minusNanos: " + minusNanos);
System.out.println("fiveTwenty: " + fiveTwenty);
}
} sixThirty: 06:30
fiveForty: 05:40
plusSeconds: 05:30:14
plusNanos: 05:30:00.099999999
sevenThirty: 07:30
seven: 07:00
fourThirty: 04:30
fiveTen: 05:10
minusSeconds: 05:29:58
minusNanos: 05:29:59.999999999
fiveTwenty: 05:20 Главная особенность — переход через границу суток. У LocalTime нет даты, поэтому при выходе за 23:59:59.999999999 отсчёт идёт с нуля, а при вычитании ниже 00:00 — с конца суток. Исключения не будет.
Что выведет этот код?
System.out.println(LocalTime.of(23, 30).plusMinutes(45));
System.out.println(LocalTime.of(0, 15).minusMinutes(30));
System.out.println(LocalTime.of(10, 0).plusHours(25));
System.out.println(LocalTime.of(10, 0).plusHours(24)); 00:15
23:45
11:00
10:00 Прибавление ровно 24 часов возвращает исходное время, а 25 часов — то же, что один час. Чтобы узнать, что при этом «перешагнули» через полночь, нужна дата, то есть LocalDateTime. Попытка прибавить к LocalTime дни, например plus(1, ChronoUnit.DAYS), закончится ошибкой UnsupportedTemporalTypeException: Unsupported unit: Days.
Форматирование LocalTime: DateTimeFormatter
Чтобы вывести LocalTime в нужном виде, вызовите time.format(DateTimeFormatter.ofPattern("HH:mm:ss")); обратная операция — LocalTime.parse(текст, formatter). В шаблоне HH — часы 00–23, hh — часы 01–12, mm — минуты, ss — секунды, SSS — миллисекунды, a — AM/PM. Полная таблица символов — в уроке Класс DateTimeFormatter.
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class FormatLocalTimeExample {
public static void main(String[] args) {
LocalTime time = LocalTime.of(13, 45, 30, 123_456_789);
System.out.println(time.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
System.out.println(time.format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")));
System.out.println(time.format(DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH)));
System.out.println(time.format(DateTimeFormatter.ISO_LOCAL_TIME));
// Обратная операция: строка -> LocalTime
LocalTime evening = LocalTime.parse("07:05 PM", DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH));
LocalTime morning = LocalTime.parse("9:30", DateTimeFormatter.ofPattern("H:mm"));
System.out.println(evening);
System.out.println(morning);
}
} 13:45:30
13:45:30.123
01:45 PM
13:45:30.123456789
19:05
09:30 Строку "9:30" вызов LocalTime.parse("9:30") не разберёт, потому что стандартный ISO-формат требует 09:30. Если часы приходят без ведущего нуля, задайте шаблон H:mm, как в примере выше.
HH или hh, и Locale для AM/PM
Шаблон hh:mm без маркера a не отличает 01:45 от 13:45, поэтому для 24-часового формата берите HH. Маркер a выводит «AM/PM» или локализованный вариант в зависимости от языка системы, так что для предсказуемого результата передавайте Locale явно.
Шаблон с датой для LocalTime не подходит: time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) выбросит UnsupportedTemporalTypeException: Unsupported field: YearOfEra, ведь даты в объекте нет.
Отбрасываем доли секунды: truncatedTo()
Метод truncatedTo(ChronoUnit) обнуляет все поля младше указанной единицы и возвращает новое время. Он нужен, когда доли секунды мешают: при выводе, сравнении и записи в БД.
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class TruncatedToExample {
public static void main(String[] args) {
LocalTime time = LocalTime.of(13, 45, 30, 123_456_789);
System.out.println(time.truncatedTo(ChronoUnit.MILLIS));
System.out.println(time.truncatedTo(ChronoUnit.SECONDS));
System.out.println(time.truncatedTo(ChronoUnit.MINUTES));
System.out.println(time.truncatedTo(ChronoUnit.HOURS));
System.out.println(time.truncatedTo(ChronoUnit.HALF_DAYS));
}
} 13:45:30.123
13:45:30
13:45
13:00
12:00 Единицы больше суток не поддерживаются: truncatedTo(ChronoUnit.MONTHS) выбросит UnsupportedTemporalTypeException: Unit is too large to be used for truncation. Типичный сценарий — два значения now() почти никогда не равны из-за долей секунды, а после truncatedTo(ChronoUnit.MINUTES) их можно сравнивать «с точностью до минуты».
toSecondOfDay() и ofSecondOfDay()
Метод toSecondOfDay() возвращает количество секунд, прошедших с полуночи (от 0 до 86399), а LocalTime.ofSecondOfDay(long) выполняет обратное преобразование. Аналогичная пара для наносекунд — toNanoOfDay() и ofNanoOfDay(). Эти методы удобны, когда время хранят одним числом.
import java.time.LocalTime;
public class SecondOfDayExample {
public static void main(String[] args) {
System.out.println(LocalTime.of(1, 1, 1).toSecondOfDay());
System.out.println(LocalTime.MAX.toSecondOfDay());
System.out.println(LocalTime.MAX.toNanoOfDay());
System.out.println(LocalTime.ofSecondOfDay(3661));
System.out.println(LocalTime.ofNanoOfDay(1_000_000_000L));
}
} 3661
86399
86399999999999
01:01:01
00:00:01 Значение 86400 (ровно сутки) недопустимо: LocalTime.ofSecondOfDay(86400) выбросит DateTimeException: Invalid value for SecondOfDay (valid values 0 - 86399): 86400. toSecondOfDay() отбрасывает доли секунды, поэтому у LocalTime.MAX получается 86399, а не 86400.
Сколько времени между двумя LocalTime
Разницу между двумя значениями считают через ChronoUnit.MINUTES.between(start, end) (или HOURS, SECONDS), через эквивалентный start.until(end, ChronoUnit.MINUTES) либо через Duration.between(start, end), который возвращает объект с часами, минутами и секундами. Подробнее о самом Duration — в уроке Класс Duration.
import java.time.Duration;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class BetweenLocalTimeExample {
public static void main(String[] args) {
LocalTime start = LocalTime.of(9, 15);
LocalTime end = LocalTime.of(17, 45);
System.out.println(ChronoUnit.MINUTES.between(start, end));
System.out.println(ChronoUnit.HOURS.between(start, end));
System.out.println(Duration.between(start, end));
// Конец раньше начала - результат отрицательный
System.out.println(Duration.between(end, start));
}
} 510
8
PT8H30M
PT-8H-30M У LocalTime нет даты, поэтому метод не знает, что смена началась вчера. Для ночной смены ChronoUnit.MINUTES.between(LocalTime.of(22, 0), LocalTime.of(6, 0)) вернёт -960, а не ожидаемые 480 минут. Если разность отрицательна, прибавьте сутки: Duration.between(from, to).plusHours(24) даст PT8H. Для надёжных расчётов с переходом через полночь используйте LocalDateTime.
LocalTime, LocalDateTime, ZonedDateTime и Instant
Класс LocalTime хранит только время суток, LocalDateTime добавляет к нему дату, ZonedDateTime — ещё и часовой пояс, а Instant описывает точный момент на временной шкале UTC. «Во сколько открывается магазин» — LocalTime, «когда пройдёт встреча» — LocalDateTime или ZonedDateTime, «когда произошло событие» — Instant.
| Класс | Что хранит | Пример значения | Когда выбирать |
|---|---|---|---|
LocalTime | Время суток | 10:30 | Расписание, часы работы, будильник |
LocalDate | Только дата | 2024-03-15 | День рождения, срок оплаты |
LocalDateTime | Дата и время без зоны | 2024-03-15T10:30 | Событие в одном часовом поясе |
ZonedDateTime | Дата, время и зона | 2024-03-15T10:30+03:00[Europe/Moscow] | Встречи и рейсы между городами |
Instant | Момент на шкале UTC | 2024-03-15T07:30:00Z | Метки времени, логи, хранение в БД |
LocalTime превращается в LocalDateTime методом atDate(LocalDate), обратно — через toLocalTime(). Чтобы получить момент Instant, нужна ещё и зона:
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
public class LocalTimeConvertExample {
public static void main(String[] args) {
LocalTime time = LocalTime.of(10, 30);
ZoneId moscow = ZoneId.of("Europe/Moscow");
LocalDateTime dateTime = time.atDate(LocalDate.of(2024, 3, 15)); // LocalTime + LocalDate
Instant instant = dateTime.atZone(moscow).toInstant(); // + зона -> момент
LocalTime inMoscow = Instant.parse("2024-03-15T10:00:00Z")
.atZone(moscow).toLocalTime(); // Instant -> LocalTime
System.out.println(dateTime);
System.out.println(dateTime.toLocalTime());
System.out.println(instant);
System.out.println(inMoscow);
}
} 2024-03-15T10:30
10:30
2024-03-15T07:30:00Z
13:00 LocalTime.now() и System.nanoTime()
LocalTime.now() отвечает на вопрос «который час?», а System.nanoTime() — на вопрос «сколько прошло?». Метод nanoTime() возвращает long наносекунд от произвольной точки отсчёта: само число не связано со временем суток и имеет смысл только как разность двух вызовов. Для замера скорости кода нужен именно он, а для показа времени пользователю — LocalTime.
long start = System.nanoTime();
// ... измеряемый код ...
long elapsedNanos = System.nanoTime() - start;
System.out.println("Прошло, мс: " + elapsedNanos / 1_000_000); Показания nanoTime() не зависят от перевода системных часов (ручная настройка, синхронизация NTP), поэтому интервалы не «прыгают». Разность двух LocalTime.now() может даже оказаться отрицательной, если часы перевели назад или замер пришёлся на полночь. Если нужен момент времени объектом, а не числом, берите Instant.now() — см. урок Класс Instant.
Шпаргалка по методам LocalTime
Методы LocalTime делятся на группы: создание, чтение полей, сравнение, получение изменённой копии и преобразование. Все методы, которые «меняют» время, на самом деле возвращают новый объект.
| Метод | Что делает | Граничный случай / ошибка |
|---|---|---|
now(), now(ZoneId), now(Clock) | Текущее время суток | Зависит от зоны JVM; для тестов передавайте Clock |
of(h, m), of(h, m, s), of(h, m, s, n) | Время из частей | of(24, 0) — DateTimeException |
parse(text), parse(text, formatter) | Разбор строки | "9:30" без шаблона H:mm — DateTimeParseException |
getHour(), getMinute(), getSecond(), getNano(), get(ChronoField) | Составляющие времени | get(ChronoField.EPOCH_DAY) — UnsupportedTemporalTypeException |
isBefore(), isAfter(), equals(), compareTo() | Сравнение | Интервал через полночь проверяют условием «или» |
withHour(), withMinute(), withSecond(), withNano(), with() | Замена одного поля | withHour(25) — DateTimeException |
plusHours(), minusMinutes() и аналоги, plus(), minus() | Прибавить или вычесть время | За границей суток время «заворачивается»; DAYS — исключение |
format(formatter) | Строка по шаблону | Шаблон с датой (yyyy) — исключение |
truncatedTo(unit) | Обнуляет поля младше единицы | MONTHS и больше — исключение |
toSecondOfDay(), ofSecondOfDay(), toNanoOfDay(), ofNanoOfDay() | Время как число от полуночи и обратно | ofSecondOfDay(86400) — DateTimeException |
atDate(LocalDate) | Превращает в LocalDateTime | Зона по-прежнему не задана |
Типичные промахи с LocalTime
Большинство ошибок с LocalTime сводятся к трём причинам: забытое присваивание результата, неверный шаблон и ожидание, что у времени есть дата.
| Ошибка | Что происходит | Как правильно |
|---|---|---|
time.plusHours(1); без присваивания | Время не меняется — результат потерян | time = time.plusHours(1); |
time1 == time2 | Сравниваются ссылки, а не значения | time1.equals(time2) |
LocalTime.of(23, 59, 59, 999999) | Получается 23:59:59.000999999 | 999_999_999 или LocalTime.MAX |
Шаблон hh:mm для 24-часового времени | 13:45 выводится как 01:45 | Шаблон HH:mm |
Интервал 22:00–06:00 как isAfter(from) && isBefore(to) | Всегда false | isAfter(from) || isBefore(to) |
Замер скорости кода через LocalTime.now() | Неточно, зависит от перевода системных часов | System.nanoTime() |
Часто задаваемые вопросы
Как перевести LocalTime из одного часового пояса в другой?
Сам LocalTime зону не хранит, поэтому конвертируют через ZonedDateTime: LocalDate.now().atTime(time).atZone(ZoneId.of("Europe/Moscow")).withZoneSameInstant(ZoneId.of("Asia/Tokyo")).toLocalTime(). Дата нужна, потому что смещение зоны может зависеть от неё (летнее время). Если LocalTime.now() показывает не то время, что на ваших часах, проверьте зону JVM — ZoneId.systemDefault().
Как сохранить LocalTime в базу данных через JDBC?
Начиная с JDBC 4.2 передавайте объект напрямую: ps.setObject(1, time) и rs.getObject("start_time", LocalTime.class); тип столбца — TIME. Старый путь через java.sql.Time.valueOf(time) тоже работает, но теряет доли секунды.
Как отсортировать список LocalTime и найти самое позднее время?
LocalTime реализует Comparable, поэтому подходит Collections.sort(list) — от раннего времени к позднему: [18:00, 09:30, 12:00] станет [09:30, 12:00, 18:00]. Самое позднее время вернёт Collections.max(list), самое раннее — Collections.min(list). Как работает сравнение — в разделе Сравнение времени.
Как протестировать код, который вызывает LocalTime.now()?
Не вызывайте LocalTime.now() прямо в бизнес-логике: передайте в класс объект Clock и пишите LocalTime.now(clock). В продакшене подставляйте Clock.systemDefaultZone(), а в тесте — Clock.fixed(...), тогда результат не зависит от времени запуска. Пример — в разделе Текущее время в Java.
Практика по теме
Закрепите урок на задачах — у каждой есть уровень и оценка времени.
Все задачи модуля →Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии