Класс Java ZonedDateTime
ZonedDateTime — важный класс в API даты и времени (пакет java.time), появившемся в Java 8; он представляет дату и время с часовым поясом. Этот класс объединяет LocalDateTime и ZoneId, чтобы точно представить момент времени в определённом часовом поясе.
Основные возможности
- Содержит дату (год, месяц, день)
- Содержит время (час, минута, секунда, наносекунда)
- Содержит информацию о часовом поясе
- Является неизменяемым и потокобезопасным классом
- Поддерживает преобразование часовых поясов и автоматическую корректировку перехода на летнее время
Способы создания ZonedDateTime
1. Создание с текущим временем
Пример
// Получить текущую дату и время в системном часовом поясе по умолчанию
ZonedDateTime now = ZonedDateTime.now();
// Получить текущую дату и время в указанном часовом поясе
ZonedDateTime nowInTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
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")
);
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);
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(); // Часовой пояс
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);
// Добавить 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);
// Преобразовать в время Нью-Йорка
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 + "часов");
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);
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 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
Instant instant = zdt.toInstant(); // Преобразовать в метку времени UTC
2. Преобразование в LocalDateTime
Пример
ZonedDateTime zdt = ZonedDateTime.now();
LocalDateTime ldt = zdt.toLocalDateTime(); // Теряется информация о часовом поясе
LocalDateTime ldt = zdt.toLocalDateTime(); // Теряется информация о часовом поясе
3. Создание из Instant
Пример
Instant instant = Instant.now();
ZonedDateTime zdt = instant.atZone(ZoneId.of("Asia/Shanghai"));
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());
}
}
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
Лучшие практики
- Хранение и передача: При хранении в базе данных или передаче по сети рекомендуется использовать Instant или ZonedDateTime в UTC
- Отображение: Преобразуйте в местный часовой пояс только при отображении в пользовательском интерфейсе
- Сравнение: При сравнении моментов времени в разных часовых поясах сначала преобразуйте их к одному и тому же часовому поясу или Instant
- Летнее время: Всегда используйте полный идентификатор часового пояса (например, "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]" |