427. Java 日期时间 API - ZonedDateTime、OffsetDateTime、OffsetTime
1. 三个和时区相关的类 🌍
Java 的 Date-Time API 中,有三个经常会用到的类,它们都能处理 时区/偏移量:
ZonedDateTime👉 包含日期、时间、时区 ID(例如"Asia/Shanghai")以及对应的 偏移量。 ✅ 最强大,适合需要完整时区规则(比如夏令时)的场景。OffsetDateTime👉 包含日期、时间,只有偏移量(例如+09:00),但没有时区 ID。 ✅ 常用于数据库存储、网络传输,或者只需要绝对偏移的情况。OffsetTime👉 只包含时间(小时、分钟、秒),加一个偏移量(+/-HH:mm)。 ✅ 常用于“只关心时间点,不关心日期”的场景,比如每天 UTC+02:00 的提醒。
2. ZonedDateTime ✈️ —— 带时区的完整日期时间
ZonedDateTime 本质上就是:
LocalDateTime + ZoneId
- 它知道自己在哪个城市/地区,并能正确处理 夏令时(DST) 的转换。
- 比如:从旧金山飞东京 ✈️
import java.time.*;
import java.time.format.DateTimeFormatter;
public class FlightExample {
public static void main(String[] args) {
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm a");
// 起飞时间:2013年7月20日 19:30,旧金山(洛杉矶时区)
LocalDateTime leaving = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneId leavingZone = ZoneId.of("America/Los_Angeles");
ZonedDateTime departure = ZonedDateTime.of(leaving, leavingZone);
System.out.printf("LEAVING: %s (%s)%n", departure.format(format), leavingZone);
// 飞行时长:10小时50分钟(650分钟)
ZoneId arrivingZone = ZoneId.of("Asia/Tokyo");
ZonedDateTime arrival = departure.withZoneSameInstant(arrivingZone).plusMinutes(650);
System.out.printf("ARRIVING: %s (%s)%n", arrival.format(format), arrivingZone);
// 判断到达时是否夏令时
if (arrivingZone.getRules().isDaylightSavings(arrival.toInstant())) {
System.out.printf(" (%s daylight saving time will be in effect.)%n", arrivingZone);
} else {
System.out.printf(" (%s standard time will be in effect.)%n", arrivingZone);
}
}
}
输出:
LEAVING: Jul 20 2013 07:30 PM (America/Los_Angeles)
ARRIVING: Jul 21 2013 10:20 PM (Asia/Tokyo)
(Asia/Tokyo standard time will be in effect.)
🎯 场景:跨国航班、会议时间换算、金融交易(需要考虑夏令时)。
3. OffsetDateTime 🕰️ —— 带偏移量的日期时间
OffsetDateTime 本质上是:
LocalDateTime + ZoneOffset
- 它没有时区 ID(比如“北京”),只有绝对偏移(比如
+08:00)。 - 常用于 数据库存储、日志记录、网络传输(XML/JSON)。
- 例子:找出 2013 年 7 月的最后一个星期四 📅
import java.time.*;
import java.time.temporal.TemporalAdjusters;
public class OffsetExample {
public static void main(String[] args) {
// 本地时间:2013-07-20 19:30
LocalDateTime localDate = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneOffset offset = ZoneOffset.of("-08:00");
OffsetDateTime offsetDate = OffsetDateTime.of(localDate, offset);
// 找到当月最后一个星期四
OffsetDateTime lastThursday =
offsetDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY));
System.out.printf("The last Thursday in July 2013 is the %sth.%n",
lastThursday.getDayOfMonth());
}
}
输出:
The last Thursday in July 2013 is the 25th.
🎯 场景:日志里的时间戳(2025-09-15T09:30:00+08:00),API 返回值。
4. OffsetTime ⏰ —— 带偏移量的时间(不含日期)
OffsetTime 本质上是:
LocalTime + ZoneOffset
- 只关心一天中的时间点,不关心具体日期。
- 例子:每天早上 9 点(UTC+02:00)的提醒
import java.time.*;
public class OffsetTimeExample {
public static void main(String[] args) {
OffsetTime meeting = OffsetTime.of(9, 0, 0, 0, ZoneOffset.of("+02:00"));
System.out.println("Meeting time: " + meeting);
}
}
输出:
Meeting time: 09:00+02:00
🎯 场景:国际会议的每日时间提醒、只关心“几点几分”的任务。
5. 总结 📝
| 类名 | 组成 | 典型场景 |
|---|---|---|
ZonedDateTime | LocalDateTime + ZoneId | 跨国航班、夏令时转换 |
OffsetDateTime | LocalDateTime + ZoneOffset | 日志存储、数据库时间戳、API |
OffsetTime | LocalTime + ZoneOffset | 国际会议提醒、每日任务时间 |
6. 课堂互动 💡
可以抛几个问题:
- 如果我想保存数据库里的交易时间戳,你会选
ZonedDateTime还是OffsetDateTime?为什么? - 如果我只关心“每天 8:00+05:30 叫醒服务”,用哪个类更合适?