Java 日期时间 API 完整学习指南:从 LocalDate 到 ZonedDateTime | 星雨笔录

本文基于 JDK 8+ / JDK 21 的现代日期时间 API,对 Java 中最常用的 LocalDate、LocalTime、LocalDateTime、DateTimeFormatter、Period、Duration、ChronoUnit、Instant、ZoneId、ZonedDateTime、OffsetDateTime、TemporalAdjusters、YearMonth 等 API 进行系统整理。 全文按照“API 总表 → 单独示例 → 类型转换 → Web 开发映射 → 综合案例”展开,并对核心方法进行连续编号。掌握本文内容后,应能够独立完成生日计算、订单过期、时间格式

一、Java 日期时间体系总览

Java 现代日期时间 API 位于:

java.time

真正需要建立的是下面这套认知:

日期
 ↓
LocalDate

时间
 ↓
LocalTime

日期 + 时间
 ↓
LocalDateTime

格式转换
 ↓
DateTimeFormatter

日期差
 ↓
Period

精确时间差
 ↓
Duration / ChronoUnit

绝对时间点
 ↓
Instant

时区
 ↓
ZoneId

日期时间 + 时区
 ↓
ZonedDateTime / OffsetDateTime

二、LocalDate:日期

LocalDate 表示:

2026-09-17

它只有:

年 + 月 + 日

没有时分秒,也没有时区。

2.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
1 当前日期 LocalDate now() 无 获取系统当前日期
2 指定日期 LocalDate of() int year, int month, int dayOfMonth 创建指定日期
3 解析后的日期 LocalDate parse() CharSequence text 字符串转日期
4 年份 int getYear() 无 获取年份
5 月份数字 int getMonthValue() 无 获取月份,范围 1~12
6 月份枚举 Month getMonth() 无 获取月份枚举
7 日 int getDayOfMonth() 无 获取当月第几天
8 星期 DayOfWeek getDayOfWeek() 无 获取星期
9 新日期 LocalDate plusDays() long daysToAdd 增加天数
10 新日期 LocalDate plusMonths() long monthsToAdd 增加月份
11 新日期 LocalDate plusYears() long yearsToAdd 增加年份
12 新日期 LocalDate minusDays() long daysToSubtract 减少天数
13 新日期 LocalDate minusMonths() long monthsToSubtract 减少月份
14 新日期 LocalDate minusYears() long yearsToSubtract 减少年份
15 新日期 LocalDate withYear() int year 修改年份
16 新日期 LocalDate withMonth() int month 修改月份
17 新日期 LocalDate withDayOfMonth() int dayOfMonth 修改日期
18 是否更早 boolean isBefore() ChronoLocalDate other 判断是否在另一个日期之前
19 是否更晚 boolean isAfter() ChronoLocalDate other 判断是否在另一个日期之后
20 是否相等 boolean isEqual() ChronoLocalDate other 判断日期是否相同
21 本月天数 int lengthOfMonth() 无 获取当前月份天数
22 是否闰年 boolean isLeapYear() 无 判断当前年份是否为闰年
23 当天零点 LocalDateTime atStartOfDay() 无 转换为当天 00:00 的日期时间

2.2 示例

import java.time.LocalDate;

public class LocalDateDemo {
    public static void main(String[] args) {

        LocalDate today = LocalDate.now();

        LocalDate birthday =
                LocalDate.of(2004, 5, 20);

        LocalDate date =
                LocalDate.parse("2026-09-17");

        System.out.println(date.getYear());
        System.out.println(date.getMonthValue());
        System.out.println(date.getMonth());
        System.out.println(date.getDayOfMonth());
        System.out.println(date.getDayOfWeek());

        System.out.println(date.plusDays(10));
        System.out.println(date.plusMonths(1));
        System.out.println(date.plusYears(1));

        System.out.println(date.minusDays(7));
        System.out.println(date.minusMonths(1));
        System.out.println(date.minusYears(1));

        System.out.println(date.withYear(2030));
        System.out.println(date.withMonth(12));
        System.out.println(date.withDayOfMonth(1));

        LocalDate future = LocalDate.of(2030, 1, 1);

        System.out.println(date.isBefore(future));
        System.out.println(date.isAfter(future));
        System.out.println(date.isEqual(future));

        System.out.println(date.lengthOfMonth());
        System.out.println(date.isLeapYear());

        System.out.println(date.atStartOfDay());
    }
}

三、LocalTime:时间

LocalTime 表示:

10:30:45

包含:

时 + 分 + 秒 + 纳秒

不包含日期和时区。

3.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
24 当前时间 LocalTime now() 无 获取当前时间
25 指定时间 LocalTime of() int hour, int minute 创建指定时分
26 指定时间 LocalTime of() int hour, int minute, int second 创建指定时分秒
27 时间对象 LocalTime parse() CharSequence text 字符串转时间
28 小时 int getHour() 无 获取小时
29 分钟 int getMinute() 无 获取分钟
30 秒 int getSecond() 无 获取秒
31 新时间 LocalTime plusHours() long hoursToAdd 增加小时
32 新时间 LocalTime plusMinutes() long minutesToAdd 增加分钟
33 新时间 LocalTime minusHours() long hoursToSubtract 减少小时
34 新时间 LocalTime minusMinutes() long minutesToSubtract 减少分钟
35 是否更早 boolean isBefore() LocalTime other 比较时间先后
36 是否更晚 boolean isAfter() LocalTime other 比较时间先后

3.2 示例

import java.time.LocalTime;

public class LocalTimeDemo {
    public static void main(String[] args) {

        LocalTime now = LocalTime.now();

        LocalTime start =
                LocalTime.of(8, 30);

        LocalTime end =
                LocalTime.of(18, 0, 0);

        LocalTime time =
                LocalTime.parse("12:30:45");

        System.out.println(time.getHour());
        System.out.println(time.getMinute());
        System.out.println(time.getSecond());

        System.out.println(time.plusHours(2));
        System.out.println(time.plusMinutes(30));

        System.out.println(time.minusHours(1));
        System.out.println(time.minusMinutes(10));

        System.out.println(start.isBefore(end));
        System.out.println(start.isAfter(end));
    }
}

四、LocalDateTime:日期 + 时间

这是 Java Web 开发中非常常用的日期类型。

2026-09-17T10:30:20

常见业务:

用户创建时间
文章发布时间
订单创建时间
更新时间
登录时间
支付时间

4.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
37 当前日期时间 LocalDateTime now() 无 获取当前日期时间
38 指定日期时间 LocalDateTime of() year, month, day, hour, minute 创建指定日期时间
39 日期时间 LocalDateTime parse() CharSequence text 解析字符串
40 日期 LocalDate toLocalDate() 无 提取日期部分
41 时间 LocalTime toLocalTime() 无 提取时间部分
42 新对象 LocalDateTime plusDays() long days 增加天数
43 新对象 LocalDateTime plusHours() long hours 增加小时
44 新对象 LocalDateTime plusMinutes() long minutes 增加分钟
45 新对象 LocalDateTime minusDays() long days 减少天数
46 是否更早 boolean isBefore() ChronoLocalDateTime<?> other 比较日期时间
47 是否更晚 boolean isAfter() ChronoLocalDateTime<?> other 比较日期时间
48 带时区时间 ZonedDateTime atZone() ZoneId zone 给日期时间绑定时区

4.2 示例

import java.time.LocalDateTime;

public class LocalDateTimeDemo {

    public static void main(String[] args) {

        LocalDateTime now =
                LocalDateTime.now();

        LocalDateTime time =
                LocalDateTime.of(
                        2026,
                        9,
                        17,
                        10,
                        30
                );

        System.out.println(now);

        System.out.println(
                time.toLocalDate()
        );

        System.out.println(
                time.toLocalTime()
        );

        System.out.println(
                time.plusDays(7)
        );

        System.out.println(
                time.plusHours(2)
        );

        System.out.println(
                time.plusMinutes(30)
        );

        System.out.println(
                time.minusDays(1)
        );

        System.out.println(
                now.isBefore(time)
        );

        System.out.println(
                now.isAfter(time)
        );
    }
}

五、日期时间对象为什么都是不可变对象

LocalDate、LocalTime、LocalDateTime 等核心类都是不可变对象。

这和 String 非常相似。

例如:

LocalDate date =
        LocalDate.of(2026, 9, 17);

date.plusDays(1);

System.out.println(date);

结果仍然是:

2026-09-17

因为:

date.plusDays(1);

不会修改原对象,而是返回一个新的 LocalDate。

正确:

date = date.plusDays(1);

或者:

LocalDate tomorrow =
        date.plusDays(1);

所以一定要形成条件反射:

plusXxx()、minusXxx()、withXxx() 大多数情况下都会返回一个新对象。

六、DateTimeFormatter:格式化

DateTimeFormatter 负责:

时间对象 ⇄ String

这是 Java 日期时间开发必须熟练掌握的工具。

6.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
49 格式器 DateTimeFormatter ofPattern() String pattern 根据模式创建格式器
50 格式化字符串 String format() TemporalAccessor temporal 时间对象转字符串
51 日期 LocalDate LocalDate.parse() CharSequence text, DateTimeFormatter formatter 按指定格式解析日期
52 日期时间 LocalDateTime LocalDateTime.parse() CharSequence text, DateTimeFormatter formatter 按指定格式解析日期时间

6.2 常用格式符号

格式字符 含义 示例
yyyy 年 2026
MM 月 09
dd 日 17
HH 24 小时制小时 18
hh 12 小时制小时 06
mm 分钟 30
ss 秒 45

必须注意:

MM = 月
mm = 分钟

HH = 24 小时制
hh = 12 小时制

6.3 时间对象 → String

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class FormatterDemo {

    public static void main(String[] args) {

        LocalDateTime now =
                LocalDateTime.now();

        DateTimeFormatter formatter =
                DateTimeFormatter.ofPattern(
                        "yyyy-MM-dd HH:mm:ss"
                );

        String result =
                now.format(formatter);

        System.out.println(result);
    }
}

结果:

2026-09-17 10:30:21

6.4 String → LocalDateTime

String str =
        "2026-09-17 18:30:00";

DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern(
                "yyyy-MM-dd HH:mm:ss"
        );

LocalDateTime time =
        LocalDateTime.parse(
                str,
                formatter
        );

System.out.println(time);

必须记住这条转换链:

String
  ↓ parse()
LocalDateTime

LocalDateTime
  ↓ format()
String

七、Period:日期之间相差多久

Period 主要用于:

年
月
日

适合:

年龄
入职年限
两个日期之间相差多少年月日

7.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
53 日期差 Period between() LocalDate startDate, LocalDate endDate 计算两个日期之间的年月日差
54 年 int getYears() 无 获取年份部分
55 月 int getMonths() 无 获取月份部分
56 日 int getDays() 无 获取天数部分

7.2 示例:计算年龄

import java.time.LocalDate;
import java.time.Period;

public class PeriodDemo {

    public static void main(String[] args) {

        LocalDate birthday =
                LocalDate.of(2004, 5, 20);

        LocalDate today =
                LocalDate.now();

        Period period =
                Period.between(
                        birthday,
                        today
                );

        System.out.println(
                period.getYears()
        );

        System.out.println(
                period.getMonths()
        );

        System.out.println(
                period.getDays()
        );
    }
}

八、Duration:精确时间间隔

Duration 主要计算:

时
分
秒
纳秒

适合:

任务耗时
视频时长
接口执行时间
登录持续时间
订单倒计时

8.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
57 时间差 Duration between() Temporal startInclusive, Temporal endExclusive 计算时间间隔
58 Duration Duration ofSeconds() long seconds 根据秒数创建 Duration
59 小时数 long toHours() 无 转换为完整小时数
60 分钟数 long toMinutes() 无 转换为完整分钟数
61 天数 long toDays() 无 转换为完整天数
62 秒数 long getSeconds() 无 获取总秒数

8.2 示例

import java.time.Duration;
import java.time.LocalDateTime;

public class DurationDemo {

    public static void main(String[] args) {

        LocalDateTime start =
                LocalDateTime.of(
                        2026, 9, 17,
                        10, 0
                );

        LocalDateTime end =
                LocalDateTime.of(
                        2026, 9, 17,
                        12, 30
                );

        Duration duration =
                Duration.between(
                        start,
                        end
                );

        System.out.println(
                duration.toHours()
        );

        System.out.println(
                duration.toMinutes()
        );

        System.out.println(
                duration.getSeconds()
        );
    }
}

结果:

2
150
9000

注意:

duration.toHours();

返回的是完整小时数。

所以:

2小时30分钟

得到:

2

而不是:

2.5

九、ChronoUnit:直接计算时间单位差

ChronoUnit 非常适合直接问:

相差多少天?
相差多少小时?
相差多少分钟?

9.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
63 差值 long ChronoUnit.YEARS.between() Temporal start, Temporal end 计算年份差
64 差值 long ChronoUnit.MONTHS.between() Temporal start, Temporal end 计算月份差
65 差值 long ChronoUnit.DAYS.between() Temporal start, Temporal end 计算天数差
66 差值 long ChronoUnit.HOURS.between() Temporal start, Temporal end 计算小时差
67 差值 long ChronoUnit.MINUTES.between() Temporal start, Temporal end 计算分钟差
68 差值 long ChronoUnit.SECONDS.between() Temporal start, Temporal end 计算秒数差

9.2 示例

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class ChronoUnitDemo {

    public static void main(String[] args) {

        LocalDate start =
                LocalDate.of(
                        2026,
                        9,
                        1
                );

        LocalDate end =
                LocalDate.of(
                        2026,
                        9,
                        17
                );

        long days =
                ChronoUnit.DAYS.between(
                        start,
                        end
                );

        System.out.println(days);
    }
}

结果:

16

计算分钟:

long minutes =
        ChronoUnit.MINUTES.between(
                LocalDateTime.of(
                        2026, 9, 17,
                        10, 0
                ),
                LocalDateTime.of(
                        2026, 9, 17,
                        12, 30
                )
        );

System.out.println(minutes);

结果:

150

十、Period、Duration、ChronoUnit 怎么选

场景 推荐 API
22 年 3 个月 Period
2 小时 30 分钟 Duration
相差多少天 ChronoUnit.DAYS
相差多少分钟 ChronoUnit.MINUTES
计算年龄 Period
接口执行时间 Duration
两日期相差天数 ChronoUnit

形成这个理解:

Period
→ 人类日期单位:年月日

Duration
→ 精确时间间隔:时分秒

ChronoUnit
→ 直接得到某个单位的数字差值

十一、Instant:时间戳与绝对时间点

Instant 表示:

UTC 时间线上的一个绝对时间点。

例如:

Instant.now();

可能得到:

2026-09-17T02:30:00Z

其中:

Z = UTC

11.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
69 当前时间点 Instant now() 无 获取当前 UTC 时间点
70 Instant Instant ofEpochMilli() long epochMilli 毫秒时间戳转 Instant
71 毫秒时间戳 long toEpochMilli() 无 Instant 转毫秒时间戳
72 新 Instant Instant plusSeconds() long secondsToAdd 增加秒数
73 是否更早 boolean isBefore() Instant other 比较绝对时间点
74 是否更晚 boolean isAfter() Instant other 比较绝对时间点

11.2 示例

import java.time.Instant;

public class InstantDemo {

    public static void main(String[] args) {

        Instant now =
                Instant.now();

        System.out.println(now);

        long timestamp =
                now.toEpochMilli();

        System.out.println(timestamp);

        Instant instant =
                Instant.ofEpochMilli(
                        timestamp
                );

        System.out.println(instant);

        Instant future =
                now.plusSeconds(60);

        System.out.println(
                now.isBefore(future)
        );

        System.out.println(
                future.isAfter(now)
        );
    }
}

十二、ZoneId:时区

世界各地时区不同,例如:

北京:UTC+8
东京:UTC+9
纽约:UTC-4 / UTC-5
洛杉矶:UTC-7 / UTC-8

LocalDateTime 本身并不知道自己的时区。

例如:

LocalDateTime.of(
        2026,
        9,
        17,
        10,
        0
);

只表示:

2026-09-17 10:00

但不知道它究竟是:

北京 10:00
东京 10:00
纽约 10:00

所以需要:

ZoneId

12.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
75 系统默认时区 ZoneId systemDefault() 无 获取 JVM 默认时区
76 指定时区 ZoneId of() String zoneId 根据地区名称获取时区
77 所有时区 ID Set<String> getAvailableZoneIds() 无 获取 Java 支持的时区 ID

12.2 示例

import java.time.ZoneId;

public class ZoneIdDemo {

    public static void main(String[] args) {

        ZoneId system =
                ZoneId.systemDefault();

        System.out.println(system);

        ZoneId shanghai =
                ZoneId.of(
                        "Asia/Shanghai"
                );

        ZoneId tokyo =
                ZoneId.of(
                        "Asia/Tokyo"
                );

        ZoneId newYork =
                ZoneId.of(
                        "America/New_York"
                );

        System.out.println(shanghai);
        System.out.println(tokyo);
        System.out.println(newYork);
    }
}

实际开发更推荐:

Asia/Shanghai
America/New_York
Europe/London

而不是仅写:

GMT+8

因为地区型时区能够正确处理:

夏令时 DST
历史时区变化
地区时区规则

十三、ZonedDateTime:日期时间 + 时区

可以简单理解为:

LocalDateTime + ZoneId

例如:

2026-09-17T10:00+08:00[Asia/Shanghai]

13.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
78 当前带时区时间 ZonedDateTime now() 无 获取当前系统时区日期时间
79 带时区时间 ZonedDateTime of() LocalDateTime localDateTime, ZoneId zone 将日期时间绑定时区
80 新时区时间 ZonedDateTime withZoneSameInstant() ZoneId zone 保持同一瞬间转换时区
81 时区 ZoneId getZone() 无 获取时区
82 绝对时间点 Instant toInstant() 无 转换为 Instant
83 本地日期时间 LocalDateTime toLocalDateTime() 无 去掉时区信息

13.2 示例:北京时间转换为纽约时间

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class ZonedDateTimeDemo {

    public static void main(String[] args) {

        LocalDateTime local =
                LocalDateTime.of(
                        2026,
                        9,
                        17,
                        20,
                        0
                );

        ZoneId china =
                ZoneId.of(
                        "Asia/Shanghai"
                );

        ZonedDateTime chinaTime =
                local.atZone(china);

        System.out.println(chinaTime);

        ZonedDateTime newYorkTime =
                chinaTime.withZoneSameInstant(
                        ZoneId.of(
                                "America/New_York"
                        )
                );

        System.out.println(newYorkTime);
    }
}

其中:

withZoneSameInstant()

表示:

保持现实世界中的同一个瞬间不变,只改变观察这个时间点所使用的时区。

十四、OffsetDateTime:日期时间 + UTC 偏移量

OffsetDateTime 例如:

2026-09-17T10:30:00+08:00

它记录:

日期
+
时间
+
UTC 偏移量

与 ZonedDateTime 的区别:

OffsetDateTime
2026-09-17T10:00+08:00

ZonedDateTime
2026-09-17T10:00+08:00[Asia/Shanghai]

前者只知道:

+08:00

后者知道:

Asia/Shanghai

14.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
84 当前偏移日期时间 OffsetDateTime now() 无 获取当前日期时间和 UTC 偏移量
85 绝对时间点 Instant toInstant() 无 转换为 Instant

14.2 示例

import java.time.Instant;
import java.time.OffsetDateTime;

public class OffsetDateTimeDemo {

    public static void main(String[] args) {

        OffsetDateTime now =
                OffsetDateTime.now();

        System.out.println(now);

        Instant instant =
                now.toInstant();

        System.out.println(instant);
    }
}

十五、TemporalAdjusters:日期规则计算

真实业务中经常需要:

本月第一天
本月最后一天
下个月第一天
今年最后一天
下一个星期一

这时可以使用:

TemporalAdjusters

15.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
86 调整器 TemporalAdjuster firstDayOfMonth() 无 本月第一天
87 调整器 TemporalAdjuster lastDayOfMonth() 无 本月最后一天
88 调整器 TemporalAdjuster firstDayOfNextMonth() 无 下个月第一天
89 调整器 TemporalAdjuster firstDayOfYear() 无 今年第一天
90 调整器 TemporalAdjuster lastDayOfYear() 无 今年最后一天
91 调整器 TemporalAdjuster next() DayOfWeek dayOfWeek 下一个指定星期
92 调整器 TemporalAdjuster nextOrSame() DayOfWeek dayOfWeek 当天或下一个指定星期
93 调整器 TemporalAdjuster previous() DayOfWeek dayOfWeek 上一个指定星期

15.2 示例

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class AdjusterDemo {

    public static void main(String[] args) {

        LocalDate today =
                LocalDate.now();

        LocalDate firstDay =
                today.with(
                        TemporalAdjusters
                                .firstDayOfMonth()
                );

        LocalDate lastDay =
                today.with(
                        TemporalAdjusters
                                .lastDayOfMonth()
                );

        LocalDate nextMonday =
                today.with(
                        TemporalAdjusters.next(
                                DayOfWeek.MONDAY
                        )
                );

        System.out.println(firstDay);
        System.out.println(lastDay);
        System.out.println(nextMonday);
    }
}

十六、YearMonth:年月

YearMonth 很适合表示:

账单月份
工资月份
统计月份
某个月有多少天

例如:

2026-09

16.1 API 表

ID 返回值 返回值类型 方法名 参数列表 作用
94 指定年月 YearMonth of() int year, int month 创建指定年月
95 月份天数 int lengthOfMonth() 无 获取该月天数
96 月末日期 LocalDate atEndOfMonth() 无 获取该月最后一天

16.2 示例

import java.time.YearMonth;

public class YearMonthDemo {

    public static void main(String[] args) {

        YearMonth month =
                YearMonth.of(
                        2026,
                        2
                );

        System.out.println(month);

        System.out.println(
                month.lengthOfMonth()
        );

        System.out.println(
                month.atEndOfMonth()
        );
    }
}

结果:

2026-02
28
2026-02-28

十七、DayOfWeek 与 Month

DayOfWeek 和 Month 都属于枚举类型。

例如:

DayOfWeek.MONDAY
Month.JANUARY

示例:

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;

public class EnumDemo {

    public static void main(String[] args) {

        LocalDate date =
                LocalDate.of(
                        2026,
                        9,
                        17
                );

        DayOfWeek week =
                date.getDayOfWeek();

        Month month =
                date.getMonth();

        System.out.println(week);
        System.out.println(month);

        if (date.getDayOfWeek()
                == DayOfWeek.SATURDAY) {

            System.out.println("星期六");
        }
    }
}

十八、日期时间类型转换关系

整个 Java 日期时间体系最重要的一张图:

               LocalDate
             2026-09-17
                  │
                  │ atStartOfDay()
                  ↓
            LocalDateTime
      2026-09-17T10:30:00
                  │
                  │ atZone()
                  ↓
           ZonedDateTime
2026-09-17T10:30+08:00[Asia/Shanghai]
                  │
                  │ toInstant()
                  ↓
               Instant
          全球绝对时间点
                  │
                  │ toEpochMilli()
                  ↓
                long
              时间戳

字符串转换关系:

String
 ↑      ↓
format  parse
 ↑      ↓
LocalDate / LocalDateTime

理解这张图,比死记大量方法更重要。

十九、Java Web 中常见日期字段

例如用户实体:

public class User {

    private Long id;

    private String username;

    private LocalDate birthday;

    private LocalDateTime createTime;

    private LocalDateTime updateTime;
}

对应关系:

birthday
→ LocalDate

createTime
→ LocalDateTime

updateTime
→ LocalDateTime

如果系统涉及:

跨国业务
全球服务器
分布式系统
不同地区用户

则应进一步考虑:

Instant
OffsetDateTime
ZonedDateTime

二十、MySQL 与 Java 日期类型映射

常见对应关系:

Java 类型 MySQL 类型 常见业务
LocalDate DATE 生日、日期
LocalTime TIME 营业时间
LocalDateTime DATETIME 创建时间、更新时间
Instant TIMESTAMP/ UTC 时间存储方案 跨时区绝对时间

例如:

private LocalDateTime createTime;

数据库:

create_time DATETIME

在 Spring Boot + MyBatis 中通常可以直接完成类型映射。

二十一、旧日期 API:Date、Calendar、SimpleDateFormat

旧 Java 项目经常看到:

java.util.Date
java.util.Calendar
java.text.SimpleDateFormat

现代 Java 更推荐:

Date
↓
Instant

Calendar
↓
LocalDateTime / ZonedDateTime

SimpleDateFormat
↓
DateTimeFormatter

这些旧 API 目前主要达到:

能看懂、能维护、会转换即可。

二十二、Date 与现代 API 转换

22.1 Date → Instant

Date date =
        new Date();

Instant instant =
        date.toInstant();

22.2 Instant → Date

Instant instant =
        Instant.now();

Date date =
        Date.from(instant);

22.3 Date → LocalDateTime

Date date =
        new Date();

LocalDateTime dateTime =
        LocalDateTime.ofInstant(
                date.toInstant(),
                ZoneId.systemDefault()
        );

二十三、为什么不推荐继续主学 SimpleDateFormat

旧代码:

SimpleDateFormat formatter =
        new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss"
        );

一个重要问题:

SimpleDateFormat 不是线程安全的。

在 Web 应用中,如果多个线程共享同一个实例,可能发生线程安全问题。

现代 Java:

DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern(
                "yyyy-MM-dd HH:mm:ss"
        );

DateTimeFormatter:

不可变
线程安全

所以现代 Java 项目应优先使用:

java.time

二十四、看到业务应该立即想到什么类型

生日

LocalDate

每天 18:00

LocalTime

2026-09-17 18:30

LocalDateTime

全球唯一时间点

Instant

北京 / 东京 / 纽约时区

ZoneId

带时区日期时间

ZonedDateTime

REST API 中携带 UTC 偏移量

OffsetDateTime

日期格式转换

DateTimeFormatter

22 岁 3 个月

Period

运行了 2 小时 30 分钟

Duration

相差 16 天

ChronoUnit.DAYS

二十五、96 个核心 API 总结

本文共整理:

96 个核心日期时间 API / 常用调用

可以按优先级学习。

第一优先级:Java SE 必须熟练

LocalDate
LocalTime
LocalDateTime
DateTimeFormatter
Period
Duration
ChronoUnit

第二优先级:Java Web 必须理解并逐渐熟练

Instant
ZoneId
ZonedDateTime
OffsetDateTime

第三优先级:业务增强

TemporalAdjusters
YearMonth
DayOfWeek
Month

兼容旧代码

Date
Calendar
SimpleDateFormat

二十六、综合案例:订单支付超时系统

需求:

用户创建订单后,必须在 30 分钟内完成支付,否则订单自动过期。同时需要展示订单创建时间、过期时间以及剩余支付分钟数。

这个案例会综合使用:

LocalDateTime
plusMinutes()
isAfter()
Duration
DateTimeFormatter
format()

26.1 完整代码

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class OrderTimeDemo {

    public static void main(String[] args) {

        // =========================
        // 1. 创建订单
        // =========================

        LocalDateTime createTime =
                LocalDateTime.now();


        // =========================
        // 2. 30 分钟后过期
        // =========================

        LocalDateTime expireTime =
                createTime.plusMinutes(30);


        // =========================
        // 3. 获取当前时间
        // =========================

        LocalDateTime now =
                LocalDateTime.now();


        // =========================
        // 4. 判断订单是否过期
        // =========================

        boolean expired =
                now.isAfter(expireTime);


        // =========================
        // 5. 计算剩余时间
        // =========================

        long remainingMinutes = 0;

        if (!expired) {

            Duration duration =
                    Duration.between(
                            now,
                            expireTime
                    );

            remainingMinutes =
                    duration.toMinutes();
        }


        // =========================
        // 6. 创建格式器
        // =========================

        DateTimeFormatter formatter =
                DateTimeFormatter.ofPattern(
                        "yyyy-MM-dd HH:mm:ss"
                );


        // =========================
        // 7. 日期时间转字符串
        // =========================

        String createTimeText =
                createTime.format(formatter);

        String expireTimeText =
                expireTime.format(formatter);


        // =========================
        // 8. 输出订单信息
        // =========================

        System.out.println(
                "订单创建时间:" +
                createTimeText
        );

        System.out.println(
                "订单过期时间:" +
                expireTimeText
        );

        if (expired) {

            System.out.println(
                    "订单状态:已过期"
            );

        } else {

            System.out.println(
                    "订单状态:待支付"
            );

            System.out.println(
                    "剩余时间:" +
                    remainingMinutes +
                    "分钟"
            );
        }
    }
}

二十七、综合案例执行链

上面的程序,本质上就是:

LocalDateTime.now()
        ↓
获得订单创建时间

plusMinutes(30)
        ↓
计算过期时间

LocalDateTime.now()
        ↓
获得当前时间

isAfter()
        ↓
判断订单是否过期

Duration.between()
        ↓
计算剩余时间

DateTimeFormatter
        ↓
定义显示格式

format()
        ↓
转换成前端需要的字符串

这就是日期 API 在真实业务中的使用方式。

二十八、升级到真实 Spring Boot 业务

未来订单实体可能是:

public class Order {

    private Long id;

    private LocalDateTime createTime;

    private LocalDateTime expireTime;

    private LocalDateTime payTime;
}

创建订单:

LocalDateTime now =
        LocalDateTime.now();

order.setCreateTime(now);

order.setExpireTime(
        now.plusMinutes(30)
);

判断订单是否过期:

if (LocalDateTime.now()
        .isAfter(
                order.getExpireTime()
        )) {

    order.setStatus(
            "CANCELLED"
    );
}

支付:

order.setPayTime(
        LocalDateTime.now()
);

因此日期时间 API 的本质并不是:

“做日期计算题”。

而是:

描述业务世界中的时间状态、时间顺序和时间规则。

二十九、最终知识结构

最后把 Java 日期 API 压缩成这一张认知图:

日期
└── LocalDate

时间
└── LocalTime

日期 + 时间
└── LocalDateTime

格式转换
└── DateTimeFormatter

年月日差
└── Period

时分秒间隔
└── Duration

指定单位差值
└── ChronoUnit

绝对时间点
└── Instant

时区
└── ZoneId

日期时间 + 地区时区
└── ZonedDateTime

日期时间 + UTC偏移量
└── OffsetDateTime

复杂日期规则
└── TemporalAdjusters

年月
└── YearMonth

星期
└── DayOfWeek

月份
└── Month

真正掌握的标准不是能背出 96 个 API,而是面对业务需求时能够立即判断:

需要什么日期类型?
需要什么时间类型?
是否涉及时区?
是否需要格式化?
是否需要计算时间差?
是否需要比较先后?
是否需要转成时间戳?

当这些问题能够自然映射到对应 API 时,Java 日期时间体系就真正进入了你的长期记忆。

三十、自测题

学完本文后,可以不看答案完成下面几个任务:

  1. 获取今天日期。
  2. 获取当前日期时间。
  3. 创建 2026-12-01。
  4. 计算 100 天后的日期。
  5. 判断两个日期谁更早。
  6. 把 2026-09-17 18:30:00 转成 LocalDateTime。
  7. 把 LocalDateTime 格式化成字符串。
  8. 根据生日计算年龄。
  9. 计算两个时间之间相差多少分钟。
  10. 获取本月最后一天。
  11. 将北京时间转换成纽约时间。
  12. 将 Instant 转换成毫秒时间戳。
  13. 编写一个 30 分钟订单过期逻辑。

如果这些任务能够独立完成,Java 日期时间 API 的核心部分就已经掌握。