Класс java.time.Instant в Java: что это и как использовать - Вопросы
Всего: 6 вопросов
1. Что такое Instant в Java и почему Instant.now() показывает время на 3 часа меньше московского?
Что такое Instant в Java и почему Instant.now() показывает время на 3 часа меньше московского?
java.time.Instant (Java 8+) — неизменяемый потокобезопасный класс, описывающий точку на временной шкале UTC. Внутри он хранит секунды от эпохи 1970-01-01T00:00:00Z (long) и наносекунды внутри секунды (int от 0 до 999 999 999). Дня, месяца, года и часового пояса у него нет.
Instant.now() возвращает текущий момент по системным часам в UTC и не зависит от зоны JVM, а toString() всегда печатает его в UTC с суффиксом Z:
System.out.println(Instant.now()); // 2026-10-19T11:35:07.123456Z
System.out.println(LocalDateTime.now()); // 2026-10-19T14:35:07.123512 (зона JVM Europe/Moscow)
Это не ошибка, а другая запись того же момента. Чтобы показать время пользователю, переведите момент в зону: instant.atZone(zone) или форматтер с withZone(). Создают Instant не конструктором (его нет), а фабричными методами now(), ofEpochSecond(), ofEpochMilli(), parse().
2. Какие значения можно получить из Instant и чем instant.get(ChronoField.MILLI_OF_SECOND) отличается от instant.toEpochMilli()?
Какие значения можно получить из Instant и чем instant.get(ChronoField.MILLI_OF_SECOND) отличается от instant.toEpochMilli()?
Только величины, связанные с эпохой: getEpochSecond() — секунды (long), getNano() — наносекунды внутри секунды (int), toEpochMilli() — миллисекунды от эпохи. Методы get() и getLong() поддерживают поля NANO_OF_SECOND, MICRO_OF_SECOND, MILLI_OF_SECOND, а getLong() — ещё и INSTANT_SECONDS.
Instant instant = Instant.parse("2024-03-15T10:30:45.123456789Z");
instant.get(ChronoField.MILLI_OF_SECOND); // 123 (внутри секунды)
instant.toEpochMilli(); // 1710498645123 (от эпохи)
MILLI_OF_SECOND — это 0–999 внутри текущей секунды, а toEpochMilli() — все миллисекунды от 1970 года (наносекунды сверх миллисекунды отбрасываются). Полей даты и времени суток нет: instant.get(ChronoField.HOUR_OF_DAY) выбросит UnsupportedTemporalTypeException: Unsupported field: HourOfDay. Чтобы узнать час или день, переведите момент в зону через atZone().
3. Как сравнить два объекта Instant?
Как сравнить два объекта Instant?
Методами isAfter(), isBefore() и equals(), которые возвращают boolean. Для сортировки есть compareTo(): Instant реализует Comparable<Instant>. Сравниваются сначала секунды, при равенстве — наносекунды; часовой пояс не участвует, потому что его нет.
Instant now = Instant.now();
Instant fiveSecondsAfterEpoch = Instant.ofEpochSecond(5);
now.isAfter(fiveSecondsAfterEpoch); // true
now.compareTo(fiveSecondsAfterEpoch) > 0; // true
Результат compareTo() сравнивайте с нулём, а не с 1 или -1: при равных секундах метод возвращает разницу наносекунд. Метода isEqual(), как у LocalDate, у Instant нет — используйте equals(). Один и тот же момент, записанный в разных зонах (10:30 по Москве и 07:30 по UTC), после toInstant() даёт равные Instant.
4. Какие строки умеет разбирать Instant.parse() и как разобрать строку со смещением вроде 2024-03-15T10:30:00+03:00?
Какие строки умеет разбирать Instant.parse() и как разобрать строку со смещением вроде 2024-03-15T10:30:00+03:00?
Instant.parse() принимает только ISO-формат (DateTimeFormatter.ISO_INSTANT) с указанием UTC. Строки без зоны не разбираются ни в одной версии Java:
Instant.parse("2024-03-15T07:30:00Z"); // 2024-03-15T07:30:00Z
Instant.parse("2024-03-15T07:30:00"); // DateTimeParseException ... at index 19
Instant.parse("2024-03-15"); // DateTimeParseException ... at index 10
Instant.parse("2024-03-15T10:30:00+03:00"); // Java 12+: 2024-03-15T07:30:00Z; Java 8-11: DateTimeParseException
Смещение вместо Z поддерживается только начиная с Java 12 (JDK-8166138). Если код должен работать и на Java 8, разбирайте такие строки через OffsetDateTime.parse(text).toInstant(). Строку без зоны превращают в Instant через LocalDateTime.parse(s).atZone(zone).toInstant(), а строку в своём формате — через ZonedDateTime.parse(text, formatterWithZone).toInstant().
5. Как перевести Instant в ZonedDateTime, LocalDateTime и java.util.Date и обратно?
Как перевести Instant в ZonedDateTime, LocalDateTime и java.util.Date и обратно?
Чтобы получить дату и время «на часах», нужно указать часовой пояс:
Instant instant = Instant.parse("2024-03-15T07:30:00Z");
ZonedDateTime moscow = instant.atZone(ZoneId.of("Europe/Moscow")); // 2024-03-15T10:30+03:00[Europe/Moscow]
OffsetDateTime plus3 = instant.atOffset(ZoneOffset.ofHours(3)); // 2024-03-15T10:30+03:00
LocalDateTime tokyo = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Tokyo")); // 2024-03-15T16:30
Date legacy = Date.from(instant);
Instant back = legacy.toInstant();
Обратно в Instant ведёт toInstant() (у ZonedDateTime, OffsetDateTime, Date). Момент один, меняется только запись на часах: в Москве 10:30, в Токио 16:30. LocalDate.ofInstant() появился в Java 9, в Java 8 пишут instant.atZone(zone).toLocalDate(). При переходе в Date точность падает до миллисекунд, наносекунды теряются.
6. Что делает метод truncatedTo() у Instant и почему truncatedTo(ChronoUnit.DAYS) не даёт начало дня по местному времени?
Что делает метод truncatedTo() у Instant и почему truncatedTo(ChronoUnit.DAYS) не даёт начало дня по местному времени?
truncatedTo(unit) возвращает новый Instant, в котором отброшено всё, что меньше указанной единицы:
Instant t = Instant.parse("2024-01-31T09:30:00Z");
System.out.println(t.truncatedTo(ChronoUnit.HOURS)); // 2024-01-31T09:00:00Z
Instant не знает часового пояса, поэтому truncatedTo(ChronoUnit.DAYS) даёт полночь по UTC — для Москвы это 03:00, а не 00:00. Начало дня по местному времени получают через зону: LocalDate.now(zone).atStartOfDay(zone).toInstant().