Java 8 全新日期时间 API:线程安全、设计优雅、时区处理轻松搞定
Java 8 引入了全新的日期时间 API(java.time 包),彻底解决了旧 API(java.util.Date、Calendar)的三大痛点:
- 非线程安全:旧 Date 可变,多线程环境下需自行同步。新 API 所有类均为不可变,无 setter 方法,天然线程安全。
- 设计混乱:年份从 1900 起算、月份 0-11、日期 1-31,缺乏一致性。新 API 提供大量直观的工具方法。
- 时区处理繁琐:旧 API 需大量样板代码。新 API 采用领域驱动设计,内置时区支持。
核心类分为两大类:
- Local*:
LocalDate、LocalTime、LocalDateTime— 无时区的日期时间处理。 - Zoned*:
ZonedDateTime、ZoneId— 带时区的日期时间处理。
Local 日期时间 API
适用于不需要时区的场景。示例:
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;
public class Java8Tester {
public static void main(String[] args) {
new Java8Tester().testLocalDateTime();
}
public void testLocalDateTime() {
// 当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期时间: " + now);
// 拆解日期、时间
LocalDate date = now.toLocalDate();
Month month = now.getMonth();
int day = now.getDayOfMonth();
int second = now.getSecond();
System.out.printf("月: %s, 日: %d, 秒: %d%n", month, day, second);
// 修改日期(不可变,返回新对象)
LocalDateTime modified = now.withDayOfMonth(10).withYear(2012);
System.out.println("修改后: " + modified);
// 静态工厂方法创建
LocalDate specificDate = LocalDate.of(2014, Month.DECEMBER, 12);
LocalTime specificTime = LocalTime.of(22, 15);
LocalTime parsedTime = LocalTime.parse("20:15:30");
System.out.println("指定日期: " + specificDate);
System.out.println("指定时间: " + specificTime);
System.out.println("解析时间: " + parsedTime);
}
}
运行结果示例
当前日期时间: 2014-12-09T11:00:45.457 月: DECEMBER, 日: 9, 秒: 45 修改后: 2012-12-10T11:00:45.457 指定日期: 2014-12-12 指定时间: 22:15 解析时间: 20:15:30
Zoned 日期时间 API
处理时区相关场景:
import java.time.ZonedDateTime;
import java.time.ZoneId;
public class Java8Tester {
public void testZonedDateTime() {
// 解析带时区字符串
ZonedDateTime zdt = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
System.out.println("解析结果: " + zdt);
// 指定时区
ZoneId paris = ZoneId.of("Europe/Paris");
System.out.println("巴黎时区: " + paris);
// 系统默认时区
ZoneId system = ZoneId.systemDefault();
System.out.println("系统时区: " + system);
}
}
运行结果示例
解析结果: 2007-12-03T10:15:30+05:00[Asia/Karachi] 巴黎时区: Europe/Paris 系统时区: Etc/UTC
ChronoUnit 枚举
替代旧 API 中的整数常量,提供类型安全的时间单位:
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class Java8Tester {
public void testChronoUnits() {
LocalDate today = LocalDate.now();
System.out.println("今天: " + today);
System.out.println("下周: " + today.plus(1, ChronoUnit.WEEKS));
System.out.println("下月: " + today.plus(1, ChronoUnit.MONTHS));
System.out.println("明年: " + today.plus(1, ChronoUnit.YEARS));
System.out.println("十年后: " + today.plus(1, ChronoUnit.DECADES));
}
}
运行结果示例
今天: 2014-12-10 下周: 2014-12-17 下月: 2015-01-10 明年: 2015-12-10 十年后: 2024-12-10
Period 与 Duration
- Period:基于日期的时间量(年、月、日)。
- Duration:基于时间的时间量(时、分、秒、纳秒)。
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
public class Java8Tester {
public void testPeriod() {
LocalDate d1 = LocalDate.now();
LocalDate d2 = d1.plus(1, ChronoUnit.MONTHS);
Period p = Period.between(d2, d1);
System.out.println("Period: " + p); // 负值表示 d1 在 d2 之前
}
public void testDuration() {
LocalTime t1 = LocalTime.now();
Duration twoHours = Duration.ofHours(2);
LocalTime t2 = t1.plus(twoHours);
Duration d = Duration.between(t1, t2);
System.out.println("Duration: " + d); // PT2H
}
}
TemporalAdjuster 时间调节器
用于复杂的日期运算,如“下一个周二”、“本月第二个周六”:
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;
public class Java8Tester {
public void testAdjusters() {
LocalDate today = LocalDate.now();
System.out.println("今天: " + today);
// 下一个周二
LocalDate nextTue = today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
System.out.println("下个周二: " + nextTue);
// 本月第二个周六
LocalDate firstDay = LocalDate.of(today.getYear(), today.getMonth(), 1);
LocalDate secondSat = firstDay.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY))
.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
System.out.println("本月第二个周六: " + secondSat);
}
}
向后兼容
旧 Date 和 Calendar 新增 toInstant() 方法,可转换为新 API:
import java.util.Date;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
public class Java8Tester {
public void testBackwardCompatibility() {
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, zone);
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zone);
System.out.println("旧 Date: " + date);
System.out.println("LocalDateTime: " + ldt);
System.out.println("ZonedDateTime: " + zdt);
}
}
运行结果示例
旧 Date: Wed Dec 10 05:44:06 UTC 2014 LocalDateTime: 2014-12-10T05:44:06.635 ZonedDateTime: 2014-12-10T05:44:06.635Z[Etc/UTC]
java
- C++日期时间完全指南:ctime库、tm结构体与格式化输出
- Python 获取当前日期和时间:实用指南
- 精通 Java 运算符:类型、语法与实战示例
- Java 注释:类型、用法与最佳实践
- 精通 Java 接口:概念、实现与最佳实践
- 精通 Java Try-With-Resources:自动资源管理详解
- Java 注解详解:类型、放置位置与实战示例
- 掌握 Java 日期时间:构造器、方法与最佳实践
- Java 10 引入基于时间的发布版本控制:可预测的半年更新节奏
- Java 10 新增 70+ API 与特性详解
- Java 9 (JDK 9) 核心特性与优势全览
- Java 9 进程 API 改进:ProcessHandle 与异步控制