Java 8时间日期API完全指南

Java 8时间日期API完全指南

Java 8 引入了全新的时间日期 API(java.time 包),彻底解决了旧 Date/Calendar 的设计缺陷。本文全面讲解新 API 的使用方法。

旧 API 的问题

  1. 设计糟糕Date 既表示日期又表示时间,月份从 0 开始
  2. 线程不安全SimpleDateFormat 并发使用会出问题
  3. 时区处理复杂TimeZone 设计混乱
  4. 不可变性缺失Date 对象可修改

核心类概览

说明
LocalDate 日期(年-月-日)
LocalTime 时间(时:分:秒)
LocalDateTime 日期+时间
ZonedDateTime 带时区的日期时间
Instant 时间戳(秒/纳秒)
Duration 时间间隔(秒/纳秒)
Period 日期间隔(年/月/日)
DateTimeFormatter 格式化

LocalDate 日期操作

// 获取当前日期
LocalDate today = LocalDate.now();

// 创建指定日期
LocalDate date = LocalDate.of(2024, 5, 12);

// 解析字符串
LocalDate parsed = LocalDate.parse("2024-05-12");

// 获取属性
int year = date.getYear();
Month month = date.getMonth(); // MAY
int day = date.getDayOfMonth();
DayOfWeek week = date.getDayOfWeek(); // SUNDAY

// 日期计算
LocalDate tomorrow = date.plusDays(1);
LocalDate lastMonth = date.minusMonths(1);
LocalDate nextYear = date.plusYears(1);

// 调整日期
LocalDate firstDay = date.withDayOfMonth(1);
LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
LocalDate nextMonday = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

LocalTime 时间操作

LocalTime now = LocalTime.now();
LocalTime time = LocalTime.of(14, 30, 0);

// 比较
boolean isBefore = now.isBefore(LocalTime.NOON);

// 截断
LocalTime truncated = now.truncatedTo(ChronoUnit.MINUTES); // 去掉秒

LocalDateTime 日期时间

LocalDateTime now = LocalDateTime.now();
LocalDateTime dateTime = LocalDateTime.of(2024, 5, 12, 14, 30);

// 与LocalDate、LocalTime互转
LocalDate date = now.toLocalDate();
LocalTime time = now.toLocalTime();
LocalDateTime combined = date.atTime(time);

ZonedDateTime 时区处理

// 系统默认时区
ZonedDateTime now = ZonedDateTime.now();

// 指定时区
ZonedDateTime ny = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));

// 时区转换
ZonedDateTime converted = ny.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));

// 可用时区列表
Set<String> zones = ZoneId.getAvailableZoneIds();

Instant 时间戳

// 当前时间戳
Instant now = Instant.now();

// 从纪元开始计算
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());

// 获取秒和纳秒
long seconds = now.getEpochSecond();
int nanos = now.getNano();

// 与Date互转
Date date = Date.from(instant);
Instant instant2 = date.toInstant();

Duration 和 Period

// Duration:时间间隔(时分秒)
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);
Duration duration = Duration.between(start, end);
long hours = duration.toHours(); // 8
long minutes = duration.toMinutes(); // 510

// Period:日期间隔(年月日)
LocalDate startDate = LocalDate.of(2024, 1, 1);
LocalDate endDate = LocalDate.of(2024, 5, 12);
Period period = Period.between(startDate, endDate);
int months = period.getMonths(); // 4
int days = period.getDays(); // 11

DateTimeFormatter 格式化

// 预定义格式
DateTimeFormatter iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

// 自定义格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String formatted = LocalDateTime.now().format(formatter);
// 结果: 2024年05月12日 14:30:00

// 解析
LocalDateTime parsed = LocalDateTime.parse("2024-05-12 14:30:00",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

与旧 API 互转

// Date -> LocalDateTime
Date date = new Date();
LocalDateTime dateTime = date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();

// LocalDateTime -> Date
Date newDate = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());

// Calendar -> ZonedDateTime
Calendar cal = Calendar.getInstance();
ZonedDateTime zdt = cal.toInstant().atZone(cal.getTimeZone().toZoneId());

线程安全

所有 java.time 类都是不可变且线程安全的:

// 可以安全地共享
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

// 多线程并发使用
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
String formatted = LocalDate.now().format(formatter);
System.out.println(formatted);
});
}

最佳实践

  1. 新代码全部使用 java.time:不再使用 Date/Calendar
  2. 数据库映射:JPA 2.2+ 支持 java.time 类型
  3. JSON 序列化:配置 Jackson/Gson 支持 JSR-310
  4. API 设计:方法参数使用 LocalDate/LocalDateTime 而非 String
// Jackson 支持
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

总结

Java 8 时间 API 的设计原则:

  • 清晰分离:日期、时间、日期时间、时区各自独立
  • 不可变性:所有对象不可修改,线程安全
  • 方法命名一致plusXxxminusXxxwithXxx

掌握 java.time 包是现代 Java 开发的基本要求,它能让你避免大量时间处理相关的 Bug。


   转载规则


《Java 8时间日期API完全指南》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录