当前位置: 代码网 > it编程>编程语言>Java > Java日期时间API Date、LocalDate、LocalDateTime、SimpleDateFormat、DateTimeFormatter全攻略

Java日期时间API Date、LocalDate、LocalDateTime、SimpleDateFormat、DateTimeFormatter全攻略

2026年08月21日 Java 我要评论
从混乱到清晰,从线程不安全到不可变,彻底搞懂 java 时间处理的进化之路一、先上全家福(一张表看懂所有类)类所属包包含内容是否可变线程安全核心用途datejava.util日期 + 时间(毫秒精度)

从混乱到清晰,从线程不安全到不可变,彻底搞懂 java 时间处理的进化之路

一、先上全家福(一张表看懂所有类)

所属包包含内容是否可变线程安全核心用途
datejava.util日期 + 时间(毫秒精度)✅ 可变❌ 不安全老项目遗留,时间戳表示
localdatejava.time仅日期(年-月-日)❌ 不可变✅ 安全生日、订单日期、报表日期
localtimejava.time仅时间(时:分:秒.纳秒)❌ 不可变✅ 安全营业时间、定时任务时间
localdatetimejava.time日期 + 时间(无时区)❌ 不可变✅ 安全数据库 datetime 字段、本地时间展示
zoneddatetimejava.time日期 + 时间 + 时区❌ 不可变✅ 安全跨时区系统、全球化应用
instantjava.timeutc 时间戳❌ 不可变✅ 安全机器时间、日志时间戳
simpledateformatjava.text格式化/解析 date可变(内部 calendar)❌ 线程不安全老项目格式化
datetimeformatterjava.time.format格式化/解析 java.time 类不可变✅ 安全新项目格式化(推荐)

黄金选型原则

  • 新项目:一律使用 java.time 包(localdatelocaldatetimedatetimeformatter
  • 遗留项目:能迁就迁,不能迁也要正确使用 simpledateformatthreadlocal

二、类关系图解(一目了然)

┌─────────────────────────────────────────────────────────────────────┐
│                         java 日期时间类体系                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  📦 java.util(老 api,遗留)                                       │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  date          │ 日期+时间(毫秒),可变,设计糟糕          │   │
│  │  calendar      │ 日期运算,混乱,几乎废弃                   │   │
│  │  simpledateformat │ 格式化/解析 date,线程不安全!!         │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  📦 java.time(新 api,java 8+,推荐)                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  日期类                │  时间类              │  带时区      │   │
│  │  localdate (年月日)    │  localtime (时分秒) │  zoneddatetime│   │
│  │  └──────┬──────────────┴──────┬────────────┘               │   │
│  │         │         组合        │                              │   │
│  │         └───── localdatetime ─┘                             │   │
│  │                      │                                      │   │
│  │          ┌──────────┼──────────┐                           │   │
│  │          ↓          ↓          ↓                           │   │
│  │    格式化/解析        时间戳      日期运算                    │   │
│  │  datetimeformatter  instant   period/duration               │   │
│  │  (线程安全!)       (utc时间)   (间隔计算)                   │   │
│  └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘

三、date:老旧的“万能军刀”(该退休了)

1. 创建与基础使用

import java.util.date;

// 1. 获取当前时间
date now = new date();
system.out.println(now);  // tue aug 18 14:30:25 cst 2026

// 2. 通过时间戳创建(毫秒)
date datefrommillis = new date(1723967425000l);

// 3. 获取/设置时间戳(可变!)
long timestamp = now.gettime();        // 毫秒级时间戳
now.settime(1723967425000l);          // ❌ 可变,破坏了不可变性

2. 令人抓狂的设计缺陷

// ❌ 年份从 1900 开始计算,月份从 0 开始
date d = new date(2026 - 1900, 8 - 1, 18);  // 8月要写成 7!
system.out.println(d.getyear());   // 126(实际年份要 +1900)
system.out.println(d.getmonth());  // 7(实际月份要 +1)

// ❌ 方法大量被废弃
d.getyear();   // @deprecated
d.getmonth();  // @deprecated
d.getday();    // @deprecated

结论:date 已被官方标记为“legacy”,新项目绝对不要用

四、localdate:仅日期(最常用的日期类)

1. 创建与基础使用

import java.time.localdate;

// ✅ 获取当前日期
localdate today = localdate.now();  // 2026-08-18

// ✅ 创建指定日期(符合直觉:年-月-日)
localdate date = localdate.of(2026, 8, 18);
localdate date2 = localdate.parse("2026-08-18");  // iso 标准格式

// ✅ 获取年月日
int year = date.getyear();        // 2026
int month = date.getmonthvalue(); // 8(注意是 getmonthvalue,不是 getmonth)
int day = date.getdayofmonth();   // 18
int dayofweek = date.getdayofweek().getvalue(); // 3(周三=3,周一=1)

2. 日期运算(不可变,返回新对象)

localdate today = localdate.now();

localdate tomorrow = today.plusdays(1);           // 加 1 天
localdate nextmonth = today.plusmonths(1);        // 加 1 月
localdate nextyear = today.plusyears(1);          // 加 1 年
localdate lastweek = today.minusweeks(1);         // 减 1 周
localdate plusday = today.plusdays(100);           // 加 100 天

// 判断
boolean isafter = today.isafter(localdate.of(2026, 1, 1));  // true
boolean isleap = today.isleapyear();  // 判断闰年

// 链式调用
localdate future = today
    .plusyears(2)
    .minusmonths(3)
    .plusdays(10);  // 2028-05-28

3. 常见业务场景

// 统计当月天数
int daysinmonth = yearmonth.now().lengthofmonth();  // 31

// 判断是否为闰年
boolean isleap = localdate.now().isleapyear();  // false(2026 不是闰年)

// 获取某个月的第一天/最后一天
localdate firstday = localdate.of(2026, 8, 1);
localdate lastday = firstday.with(temporaladjusters.lastdayofmonth());  // 2026-08-31

五、localdatetime:日期 + 时间(最常用的组合类)

1. 创建与基础使用

import java.time.localdatetime;
import java.time.localdate;
import java.time.localtime;

// ✅ 获取当前日期时间
localdatetime now = localdatetime.now();  // 2026-08-18t14:30:25.123456789

// ✅ 从年月日时分秒创建
localdatetime dt = localdatetime.of(2026, 8, 18, 14, 30, 25);

// ✅ 由 localdate + localtime 组合
localdate date = localdate.of(2026, 8, 18);
localtime time = localtime.of(14, 30, 25);
localdatetime dt2 = localdatetime.of(date, time);

// ✅ 从字符串解析(iso 标准:t 分隔)
localdatetime parsed = localdatetime.parse("2026-08-18t14:30:25");

// ✅ 获取各字段
int year = now.getyear();
int month = now.getmonthvalue();
int day = now.getdayofmonth();
int hour = now.gethour();
int minute = now.getminute();
int second = now.getsecond();
int nano = now.getnano();

2. 日期时间运算

localdatetime now = localdatetime.now();

localdatetime later = now
    .plusyears(1)
    .plusmonths(2)
    .plusdays(3)
    .plushours(4)
    .plusminutes(5)
    .plusseconds(6);  // 链式调用,灵活直观

// 比较
boolean before = now.isbefore(localdatetime.of(2027, 1, 1, 0, 0));  // true
boolean after = now.isafter(localdatetime.now().minusdays(1));      // true

六、localtime:仅时间

import java.time.localtime;

// ✅ 创建
localtime now = localtime.now();              // 14:30:25.123456789
localtime time = localtime.of(14, 30, 25);    // 14:30:25
localtime parsed = localtime.parse("14:30:25");

// ✅ 运算
localtime later = time.plushours(1).plusminutes(30).plusseconds(10);

// ✅ 获取字段
int hour = time.gethour();    // 14
int minute = time.getminute(); // 30
int second = time.getsecond(); // 25
int nano = time.getnano();

七、格式化:simpledateformatvsdatetimeformatter

1.simpledateformat(老 api,线程不安全)

import java.text.simpledateformat;
import java.util.date;

// ❌ 危险:不能作为 static 共享
simpledateformat sdf = new simpledateformat("yyyy-mm-dd hh:mm:ss");

// 格式化:date → string
string str = sdf.format(new date());  // "2026-08-18 14:30:25"

// 解析:string → date
date date = sdf.parse("2026-08-18 14:30:25");  // 需处理 parseexception

 线程不安全原理

simpledateformat 内部维护了 calendar 对象。多线程同时 format/parse 时会并发修改这个共享 calendar,导致:

  • 日期错乱(2026-08-18 变成 2025-07-17)
  • numberformatexception
  • 线上偶发故障,排查极难

正确用法

// 方案1:每次新建(低频场景)
public string format(date date) {
    return new simpledateformat("yyyy-mm-dd").format(date);
}

// 方案2:threadlocal(推荐,性能最佳)
public class dateutils {
    private static final threadlocal<simpledateformat> tl = 
        threadlocal.withinitial(() -> new simpledateformat("yyyy-mm-dd"));
    
    public static string format(date date) {
        return tl.get().format(date);
    }
}

// 方案3:同步锁(不推荐,性能差)
synchronized (sdf) {
    sdf.format(date);
}

2.datetimeformatter(新 api,线程安全,推荐)

import java.time.localdatetime;
import java.time.format.datetimeformatter;

// ✅ 不可变,可定义为全局静态常量!
public static final datetimeformatter formatter = 
    datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");

// 格式化:localdatetime → string
localdatetime now = localdatetime.now();
string str = now.format(formatter);  // "2026-08-18 14:30:25"

// 解析:string → localdatetime
localdatetime dt = localdatetime.parse("2026-08-18 14:30:25", formatter);

// ✅ 使用预定义格式
string iso1 = localdatetime.now().format(datetimeformatter.iso_local_date_time);
string iso2 = localdate.now().format(datetimeformatter.iso_local_date);

// ✅ 自定义格式(中文)
datetimeformatter chineseformatter = 
    datetimeformatter.ofpattern("yyyy年mm月dd日 hh时mm分ss秒");
string str = localdatetime.now().format(chineseformatter);  // 2026年08月18日 14时30分25秒

3. 格式化模式对照表(强烈收藏)

字母含义示例
yyyy年份2026
yy年份(后两位)26
mm月份08
m月份(不带前导零)8
dd18
d日(不带前导零)18
hh小时(24 小时制)14
hh小时(12 小时制)02
mm分钟30
ss25
sss毫秒123
ssssss纳秒123456
a上午/下午(am/pm)pm
e星期(简写)wed
eee星期(简写)周三
eeee星期(全称)星期三
z时区cst
z时区(偏移量)+0800
g公元公元(ad)
// 常用格式示例
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss")      // 2026-08-18 14:30:25
datetimeformatter.ofpattern("yyyy年mm月dd日")          // 2026年08月18日
datetimeformatter.ofpattern("yyyy/mm/dd hh:mm")        // 2026/08/18 14:30
datetimeformatter.ofpattern("yyyy-mm-dd't'hh:mm:ss")   // 2026-08-18t14:30:25
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss.sss") // 2026-08-18 14:30:25.123
datetimeformatter.ofpattern("eee, mmm dd, yyyy")       // wed, aug 18, 2026

八、新旧 api 互转(迁移必看)

import java.time.*;
import java.util.date;

public class dateconverter {
    
    // ===== date ↔ localdate =====
    public static localdate datetolocaldate(date date) {
        return date.toinstant()
            .atzone(zoneid.systemdefault())
            .tolocaldate();
    }
    
    public static date localdatetodate(localdate localdate) {
        return date.from(localdate.atstartofday(zoneid.systemdefault()).toinstant());
    }
    
    // ===== date ↔ localdatetime =====
    public static localdatetime datetolocaldatetime(date date) {
        return date.toinstant()
            .atzone(zoneid.systemdefault())
            .tolocaldatetime();
    }
    
    public static date localdatetimetodate(localdatetime ldt) {
        return date.from(ldt.atzone(zoneid.systemdefault()).toinstant());
    }
    
    // ===== date ↔ instant =====
    public static instant datetoinstant(date date) {
        return date.toinstant();
    }
    
    public static date instanttodate(instant instant) {
        return date.from(instant);
    }
    
    // ===== localdatetime ↔ localdate =====
    public static localdate localdatetimetolocaldate(localdatetime ldt) {
        return ldt.tolocaldate();
    }
    
    public static localdatetime localdatetolocaldatetime(localdate ld) {
        return ld.atstartofday();  // 00:00:00
    }
}

九、实战避坑总结(高频错题)

常见错误后果正确做法
simpledateformat 定义为 static 共享多线程日期错乱、numberformatexception使用 threadlocal 或换 datetimeformatter
date 的月份从 0 开始日期显示为 8 月却输出 7 月新项目用 localdate/localdatetime
dategetmonth() 判断逻辑错误localdate.getmonthvalue()
new date(2026, 7, 18)年变为 3926 年,月份偏移localdate.of(2026, 8, 18)
localdatetime 跨时区时间展示不一致跨时区用 zoneddatetime
字符串解析不处理 parseexception程序异常崩溃try-catch 或使用 datetimeformatter(不抛受检异常)
datetimeformatter.ofpattern("yyyy-mm-dd") 传入 localdatetime编译通过但运行正常虽然可以,但建议用对应类型:localdate 格式用 localdate

十、思考题(检验是否真的懂了)

// 问题 1:下面这段代码在高并发下会出现什么问题?
public class dateutils {
    public static final simpledateformat sdf = 
        new simpledateformat("yyyy-mm-dd hh:mm:ss");
    
    public static string format(date date) {
        return sdf.format(date);
    }
}

// 问题 2:下面代码分别输出什么?
localdate date = localdate.of(2026, 8, 18);
system.out.println(date.getmonthvalue());    // a
system.out.println(date.getmonth());         // b

date d = new date(126, 7, 18);
system.out.println(d.getmonth() + 1);        // c

// 问题 3:下面两种方式,哪一种性能更好、更安全?
// 方式 a
datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd");
string str = localdate.now().format(formatter);

// 方式 b
string str = localdate.now().format(datetimeformatter.ofpattern("yyyy-mm-dd"));

答案(选中下方空白区域查看):

  1. 多线程并发调用时会出现日期解析错误、numberformatexception 等随机异常,因为 simpledateformat 线程不安全。
  2. a 输出 8getmonthvalue() 返回 1-12);b 输出 augustgetmonth() 返回枚举 month);c 输出 8date 月份从 0 开始,7+1=8)。
  3. 方式 a 更好:复用同一个 formatter 对象(线程安全),方式 b 每次调用都创建新对象,有性能开销。

总结(终极速查表)

场景推荐类理由
只需要日期(年月日)localdate轻量、不可变、线程安全
只需要时间(时分秒)localtime轻量、不可变、线程安全
日期 + 时间(本地)localdatetime最常用,对应数据库 datetime
日期 + 时间 + 时区zoneddatetime全球化应用
时间戳(机器时间)instantutc 时间,适合日志、排序
格式化(新项目)datetimeformatter不可变、线程安全、功能强大
格式化(遗留项目)simpledateformat必须用 threadlocal 或每次新建
日期时间间隔period(年月日)/ duration(时分秒)计算差值

到此这篇关于java日期时间api date、localdate、localdatetime、simpledateformat、datetimeformatter全攻略的文章就介绍到这了,更多相关java日期时间api内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com