Класс Java ZonedDateTime

ZonedDateTime — важный класс в API даты и времени (пакет java.time), появившемся в Java 8; он представляет дату и время с часовым поясом. Этот класс объединяет LocalDateTime и ZoneId, чтобы точно представить момент времени в определённом часовом поясе.

Основные возможности

  • Содержит дату (год, месяц, день)
  • Содержит время (час, минута, секунда, наносекунда)
  • Содержит информацию о часовом поясе
  • Является неизменяемым и потокобезопасным классом
  • Поддерживает преобразование часовых поясов и автоматическую корректировку перехода на летнее время

Способы создания ZonedDateTime

1. Создание с текущим временем

Пример

// Получить текущую дату и время в системном часовом поясе по умолчанию
ZonedDateTime now = ZonedDateTime.now();

// Получить текущую дату и время в указанном часовом поясе
ZonedDateTime nowInTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));

2. Создание из компонентов

Пример

// Укажите год, месяц, день, час, минуту, секунду и часовой пояс
ZonedDateTime customDateTime = ZonedDateTime.of(
    2023, 12, 25, 15, 30, 0, 0,
    ZoneId.of("America/New_York")
);

3. Объединение LocalDateTime и ZoneId

Пример

LocalDateTime localDateTime = LocalDateTime.of(2023, 6, 15, 10, 30);
ZoneId zoneId = ZoneId.of("Europe/Paris");
ZonedDateTime parisTime = ZonedDateTime.of(localDateTime, zoneId);

Общие методы операций

1. Получение информации о времени

Пример

ZonedDateTime zdt = ZonedDateTime.now();

int year = zdt.getYear();          // Год
Month month = zdt.getMonth();      // Месяц
int day = zdt.getDayOfMonth();     // День
int hour = zdt.getHour();          // Час
int minute = zdt.getMinute();      // Минута
int second = zdt.getSecond();      // Секунда
ZoneId zone = zdt.getZone();       // Часовой пояс

2. Добавление и вычитание времени

Пример

ZonedDateTime zdt = ZonedDateTime.now();

// Добавить 1 день
ZonedDateTime tomorrow = zdt.plusDays(1);

// Вычесть 2 часа
ZonedDateTime twoHoursEarlier = zdt.minusHours(2);

// Добавить 3 недели
ZonedDateTime threeWeeksLater = zdt.plusWeeks(3);

3. Преобразование часового пояса

Пример

ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));

// Преобразовать в время Нью-Йорка
ZonedDateTime newYorkTime = tokyoTime.withZoneSameInstant(ZoneId.of("America/New_York"));

// Преобразовать в UTC
ZonedDateTime utcTime = tokyoTime.withZoneSameInstant(ZoneOffset.UTC);

Примеры практического применения

1. Вычисление разницы во времени между двумя часовыми поясами

Пример

ZonedDateTime londonTime = ZonedDateTime.now(ZoneId.of("Europe/London"));
ZonedDateTime sydneyTime = ZonedDateTime.now(ZoneId.of("Australia/Sydney"));

// Вычислить разницу в часах
long hoursBetween = ChronoUnit.HOURS.between(londonTime, sydneyTime);
System.out.println("Текущая разница во времени между Лондоном и Сиднеем: " + hoursBetween + "часов");

2. Обработка времени встреч в разных часовых поясах

Пример

// Время встречи в Нью-Йорке: 3 PM
ZonedDateTime meetingTimeNY = ZonedDateTime.of(
    LocalDate.now(),
    LocalTime.of(15, 0),
    ZoneId.of("America/New_York")
);

// Преобразовать в токийское время
ZonedDateTime meetingTimeTokyo = meetingTimeNY.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("Участники из Токио должны быть там по местному времени: " + meetingTimeTokyo);

3. Обработка перехода на летнее время

Пример

// Время до и после перехода на летнее время в Нью-Йорке
ZonedDateTime beforeDST = ZonedDateTime.of(
    LocalDateTime.of(2023, 3, 11, 1, 30),
    ZoneId.of("America/New_York")
);

ZonedDateTime afterDST = beforeDST.plusHours(1);
System.out.println("Фактическая разница во времени: " +
    Duration.between(beforeDST, afterDST).toHours() + "hours");

Преобразование ZonedDateTime и других классов даты и времени

1. Преобразование в Instant

Пример

ZonedDateTime zdt = ZonedDateTime.now();
Instant instant = zdt.toInstant();  // Преобразовать в метку времени UTC

2. Преобразование в LocalDateTime

Пример

ZonedDateTime zdt = ZonedDateTime.now();
LocalDateTime ldt = zdt.toLocalDateTime();  // Теряется информация о часовом поясе

3. Создание из Instant

Пример

Instant instant = Instant.now();
ZonedDateTime zdt = instant.atZone(ZoneId.of("Asia/Shanghai"));

Комплексный пример

Пример

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

public class ZonedDateTimeDemo {
    public static void main(String[] args) {
        // 1. Получить текущее время в текущем часовом поясе
        ZonedDateTime now = ZonedDateTime.now();
        System.out.println("Текущее время в часовом поясе: " + now);
       
        // 2. Получить текущее время в указанном часовом поясе
        ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
        System.out.println("Время в Токио: " + tokyoTime);
       
        // 3. Создание конкретной даты и времени с часовым поясом
        ZonedDateTime specificTime = ZonedDateTime.of(
            2023, 5, 15, 10, 30, 0, 0,
            ZoneId.of("America/New_York")
        );
        System.out.println("Конкретное время в Нью-Йорке: " + specificTime);
       
        // 4. Преобразование часового пояса
        ZonedDateTime londonTime = specificTime.withZoneSameInstant(ZoneId.of("Europe/London"));
        System.out.println("Соответствующее время в Лондоне: " + londonTime);
       
        // 5. Форматирование вывода
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
        System.out.println("Форматированное время: " + now.format(formatter));
       
        // 6. Получение информации для каждого компонента
        System.out.println("Часовой пояс: " + now.getZone());
        System.out.println("Год: " + now.getYear());
        System.out.println("Месяц: " + now.getMonth());
        System.out.println("Час: " + now.getHour());
    }
}

Вывод аналогичен следующему:

当前时区时间: 2025-05-01T11:29:07.411467+08:00[Asia/Shanghai]
东京时间: 2025-05-01T12:29:07.417938+09:00[Asia/Tokyo]
纽约特定时间: 2023-05-15T10:30-04:00[America/New_York]
对应的伦敦时间: 2023-05-15T15:30+01:00[Europe/London]
格式化时间: 2025-05-01 11:29:07 CST
时区: Asia/Shanghai
年份: 2025
月份: MAY
小时: 11

Лучшие практики

  1. Хранение и передача: При хранении в базе данных или передаче по сети рекомендуется использовать Instant или ZonedDateTime в UTC
  2. Отображение: Преобразуйте в местный часовой пояс только при отображении в пользовательском интерфейсе
  3. Сравнение: При сравнении моментов времени в разных часовых поясах сначала преобразуйте их к одному и тому же часовому поясу или Instant
  4. Летнее время: Всегда используйте полный идентификатор часового пояса (например, "America/New_York") вместо простого смещения (например, "-05:00")

ZonedDateTime — мощный инструмент для работы с приложениями, чувствительными к часовым поясам; правильное его использование позволяет избежать многих распространённых проблем с датой и временем.

Статические фабричные методы

Метод Описание Пример
static ZonedDateTime now() Получить текущую дату и время в текущем часовом поясе ZonedDateTime.now()
static ZonedDateTime now(ZoneId zone) Получить текущую дату и время в указанном часовом поясе ZonedDateTime.now(ZoneId.of("Asia/Shanghai"))
static ZonedDateTime of(int year, int month, int day, int hour, int minute, int second, int nano, ZoneId zone) Создать с указанными параметрами ZonedDateTime.of(2023, 6, 15, 14, 30, 0, 0, ZoneId.of("GMT+8"))
static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone) Создать путём объединения LocalDate, LocalTime и часового пояса ZonedDateTime.of(LocalDate.now(), LocalTime.now(), ZoneId.systemDefault())
static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) Создать из LocalDateTime и часового пояса ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("America/New_York"))
static ZonedDateTime ofInstant(Instant instant, ZoneId zone) Создать из Instant и часового пояса ZonedDateTime.ofInstant(Instant.now(), ZoneId.of("UTC"))
static ZonedDateTime parse(CharSequence text) Разобрать из строки ZonedDateTime.parse("2023-06-15T14:30:45+08:00[Asia/Shanghai]")
static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter) Разобрать с использованием указанного формата ZonedDateTime.parse("2023-06-15 14:30 CST", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"))

Получение информации о дате и времени

Метод Описание Пример
int getYear() Получить год 2023
Month getMonth() Получить месяц (перечисление Month) Month.JUNE
int getMonthValue() Получить значение месяца (1-12) 6
int getDayOfMonth() Получить день месяца 15
int getDayOfYear() Получить день года 166
DayOfWeek getDayOfWeek() Получить день недели (перечисление DayOfWeek) DayOfWeek.THURSDAY
int getHour() Получить час (0-23) 14
int getMinute() Получить минуту (0-59) 30
int getSecond() Получить секунду (0-59) 45
int getNano() Получить наносекунду 0
ZoneId getZone() Получить часовой пояс Asia/Shanghai
ZoneOffset getOffset() Получить смещение часового пояса +08:00

Операции с датой и временем

Метод Описание Пример
ZonedDateTime plusDays(long days) Добавить дни zdt.plusDays(5)
ZonedDateTime plusHours(long hours) Добавить часы zdt.plusHours(2)
ZonedDateTime plusMinutes(long minutes) Добавить минуты zdt.plusMinutes(30)
ZonedDateTime plusSeconds(long seconds) Добавить секунды zdt.plusSeconds(45)
ZonedDateTime plusNanos(long nanos) Добавить наносекунды zdt.plusNanos(1000000)
ZonedDateTime minusDays(long days) Вычесть дни zdt.minusDays(5)
ZonedDateTime minusHours(long hours) Вычесть часы zdt.minusHours(2)
ZonedDateTime minusMinutes(long minutes) Вычесть минуты zdt.minusMinutes(30)
ZonedDateTime minusSeconds(long seconds) Вычесть секунды zdt.minusSeconds(45)
ZonedDateTime minusNanos(long nanos) Вычесть наносекунды zdt.minusNanos(1000000)
ZonedDateTime withYear(int year) Изменить год zdt.withYear(2024)
ZonedDateTime withMonth(int month) Изменить месяц zdt.withMonth(12)
ZonedDateTime withDayOfMonth(int day) Изменить день месяца zdt.withDayOfMonth(1)
ZonedDateTime withHour(int hour) Изменить час zdt.withHour(0)
ZonedDateTime withMinute(int minute) Изменить минуту zdt.withMinute(0)
ZonedDateTime withSecond(int second) Изменить секунду zdt.withSecond(0)
ZonedDateTime withNano(int nano) Изменить наносекунду zdt.withNano(0)
ZonedDateTime withZoneSameInstant(ZoneId zone) Преобразовать часовой пояс (сохраняя тот же момент времени) zdt.withZoneSameInstant(ZoneId.of("UTC"))
ZonedDateTime withZoneSameLocal(ZoneId zone) Преобразовать часовой пояс (сохраняя местное время) zdt.withZoneSameLocal(ZoneId.of("UTC"))

Методы преобразования

Метод Описание Пример
LocalDate toLocalDate() Преобразовать в LocalDate zdt.toLocalDate()
LocalTime toLocalTime() Преобразовать в LocalTime zdt.toLocalTime()
LocalDateTime toLocalDateTime() Преобразовать в LocalDateTime zdt.toLocalDateTime()
OffsetDateTime toOffsetDateTime() Преобразовать в OffsetDateTime zdt.toOffsetDateTime()
Instant toInstant() Преобразовать в Instant zdt.toInstant()

Методы сравнения

Метод Описание Пример
boolean isAfter(ChronoZonedDateTime<?> other) Проверяет, идёт ли после указанной даты и времени zdt1.isAfter(zdt2)
boolean isBefore(ChronoZonedDateTime<?> other) Проверяет, идёт ли до указанной даты и времени zdt1.isBefore(zdt2)
boolean isEqual(ChronoZonedDateTime<?> other) Проверяет, равен ли указанной дате и времени zdt1.isEqual(zdt2)
int compareTo(ChronoZonedDateTime<?> other) Сравнить две даты и времени zdt1.compareTo(zdt2)

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

Методы Описание Пример
String format(DateTimeFormatter formatter) Форматировать как строку zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
String toString() Преобразовать в строку формата ISO "2023-06-15T14:30:45+08:00[Asia/Shanghai]"
Другие расширения