Java 8 日期时间 API 全面指南:使用技巧与场景实践

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

日期类在日常开发中很常见,我们习惯了使用各种DateUtils工具包,直接处理java.util.Date类,包括计算、格式化、解析等操作。但是,在使用中存在线程安全等问题。

Java 8 引入的 java.time 包彻底解决了旧版 java.util.DateCalendar 的设计缺陷,提供了线程安全**、**直观易用且功能全面的日期时间处理方案。

习惯了以前的工具类,反而觉得Java 8中的日期类用起来不怎么顺手,本文将深入解析核心类、使用技巧及实际场景应用。

02 核心类解析

类名 描述 示例
LocalDate 仅含日期(年-月-日) 生日、会议日期
LocalTime 仅含时间(时:分:秒.纳秒) 打卡时间、营业时间
LocalDateTime 日期+时间(无时区) 本地事件记录
ZonedDateTime 带时区的完整日期时间 跨时区会议、航班时刻
Instant 时间戳(Unix 时间,精确到纳秒) 日志记录、性能计时
Period 日期区间(年/月/日) 计算年龄、项目周期
Duration 时间间隔(秒/纳秒) 程序执行时间、倒计时
DateTimeFormatter 日期格式化/解析 日期字符串转换

03 关键使用技巧

因为都是关于日期的类,所以他们之间都用几乎相同的API,我们以LocalDateTime为代表介绍其中的用法。

3.1 日期的创建和获取

java 复制代码
@Test
void test01() {
    // 获取当前日期
    LocalDateTime now = LocalDateTime.now();
    System.out.println(now); 
    //2025-07-17T18:19:45.927848800

    // 自定义日期
    LocalDateTime localDateTime = LocalDateTime.of(2025, 7, 17, 12, 30, 20);
    System.out.println(localDateTime);
    // 2025-07-17T12:30:20

    // 通过秒和偏移量获取日期:偏移8小时
    LocalDateTime localDateTime1 = LocalDateTime.ofEpochSecond(System.currentTimeMillis()/1000, 8888, ZoneOffset.ofHours(8));
    System.out.println(localDateTime1);
    // 2025-07-17T18:19:45.000008888

    // 通过秒和偏移量获取日期:使用当前时区时间
    LocalDateTime localDateTime2 = LocalDateTime.ofEpochSecond(System.currentTimeMillis()/1000, 8888, ZoneOffset.from(ZonedDateTime.now()));
    System.out.println(localDateTime2);
    // 2025-07-17T18:19:45.000008888
}

需要说明的是LocalDateTime.ofEpochSecond()方法,通过当前的秒数+纳秒数+时区的偏移/时区时间来构建LocalDateTime对象。

通过LocalDateTime可以获取日期的年、月、日、时、分、秒、纳秒等参数。如图:

3.2 日期的运算和调整

java 复制代码
@Test
void test02() {
    // 获取当前日期
    LocalDateTime now = LocalDateTime.now();
    System.out.println(now);
    // 2025-07-17T18:19:45.927848800
    
    // 时间计算:- 时间段
    LocalDateTime minus = now.minus(Duration.ofDays(1));
    System.out.println(minus);
    // 2025-07-16T18:19:45.927848800

    // 时间计算:+ 时间段
    LocalDateTime plus = now.plusDays(1);
    System.out.println(plus);
    // 2025-07-18T18:19:45.927848800
    
    // 获取本月第一天
    LocalDateTime with = now.with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0).withNano(0);
    // LocalDateTime with = LocalDateTime.of(now.with(TemporalAdjusters.firstDayOfMonth()).toLocalDate(), LocalTime.MIN);
    System.out.println(with);
    // 2025-07-01T00:00

    // 获取本月最后一天
    LocalDateTime with2 = now.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(999999999);
    // LocalDateTime with2 = LocalDateTime.of(now.with(TemporalAdjusters.firstDayOfMonth()).toLocalDate(), LocalTime.MAX);
    System.out.println(with2);
    // 2025-07-31T23:59:59.999999999
  
}

日期的调整需要用到调整器TemporalAdjusters

3.3 日期的比较

日期的比较和之前的差别不大

java 复制代码
@Test
void test03() {
    System.out.println(now.compareTo(now.plusSeconds(2)));
    // -1
    System.out.println(now.isEqual(now));
    // true
    System.out.println(now.isAfter(now.plusSeconds(2)));
    // false
    System.out.println(now.isBefore(now.plusSeconds(2)));
    // true
}

3.4 时区的处理

java 复制代码
@Test
void test04() {
    // 时区转化
    ZonedDateTime now = ZonedDateTime.now();
    System.out.println(now);
    // 2025-07-17T18:40:30.202283400+08:00[Asia/Shanghai]

    ZonedDateTime zonedDateTime = now.withZoneSameInstant(ZoneId.of("America/New_York"));
    System.out.println(zonedDateTime);
    // 2025-07-17T06:40:30.202283400-04:00[America/New_York]
}

3.5 时间间隔计算

java 复制代码
@Test
void test05() {
    // 不会向上取整
    long between = ChronoUnit.HOURS.between(LocalDateTime.now(), LocalDateTime.now().plusMinutes(40));
    System.out.println(between);
    // 0

    Period between1 = Period.between(LocalDate.now(), LocalDate.now().plusDays(2));
    System.out.println(between1.getDays());
    // 2
    System.out.println(between1.getYears());
    // 0

    long minutes = Duration.ofSeconds(59).toMinutes();
    System.out.println(minutes);
    // 0
}

这里需要注意的是,时间间隔的计算不会向上取整

3.6 高精度时间

java.time.Instant该采用的时间格式是yyyy-MM-ddTHH:mm:ss.SSSSSSSZ,比LocalDateTime多了一个后缀Z,表示该时间采用 ‌协调世界时(UTC)

java 复制代码
@Test
void test06() {
    Instant now = Instant.now();
    System.out.println(now);
    // 2025-07-17T10:47:08.873287Z

    // 转毫秒
    long epochMilli = now.toEpochMilli();
    System.out.println(epochMilli);
    // 1752749228873

    // 转秒
    System.out.println(now.getEpochSecond());
    // 1752749228
}

为什么这类要单独说呢,因为该类将是java.util.Datejava.time.LocalDateTime的桥梁。

3.7 日期之间的转化

java 复制代码
@Test
void test07() {
    // 转化
    Instant instant = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
    System.out.println(instant);
    // 2025-07-17T10:49:36.742740400Z

    LocalDate localDate = LocalDate.ofInstant(instant, ZoneId.systemDefault());
    System.out.println(localDate);
    // 2025-07-17

    LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    System.out.println(localDateTime);
    // 2025-07-17T18:49:36.742740400

    Date from = Date.from(instant);
    System.out.println(from);
    // Thu Jul 17 18:49:36 CST 2025

    Instant instant2 = from.toInstant();
    System.out.println(instant2);
    // 2025-07-17T10:49:36.742Z
}

3.8 日期格式化和解析

java 复制代码
@Test
void test06() {
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    LocalDateTime now = LocalDateTime.now();
    System.out.println(now.format(dateTimeFormatter));
    // 2025-07-17

    TemporalAccessor parse = dateTimeFormatter.parse("2025-02-04");
    System.out.println(LocalDate.from(parse));
    // 2025-02-04

    LocalDate parse1 = LocalDate.parse("2025-02-04", dateTimeFormatter);
    System.out.println(parse1);
    // 2025-02-04
}

04 小结

Java 8 日期时间 API 的核心优势:

  • 线程安全:所有类均为不可变对象
  • 链式调用 :流畅的 API 设计(如 plusDays().withHour()
  • 明确语义LocalDateZonedDateTime 等命名清晰
  • 强大工具 :内置 TemporalAdjustersChronoUnit

关键建议

  • 生产环境统一使用 java.time.format.DateTimeFormatter 替代 SimpleDateFormat
  • 复杂日期逻辑优先使用 TemporalAdjusters 而非手动计算
  • 分布式系统强制采用 UTC 时间存储(Instant

通过合理运用这些工具,可显著提升日期时间处理的准确性可维护性 ,彻底告别 Calendar.set(Calendar.MONTH, -1) 式的"魔法数字"陷阱!

相关推荐
秋千码途3 分钟前
小架构step系列14:白盒集成测试原理
java·架构·集成测试
XuanXu6 分钟前
MCP简单研究以及介绍
后端·ai编程·cursor
佛说"獨"7 分钟前
Docker swarm集群部署,包含compose.yml文件详情
java·docker·容器
该用户已不存在8 分钟前
我不管,我的 Claude Code 必须用上 Gemini 2.5 Pro
前端·人工智能·后端
FrigidCrow9 分钟前
最新实践LangGraph的记忆体
后端
iOS开发上架哦10 分钟前
iOS加固工具有哪些?项目场景下的组合策略与实战指南
后端
Bella_chene12 分钟前
IDEA 中使用 <jsp:useBean>动作指令时,class属性引用无效
java·intellij-idea·jsp·java web开发
都叫我大帅哥13 分钟前
🌌 星际迷航版Java微服务探索:Eureka服务发现全解
java·spring cloud
expect7g13 分钟前
Flink-反压-1.基本概念
后端·flink
Ashlee_code13 分钟前
裂变时刻:全球关税重构下的券商交易系统跃迁路线图(2025-2027)
java·大数据·数据结构·python·云原生·区块链·perl