Java 8日期时间API完全指南

一、Java 8 之前的日期时间 API 问题

1. 设计缺陷

  • Date 类:既包含日期又包含时间,且时间以毫秒数存储,设计混乱,Date可变,线程不安全

  • Calendar 类:月份从0开始(0=一月),不符合人类直觉,反人类设计

  • SimpleDateFormat 非线程安全:多线程环境下需要额外同步

2. 代码示例 - 旧API的坑

  • ❌ 月份从0开始,年份要减1900

  • ❌ Date可变,线程不安全

  • ❌ SimpleDateFormat线程不安全

  • ❌ API设计混乱

  • ❌ 时区处理复杂

java 复制代码
// 1. 月份从0开始
Date date = new Date(2025-1900, 0, 1); // 2025年1月1日?实际上是2025年1月1日
// 实际上月份参数0表示一月

// 2. SimpleDateFormat 线程不安全
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 多线程使用会抛出异常

// 3. 日期计算复杂
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 5); // 增加5天
// 代码冗长,可读性差

// 4. 时区处理混乱
Date date = new Date();
// 默认使用系统时区,转换麻烦

Date now = newDate();
now.setTime(0); // 可以随意修改,多线程下危险!

二、Java 8 日期时间 API 核心类

1. 主要类层次结构

bash 复制代码
java.time
├── LocalDate           // 日期(年-月-日)
├── LocalTime           // 时间(时-分-秒-纳秒)
├── LocalDateTime       // 日期+时间
├── ZonedDateTime       // 带时区的日期时间
├── Instant             // 时间戳(Unix时间)
├── Duration            // 时间间隔(秒,纳秒)
├── Period              // 日期间隔(年,月,日)
└── DateTimeFormatter   // 格式化器

2.Java 8新API的优势 ✨

java 复制代码
// 新API:简洁、清晰、不可变
LocalDate date = LocalDate.of(2025, 1, 15); // 就是2025年1月15日
LocalDate tomorrow = date.plusDays(1); // 返回新对象,原对象不变

// 线程安全的格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
Stringstr = date.format(formatter); // 完全线程安全

// 流畅的API
LocalDateTime now = LocalDateTime.now()
    .plusDays(7)
    .minusHours(2)
    .withMinute(30);

三、核心类详解

1. LocalDate - 本地日期

java 复制代码
// 创建
LocalDate today = LocalDate.now();
LocalDate specificDate = LocalDate.of(2025, 12, 25);
LocalDate parsedDate = LocalDate.parse("2025-12-25");

// 操作
LocalDate tomorrow = today.plusDays(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate previousYear = today.minusYears(1);

// 获取信息
int year = today.getYear();
Month month = today.getMonth();  // 返回Month枚举
int dayOfMonth = today.getDayOfMonth();
DayOfWeek dayOfWeek = today.getDayOfWeek();

// 判断
boolean isLeapYear = today.isLeapYear();
boolean isBefore = today.isBefore(LocalDate.of(2025, 1, 1));

2. LocalTime - 本地时间

java 复制代码
// 创建
LocalTime now = LocalTime.now();
LocalTime specificTime = LocalTime.of(14, 30, 45);  // 14:30:45
LocalTime parsedTime = LocalTime.parse("14:30:45");

// 操作
LocalTime plusHours = now.plusHours(2);
LocalTime minusMinutes = now.minusMinutes(30);

// 获取信息
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();

3. LocalDateTime - 本地日期时间

java 复制代码
// 创建
LocalDateTime now = LocalDateTime.now();
LocalDateTime specificDateTime = LocalDateTime.of(2025, 12, 25, 14, 30);
LocalDateTime combined = LocalDateTime.of(today, now);

// 转换
LocalDate date = now.toLocalDate();
LocalTime time = now.toLocalTime();

// 操作
LocalDateTime nextWeek = now.plusWeeks(1);
LocalDateTime lastHour = now.minusHours(1);

4. Instant - 时间戳

java 复制代码
// 创建
Instant now = Instant.now();
Instant specific = Instant.ofEpochSecond(1700000000L);

// 转换
Instant fromDate = date.atStartOfDay(ZoneId.systemDefault()).toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(now, ZoneId.systemDefault());

// 计算
Instant plusSeconds = now.plusSeconds(3600);
Duration between = Duration.between(now, plusSeconds);

5. ZonedDateTime - 带时区日期时间

java 复制代码
// 创建
ZonedDateTime nowInShanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zoned = ZonedDateTime.of(
    LocalDateTime.now(), 
    ZoneId.of("America/New_York")
);

// 时区转换
ZonedDateTime nowTime= ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = nowTime.withZoneSameInstant(
    ZoneId.of("America/New_York")
);

// 获取所有可用时区
Set<String> allZones = ZoneId.getAvailableZoneIds();

6. Duration 和 Period

java 复制代码
// Duration - 时间间隔(精确到纳秒)
Duration duration = Duration.between(
    LocalTime.of(14, 0), 
    LocalTime.of(16, 30)
);
long hours = duration.toHours();  // 2
long minutes = duration.toMinutes();  // 150

// Period - 日期间隔(年、月、日)
Period period = Period.between(
    LocalDate.of(2025, 1, 1),
    LocalDate.of(2025, 12, 31)
);
int months = period.getMonths();  // 11
int days = period.getDays();      // 30

四、格式化与解析

1. DateTimeFormatter

java 复制代码
// 预定义格式器
LocalDateTime now = LocalDateTime.now();
String isoFormat = now.format(DateTimeFormatter.ISO_DATE_TIME);

// 自定义格式
DateTimeFormatter formatter = DateTimeFormatter
    .ofPattern("yyyy-MM-dd HH:mm:ss")
    .withLocale(Locale.CHINA);

// 格式化
String formatted = now.format(formatter);

// 解析
LocalDateTime parsed = LocalDateTime.parse("2025-12-25 14:30:00", formatter);

// 本地化格式
DateTimeFormatter germanFormatter = DateTimeFormatter
    .ofLocalizedDateTime(FormatStyle.MEDIUM)
    .withLocale(Locale.GERMAN);

五、实用操作示例

1. 日期计算

java 复制代码
// 计算两个日期之间的天数
long daysBetween = ChronoUnit.DAYS.between(
    LocalDate.of(2025, 1, 1),
    LocalDate.of(2025, 12, 31)
);

// 获取本月第一天和最后一天
LocalDate firstDay = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());

// 获取下个周一
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

2. 时区处理

java 复制代码
// 获取当前所有时区的当前时间
Map<String, LocalDateTime> timesInAllZones = 
    ZoneId.getAvailableZoneIds().stream()
          .collect(Collectors.toMap(
              zone -> zone,
              zone -> LocalDateTime.now(ZoneId.of(zone))
          ));

// 判断是否夏令时
ZoneId zone = ZoneId.of("Asia/Shanghai");
ZonedDateTime zdt = ZonedDateTime.now(zone);
boolean isDST = zdt.getZone().getRules().isDaylightSavings(zdt.toInstant());

3. 业务常见场景

java 复制代码
// 1. 计算年龄
public int calculateAge(LocalDate birthDate) {
    return Period.between(birthDate, LocalDate.now()).getYears();
}

// 2. 计算工作日(排除周末)
public long calculateWorkingDays(LocalDate start, LocalDate end) {
    return Stream.iterate(start, date -> date.plusDays(1))
                 .limit(ChronoUnit.DAYS.between(start, end))
                 .filter(date -> date.getDayOfWeek() != DayOfWeek.SATURDAY
                              && date.getDayOfWeek() != DayOfWeek.SUNDAY)
                 .count();
}

// 3. 定时任务执行时间计算
public LocalDateTime nextExecutionTime(LocalDateTime lastExecution, 
                                       Duration interval) {
    return lastExecution.plus(interval);
}

六、与传统API的互操作

java 复制代码
// Date 转 LocalDateTime
Date oldDate = new Date();
LocalDateTime newDateTime = oldDate.toInstant()
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime();

// LocalDateTime 转 Date
LocalDateTime ldt = LocalDateTime.now();
Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

// Calendar 转 LocalDate
Calendar calendar = Calendar.getInstance();
LocalDate localDate = LocalDate.of(
    calendar.get(Calendar.YEAR),
    calendar.get(Calendar.MONTH) + 1,  // 注意月份+1
    calendar.get(Calendar.DAY_OF_MONTH)
);

七、最佳实践

1. 选择正确的类

  • 只关心日期 → LocalDate

  • 只关心时间 → LocalTime

  • 需要日期时间 → LocalDateTime

  • 需要时区 → ZonedDateTime

  • 时间戳存储 → Instant

2. 线程安全

java 复制代码
// DateTimeFormatter 是线程安全的,可以共享
private static final DateTimeFormatter FORMATTER = 
    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

public String formatDateTime(LocalDateTime dateTime) {
    return dateTime.format(FORMATTER);  // 线程安全
}

3. 避免空指针

java 复制代码
public Optional<LocalDate> parseDate(String dateStr) {
    try {
        return Optional.of(LocalDate.parse(dateStr));
    } catch (DateTimeParseException e) {
        return Optional.empty();
    }
}

八、总结对比

特性 旧API (java.util.Date) 新API (java.time)
设计清晰度 混乱,一锅炖 职责单一,清晰
线程安全 不安全 所有类不可变,线程安全
月份表示 0-11(0=一月) 1-12(符合直觉)
格式化 SimpleDateFormat(非线程安全) DateTimeFormatter(线程安全)
时区处理 复杂易错 内置支持完善
日期计算 繁琐 简单直观
可读性 优秀

Java 8 日期时间 API 的设计遵循了以下原则:

  1. 不可变性:所有核心类都是不可变的

  2. 清晰性:类名和方法名明确表达意图

  3. 流畅性:方法链式调用,代码流畅

  4. 扩展性:支持自定义的时间调节器

  5. 完整性:覆盖了所有常见的日期时间操作场景

建议所有新项目都使用 Java 8 日期时间 API,对于老项目逐步迁移替换。

相关推荐
毕设源码-赖学姐2 小时前
【开题答辩全过程】以 高校教学质量监控平台为例,包含答辩的问题和答案
java·eclipse
高山上有一只小老虎2 小时前
翻之矩阵中的行
java·算法
yangpipi-2 小时前
《C++并发编程实战》 第4章 并发操作的同步
开发语言·c++
西南胶带の池上桜2 小时前
1.Pytorch模型应用(线性与非线性预测)
人工智能·pytorch·python
火钳游侠2 小时前
java单行注释,多行注释,文档注释
java·开发语言
有趣的我3 小时前
C++ 多态介绍
开发语言·c++
code bean3 小时前
【CMake】为什么需要清理 CMake 缓存文件?深入理解 CMake 生成器切换机制
java·spring·缓存
selt7913 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
RestCloud3 小时前
智能制造的底层基建:iPaaS 如何统一 ERP、MES 与 WMS 的数据流
java·wms·erp·数据传输·ipaas·mes·集成平台