Класс java.time.Instant в Java: что это и как использовать - Вопросы

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

1. 

Что такое 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()?

Только величины, связанные с эпохой: 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?

Методами 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() принимает только 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 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(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().

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