Java ZonedDateTime 类
ZonedDateTime 是 Java 8 引入的日期时间 API (java.time 包)中的一个重要类,它表示带有时区的日期和时间。这个类结合了 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点
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() + "小时");
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() + "小时");
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"));
最佳实践
- 存储和传输:在数据库存储或网络传输时,建议使用 Instant 或 UTC 时间的 ZonedDateTime
- 显示:只在用户界面显示时转换为本地时区
- 比较:比较不同时区的时间时,先转换为同一时区或 Instant
- 夏令时:始终使用完整的时区ID(如"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]" |
点我分享笔记