Дата и время ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-10-19

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

Строка DateTimeFormatter.ofPattern("dd.MM.yyyy").format(Instant.now()) компилируется, но при запуске падает с UnsupportedTemporalTypeException: Unsupported field: DayOfMonth. У Instant нет ни дня, ни месяца, ни года — только секунды от 1970 года. Чтобы получить «15.03.2024», сначала нужно сказать, в каком часовом поясе смотреть на этот момент: .withZone(ZoneId.of("Europe/Moscow")).

Instant в Java (java.time.Instant) — неизменяемый потокобезопасный класс, который описывает мгновение (instant, «инстант») — точку на временной шкале UTC. Внутри он хранит два числа: секунды от эпохи 1970-01-01T00:00:00Z (long) и наносекунды внутри секунды (int от 0 до 999 999 999). Например, 923 456 789 секунд и 186 054 812 наносекунд от эпохи — это момент 1999-04-07T03:46:29.186054812Z. Класс появился в Java 8; текущий момент возвращает Instant.now(). Используют Instant для меток времени: время создания записи в БД, события в логах, обмен датами между сервисами.

Как создать Instant: now(), ofEpochSecond(), ofEpochMilli(), parse()

Конструктора у Instant нет — объект создают статическими методами. Instant.now() возвращает текущий момент, ofEpochSecond(секунды[, наносекунды]) и ofEpochMilli(миллисекунды) задают момент относительно эпохи 1970-01-01T00:00:00Z, а parse() разбирает строку вида 2024-03-15T07:30:00Z. Отрицательные значения означают моменты до 1970 года.

import java.time.Instant;
import java.time.temporal.ChronoField;

public class InstantExample1 {
    public static void main(String[] args) {
        Instant now = Instant.now();

        Instant instant = now.with(ChronoField.NANO_OF_SECOND, 10);
        // Устанавливаем секунды
        Instant fiveSecondsAfterEpoch = Instant.ofEpochSecond(5);
        // Устанавливаем секунды и наносекунды (могут быть отрицательными)
        Instant sixSecTwoNanBeforeEpoch = Instant.ofEpochSecond(-6, -2);
        // Устанавливаем миллисекунды после (могут быть и до) эпохи
        Instant fiftyMilliSecondsAfterEpoch = Instant.ofEpochMilli(50);
        // Пример из определения
        Instant y1999 = Instant.ofEpochSecond(923_456_789, 186_054_812);
        Instant parsed = Instant.parse("2024-03-15T07:30:00Z");

        System.out.println("now: " + now);
        System.out.println("instant: " + instant);
        System.out.println("fiveSecondsAfterEpoch: " + fiveSecondsAfterEpoch);
        System.out.println("sixSecTwoNanBeforeEpoch: " + sixSecTwoNanBeforeEpoch);
        System.out.println("fiftyMilliSecondsAfterEpoch: " + fiftyMilliSecondsAfterEpoch);
        System.out.println("y1999: " + y1999);
        System.out.println("parsed: " + parsed);
        System.out.println("EPOCH: " + Instant.EPOCH);
    }
}
now: 2026-10-19T11:35:07.123456Z   (зависит от момента запуска)
instant: 2026-10-19T11:35:07.000000010Z
fiveSecondsAfterEpoch: 1970-01-01T00:00:05Z
sixSecTwoNanBeforeEpoch: 1969-12-31T23:59:53.999999998Z
fiftyMilliSecondsAfterEpoch: 1970-01-01T00:00:00.050Z
y1999: 1999-04-07T03:46:29.186054812Z
parsed: 2024-03-15T07:30:00Z
EPOCH: 1970-01-01T00:00:00Z

Посмотрите на четвёртую строку: ofEpochSecond(-6, -2) — это «6 секунд и 2 наносекунды до эпохи». Внутри Instant наносекунды всегда неотрицательны, поэтому значение нормализуется в минус 7 секунд плюс 999 999 998 наносекунд, отсюда 23:59:53.999999998. Доли секунды toString() печатает группами по три цифры: 50 миллисекунд выводятся как .050, а 10 наносекунд — как .000000010.

Допустимый диапазон — от Instant.MIN (-1000000000-01-01T00:00:00Z) до Instant.MAX (+1000000000-12-31T23:59:59.999999999Z). За его пределами ofEpochSecond() выбрасывает DateTimeException: Instant exceeds minimum or maximum instant.

Instant.now(): текущий момент в UTC

Метод Instant.now() возвращает текущий момент по системным часам в UTC и не зависит от часового пояса JVM: на сервере в Москве и в Лондоне он даст одно и то же значение. Поэтому Instant.now() — стандартный способ получить метку времени для логов, БД и обмена между сервисами.

import java.time.Instant;
import java.time.LocalDateTime;

public class InstantNowExample {
    public static void main(String[] args) {
        System.out.println("Instant.now():       " + Instant.now());
        System.out.println("LocalDateTime.now(): " + LocalDateTime.now());
    }
}
Instant.now():       2026-10-19T11:35:07.123456Z
LocalDateTime.now(): 2026-10-19T14:35:07.123512   (зона JVM Europe/Moscow)

Суффикс Z (Zulu) означает UTC, поэтому Instant «отстаёт» от московского времени на 3 часа — это не ошибка, а другая запись того же момента. Точность зависит от версии Java и ОС: в Java 8 обычно миллисекунды, в Java 9+ — как правило микросекунды. Для тестов есть перегрузка Instant.now(Clock) — пример с фиксированными часами в уроке LocalTime.

Получение секунд, миллисекунд и наносекунд

Из Instant можно получить только величины, связанные с эпохой: getEpochSecond() возвращает секунды (long), getNano() — наносекунды внутри секунды (int), toEpochMilli() — миллисекунды от эпохи. Универсальные get() и getLong() поддерживают поля NANO_OF_SECOND, MICRO_OF_SECOND, MILLI_OF_SECOND, а getLong() — ещё и INSTANT_SECONDS.

import java.time.Instant;
import java.time.temporal.ChronoField;

public class InstantExample2 {
    public static void main(String[] args) {
        Instant instant = Instant.parse("2024-03-15T10:30:45.123456789Z");
        long seconds = instant.getEpochSecond(); // Получить секунды
        int nanos1 = instant.getNano();          // Получить наносекунды
        // Получить значение как int
        int millis = instant.get(ChronoField.MILLI_OF_SECOND);
        int micros = instant.get(ChronoField.MICRO_OF_SECOND);
        // Получить значение как long
        long nanos2 = instant.getLong(ChronoField.NANO_OF_SECOND);
        long epochMillis = instant.toEpochMilli();

        System.out.println("Секунды: " + seconds);
        System.out.println("Наносекунды: " + nanos1);
        System.out.println("Миллисекунды: " + millis);
        System.out.println("Микросекунды: " + micros);
        System.out.println("Наносекунды: " + nanos2);
        System.out.println("Миллисекунды от эпохи: " + epochMillis);
    }
}
Секунды: 1710498645
Наносекунды: 123456789
Миллисекунды: 123
Микросекунды: 123456
Наносекунды: 123456789
Миллисекунды от эпохи: 1710498645123

Обратите внимание: MILLI_OF_SECOND — это миллисекунды внутри текущей секунды (123), а не от эпохи; миллисекунды от эпохи даёт toEpochMilli(), причём наносекунды сверх миллисекунды при этом отбрасываются. Полей даты и времени суток у Instant нет: instant.get(ChronoField.HOUR_OF_DAY) выбросит UnsupportedTemporalTypeException: Unsupported field: HourOfDay. Чтобы узнать час, день или год, переведите момент в зону — см. раздел Конвертация.

Сравнение Instant: isAfter(), isBefore(), equals()

Два объекта Instant сравнивают методами isAfter(), isBefore() и equals(), которые возвращают boolean. Для сортировки используется compareTo(): класс реализует Comparable<Instant>. Сравнение идёт по секундам, а при равенстве — по наносекундам; часовой пояс не участвует, потому что его нет.

import java.time.Instant;

public class InstantExample3 {
    public static void main(String[] args) {
        Instant now = Instant.now();
        Instant fiveSecondsAfterEpoch = Instant.ofEpochSecond(5);

        System.out.println("now.isAfter(fiveSecondsAfterEpoch)? " + now.isAfter(fiveSecondsAfterEpoch));
        System.out.println("now.isBefore(fiveSecondsAfterEpoch)? " + now.isBefore(fiveSecondsAfterEpoch));
        System.out.println("now.equals(fiveSecondsAfterEpoch)? " + now.equals(fiveSecondsAfterEpoch));
        System.out.println("now.compareTo(fiveSecondsAfterEpoch) > 0? " + (now.compareTo(fiveSecondsAfterEpoch) > 0));
    }
}
now.isAfter(fiveSecondsAfterEpoch)? true
now.isBefore(fiveSecondsAfterEpoch)? false
now.equals(fiveSecondsAfterEpoch)? false
now.compareTo(fiveSecondsAfterEpoch) > 0? true

Результат compareTo() сравнивайте с нулём: если секунды совпадают, метод возвращает разницу наносекунд, а не строго -1 или 1. Метода isEqual(), как у LocalDate, у Instant нет — используйте equals(). Одинаковые моменты, записанные в разных зонах, например ZonedDateTime 10:30 по Москве и 07:30 по UTC, после toInstant() дают равные Instant.

Методы plus() и minus()

Чтобы сдвинуть Instant, используют plusSeconds(), plusMillis(), plusNanos(), парные minus...() и универсальные plus(количество, ChronoUnit) / minus(...) или plus(Duration). Поддерживаются единицы от NANOS до DAYS (день считается ровно 86 400 секундами); месяцев, недель и лет нет. Каждый вызов возвращает новый объект.

import java.time.Instant;
import java.time.temporal.ChronoUnit;

public class InstantExample4 {
    public static void main(String[] args) {
        Instant epoch = Instant.ofEpochSecond(0);
        // Добавление
        Instant instant1 = epoch.plusSeconds(400);
        Instant instant2 = epoch.plusMillis(98622200);
        Instant instant3 = epoch.plusNanos(300013890);
        Instant instant4 = epoch.plus(2, ChronoUnit.MINUTES);

        // Вычитание
        Instant instant5 = epoch.minusSeconds(2);
        Instant instant6 = epoch.minusMillis(1);
        Instant instant7 = epoch.minusNanos(1);
        Instant instant8 = epoch.minus(10, ChronoUnit.SECONDS);

        System.out.println(epoch);
        System.out.println(instant1);
        System.out.println(instant2);
        System.out.println(instant3);
        System.out.println(instant4);

        System.out.println(instant5);
        System.out.println(instant6);
        System.out.println(instant7);
        System.out.println(instant8);
        System.out.println(epoch.plus(1, ChronoUnit.DAYS));
    }
}
1970-01-01T00:00:00Z
1970-01-01T00:06:40Z
1970-01-02T03:23:42.200Z
1970-01-01T00:00:00.300013890Z
1970-01-01T00:02:00Z
1969-12-31T23:59:58Z
1969-12-31T23:59:59.999Z
1969-12-31T23:59:59.999999999Z
1969-12-31T23:59:50Z
1970-01-02T00:00:00Z

Что выведет Instant.now().plus(1, ChronoUnit.MONTHS)? Ничего: будет выброшено UnsupportedTemporalTypeException: Unsupported unit: Months. Длина месяца зависит от календаря и часового пояса, а Instant не знает ни того, ни другого. Месяц прибавляют через зону:

Instant start = Instant.parse("2024-01-31T09:00:00Z");
ZoneId zone = ZoneId.of("Europe/Moscow");

Instant nextMonth = start.atZone(zone).plusMonths(1).toInstant();
System.out.println(nextMonth);                                             // 2024-02-29T09:00:00Z
System.out.println(start.plusSeconds(1800));                               // 2024-01-31T09:30:00Z
System.out.println(start.plusSeconds(1800).truncatedTo(ChronoUnit.HOURS)); // 2024-01-31T09:00:00Z

Метод truncatedTo() отбрасывает всё, что меньше указанной единицы. Учтите, что truncatedTo(ChronoUnit.DAYS) даёт полночь по UTC, а не по местному времени, — для Москвы это 03:00.

Разница между двумя Instant: Duration.between

Разницу между двумя моментами возвращает Duration.between(start, end) — объект с часами, минутами, секундами и наносекундами. Если нужно число в конкретных единицах, используйте ChronoUnit.X.between(start, end) или start.until(end, unit): они возвращают long и отбрасывают неполные единицы.

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;

public class InstantBetweenExample {
    public static void main(String[] args) {
        Instant start = Instant.parse("2024-03-15T07:30:00Z");
        Instant end = Instant.parse("2024-03-15T09:45:30.500Z");

        Duration duration = Duration.between(start, end);
        System.out.println(duration);                               // PT2H15M30.5S
        System.out.println(duration.toMillis());                    // 8130500
        System.out.println(ChronoUnit.MINUTES.between(start, end)); // 135
        System.out.println(start.until(end, ChronoUnit.SECONDS));   // 8130
        System.out.println(Duration.between(end, start));           // PT-2H-15M-30.5S
    }
}

Если конец раньше начала, длительность отрицательная. Для замера времени работы кода Instant.now() подходит с оговоркой: системные часы могут перевести (например, при синхронизации NTP), и разница окажется неверной, поэтому для бенчмарков берут System.nanoTime(). Методы Duration (toMinutes(), getSeconds(), plus()) разобраны в уроке Класс Duration.

Форматирование Instant и разбор строки

instant.toString() выводит момент в формате ISO-8601 по UTC (DateTimeFormatter.ISO_INSTANT), например 2024-03-15T07:30:00.123Z. Для любого другого формата нужен DateTimeFormatter с часовым поясом: DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss").withZone(zone). Без withZone() форматтер не сможет вычислить дату и время суток.

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class InstantFormatExample {
    public static void main(String[] args) {
        Instant instant = Instant.parse("2024-03-15T07:30:00.123Z");

        DateTimeFormatter moscow = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
                                                    .withZone(ZoneId.of("Europe/Moscow"));
        DateTimeFormatter http = DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC);

        System.out.println(instant);                                  // toString()
        System.out.println(DateTimeFormatter.ISO_INSTANT.format(instant));
        System.out.println(moscow.format(instant));
        System.out.println(http.format(instant));

        // Строка в «своём» формате -> Instant
        Instant parsed = ZonedDateTime.parse("15.03.2024 10:30:00", moscow).toInstant();
        System.out.println(parsed);
    }
}
2024-03-15T07:30:00.123Z
2024-03-15T07:30:00.123Z
15.03.2024 10:30:00
Fri, 15 Mar 2024 07:30:00 GMT
2024-03-15T07:30:00Z

Метод Instant.parse() принимает только ISO-формат с указанием UTC (а в новых версиях — и смещения). Строки без зоны не разбираются ни в одной версии Java:

Instant.parse("2024-03-15T07:30:00Z");      // 2024-03-15T07:30:00Z
Instant.parse("2024-03-15T07:30:00");       // DateTimeParseException: Text '2024-03-15T07:30:00' could not be parsed at index 19
Instant.parse("2024-03-15");                // DateTimeParseException: Text '2024-03-15' could not be parsed at index 10
Instant.parse("2024-03-15T10:30:00+03:00"); // Java 12+: 2024-03-15T07:30:00Z; Java 8-11: DateTimeParseException at index 19

Смещение в строке: зависит от версии Java

До Java 11 включительно Instant.parse() понимает только суффикс Z, и строка 2024-03-15T10:30:00+03:00 из чужого API падает с DateTimeParseException. Начиная с Java 12 ISO_INSTANT принимает и смещение (JDK-8166138). Если код должен работать и на Java 8, разбирайте такие строки через OffsetDateTime.parse(text).toInstant() — это работает во всех версиях.

Символы шаблона (dd, MM, HH и др.) и работа с Locale — в уроке Класс DateTimeFormatter.

Конвертация Instant в ZonedDateTime, LocalDateTime и Date

Чтобы из Instant получить дату и время «на часах», укажите часовой пояс: instant.atZone(zone) возвращает ZonedDateTime, instant.atOffset(offset) — OffsetDateTime, а LocalDateTime.ofInstant(instant, zone) — LocalDateTime. Обратно в Instant ведёт toInstant(). Со старым API связывают Date.from(instant) и date.toInstant().

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Date;

public class InstantConvertExample {
    public static void main(String[] args) {
        Instant instant = Instant.parse("2024-03-15T07:30:00Z");

        ZonedDateTime moscow = instant.atZone(ZoneId.of("Europe/Moscow"));
        OffsetDateTime plus3 = instant.atOffset(ZoneOffset.ofHours(3));
        LocalDateTime tokyo = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Tokyo"));
        LocalDate date = LocalDate.ofInstant(instant, ZoneOffset.UTC); // Java 9+

        Date legacy = Date.from(instant);
        Instant fromLegacy = legacy.toInstant();
        Instant fromMillis = Instant.ofEpochMilli(legacy.getTime());

        System.out.println(moscow);
        System.out.println(plus3);
        System.out.println(tokyo);
        System.out.println(date);
        System.out.println(moscow.getHour() + " ч., " + moscow.getDayOfWeek());
        System.out.println(legacy.getTime());
        System.out.println(moscow.toInstant().equals(instant));
        System.out.println(fromLegacy.equals(fromMillis));
    }
}
2024-03-15T10:30+03:00[Europe/Moscow]
2024-03-15T10:30+03:00
2024-03-15T16:30
2024-03-15
10 ч., FRIDAY
1710487800000
true
true

Один и тот же Instant в Москве — 10:30, в Токио — 16:30: момент один, меняется только запись на часах. В Java 8 метода LocalDate.ofInstant() ещё нет, там пишут instant.atZone(zone).toLocalDate(). При конвертации в Date точность падает до миллисекунд, наносекунды теряются.

Обратное направление — из LocalDateTime в Instant — требует зоны (ldt.atZone(zone).toInstant()); подробный разбор с переводом часов на летнее время — в уроке LocalDateTime, а когда что хранить — в его разделе LocalDateTime vs Instant. Полная таблица перехода с Date и Calendar — в уроке Дата и время в Java.

Шпаргалка по методам Instant

Методы Instant делятся на создание, чтение значений от эпохи, сравнение, сдвиг и привязку к часовому поясу. Все методы, которые «меняют» момент, возвращают новый объект.

Метод Что делает Граничный случай / ошибка
now(), now(Clock) Текущий момент в UTC Точность: Java 8 — миллисекунды, Java 9+ — обычно микросекунды
ofEpochSecond(s[, n]), ofEpochMilli(ms) Момент относительно 1970-01-01T00:00:00Z Отрицательные значения — до 1970 года; за пределами MIN/MAX — DateTimeException
parse(text) Разбор ISO-строки Без Z — DateTimeParseException; смещение +03:00 — только с Java 12
getEpochSecond(), getNano(), toEpochMilli() Секунды, наносекунды внутри секунды, миллисекунды от эпохи toEpochMilli() отбрасывает наносекунды сверх миллисекунды
get(ChronoField), getLong(ChronoField) Поля NANO/MICRO/MILLI_OF_SECOND, INSTANT_SECONDS (через getLong) HOUR_OF_DAY и др. — UnsupportedTemporalTypeException
isAfter(), isBefore(), equals(), compareTo() Сравнение моментов compareTo() сравнивайте с нулём; isEqual() нет
plusSeconds(), plusMillis(), plusNanos(), minus...(), plus(n, unit) Сдвиг на N единиц MONTHS, WEEKS, YEARS — Unsupported unit
truncatedTo(unit) Отбрасывает младшие единицы DAYS — полночь по UTC, не по местному времени
until(end, unit), Duration.between(a, b) Разница между моментами Если конец раньше начала — отрицательное значение
atZone(ZoneId), atOffset(ZoneOffset) Переход к ZonedDateTime / OffsetDateTime Без этого шага нельзя получить год, месяц, час

Типичные промахи при работе с Instant

Почти все ошибки с Instant происходят из одного: от него ждут даты и времени «на часах», а это лишь момент на шкале UTC без часового пояса. Ниже — частые случаи и исправления.

Ошибка Что происходит Как правильно
DateTimeFormatter.ofPattern("dd.MM.yyyy").format(instant) UnsupportedTemporalTypeException: Unsupported field: DayOfMonth ofPattern("dd.MM.yyyy").withZone(zone)
instant.plus(1, ChronoUnit.MONTHS) UnsupportedTemporalTypeException: Unsupported unit: Months instant.atZone(zone).plusMonths(1).toInstant()
«Instant показывает время на 3 часа меньше» Это не ошибка: toString() всегда выводит UTC Для показа пользователю: instant.atZone(zone) или форматтер с withZone()
Instant.parse("2024-03-15T07:30:00") DateTimeParseException at index 19 Добавить Z или LocalDateTime.parse(s).atZone(zone).toInstant()
instant.truncatedTo(ChronoUnit.DAYS) как «начало дня» Полночь по UTC (03:00 по Москве) LocalDate.now(zone).atStartOfDay(zone).toInstant()
instant.get(ChronoField.MILLI_OF_SECOND) как «миллисекунды от эпохи» Возвращает 0–999 внутри секунды instant.toEpochMilli()

Часто задаваемые вопросы

Чем Instant отличается от ZonedDateTime и OffsetDateTime?

Все три могут описывать один и тот же момент, но по-разному. Instant — голая точка на шкале UTC, без календарных полей. OffsetDateTime добавляет фиксированное смещение (+03:00), а ZonedDateTime — зону с правилами перевода часов (Europe/Berlin). Храните и передавайте Instant, а ZonedDateTime создавайте, когда нужно показать время пользователю или прибавить календарные месяцы.

Учитывает ли Instant високосные секунды?

Нет. В java.time используется шкала Java Time-Scale: в каждых сутках ровно 86 400 секунд, а високосная секунда «размазывается» по последним минутам дня. Строку с секундой 60 разбор не отвергает, а сводит к обычной: Instant.parse("2016-12-31T23:59:60Z") вернёт 2016-12-31T23:59:59Z. Поэтому арифметика с Instant всегда предсказуема; точное астрономическое время требует отдельных библиотек.

Как сохранить Instant в базе данных и в JSON?

В JDBC 4.2 обязательна поддержка OffsetDateTime, поэтому самый переносимый способ — ps.setObject(1, instant.atOffset(ZoneOffset.UTC)) для столбца TIMESTAMP WITH TIME ZONE; в старом коде — Timestamp.from(instant) и timestamp.toInstant(). Hibernate 5.2+ отображает поле Instant сам. В JSON Jackson пишет Instant строкой вроде "2024-03-15T07:30:00Z" при подключённом JavaTimeModule и выключенном WRITE_DATES_AS_TIMESTAMPS, иначе — числом секунд с дробной частью.

Зачем Instant, если можно хранить long с миллисекундами?

long не говорит, что в нём лежит: миллисекунды, секунды или вообще идентификатор, и перепутать единицы легко. Instant типобезопасен, хранит наносекунды, печатается в читаемом ISO-виде и сразу сочетается с Duration, atZone() и форматтерами. Переход между ними — одна строка: Instant.ofEpochMilli(ms) и instant.toEpochMilli(), см. раздел Получение секунд и миллисекунд.

Practice

Reinforce this lesson with hands-on tasks, each with a level and a time estimate.

All module tasks →

Video Explanation

Prefer video format? Watch this lesson with examples and explanations.

Comments

Please log in or register to have a possibility to add comment.