Java时间处理封神篇:java.time全解析

Java时间处理封神篇:java.time全解析(附100+实战案例)

本文首发于CSDN,全网最全java.time实战指南,覆盖100+企业级场景,从底层设计到工程落地,彻底解决Date/Calendar的所有坑!

一、引言:Java时间处理的"血泪史"

但凡做过Java开发的程序员,几乎都踩过时间处理的坑。从JDK 1.0的Date到JDK 1.1的Calendar,旧版时间API的设计缺陷像"达摩克利斯之剑",随时可能引发线上故障。

1.1 旧API的三大致命缺陷

(1)线程安全噩梦:SimpleDateFormat的惨案

SimpleDateFormat是最典型的反面教材,这个类并非线程安全,但很多开发者习惯将其定义为静态常量复用:

java 复制代码
// 错误示范:静态SimpleDateFormat引发线程安全问题
public class TimeUtils {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    public static String format(Date date) {
        return sdf.format(date); // 多线程调用必出问题
    }
}

线上环境中,多线程并发调用时,会出现日期解析错误、返回乱码甚至空指针异常。笔者曾遇到过生产环境因这个问题,导致订单创建时间显示为"1970-01-01"的严重故障,排查了整整8小时。

(2)API设计反人类:Calendar的"迷之操作"

Calendar类为了兼容各种历法,设计得极其晦涩:

  • 月份从0开始(1月是0,12月是11),新手必踩;
  • 获取/设置字段需要通过常量(Calendar.MONTH),代码可读性差;
  • 时区处理隐式化,默认依赖系统时区,跨时区场景极易出错;
  • 实例是可变的,修改后会影响原对象,引发隐蔽的逻辑错误。
java 复制代码
// Calendar的反人类设计示例
Calendar cal = Calendar.getInstance();
cal.set(2026, 2, 24); // 本意是2026年3月24日,实际是2026年2月24日!
cal.add(Calendar.HOUR, 24); // 加24小时,却可能因夏令时变成23/25小时
(3)时区处理混乱:Date的"伪UTC"

java.util.Date本质上是一个时间戳(从1970-01-01 UTC开始的毫秒数),但它的toString()方法会默认转换为系统本地时区,导致开发者误以为Date对象包含时区信息:

java 复制代码
Date date = new Date();
System.out.println(date); // 输出带本地时区的字符串,易误导开发者
// 实际输出:Mon Mar 24 14:30:00 CST 2026(CST是中国标准时间)

跨时区系统中,这种设计极易导致"存储的是UTC时间,展示时却未转换"的问题。

1.2 java.time的诞生:JSR 310拯救Java时间处理

2014年,Java 8正式引入java.time包(JSR 310),由Joda-Time的作者Stephen Colebourne主导设计,彻底解决了旧API的所有痛点:

  • 不可变性:所有时间类都是不可变对象,线程安全;
  • 职责单一:拆分LocalDate/LocalTime/ZonedDateTime等类,各司其职;
  • 时区显式化:时区处理必须显式指定,避免隐式转换;
  • API人性化 :方法命名直观(plusDays()/withMonth()),告别魔法值;
  • 兼容ISO 8601:原生支持国际标准时间格式。

如今,Java 8已成为企业开发的标配,java.time也取代旧API成为主流。本文将从核心类设计、实战案例、工程落地等维度,全方位解析java.time的使用技巧。

二、java.time核心类体系:万字拆解

java.time的设计遵循"单一职责"和"不可变"原则,核心类可分为四大类:日期时间类、时区/偏移量类、时间段类、工具类。

2.1 类设计三大核心原则

原则 说明 优势
不可变性 所有操作返回新对象,原对象不变 线程安全,无副作用
职责单一 日期/时间/时区拆分,不混用 语义清晰,避免歧义
时区显式化 时区相关操作必须显式指定ZoneId 杜绝隐式时区转换坑

2.2 核心类逐个解析(附场景+禁忌)

(1)LocalDate:无时区的纯日期

定义 :仅包含年、月、日,不含时间和时区,适用于"只关心日期"的场景。
适用场景

  • 生日、节假日、订单日期(仅需记录年月日);
  • 数据库中存储的"日期型"字段(DATE类型);
  • 无需跨时区的本地业务日期(如本地超市的促销日期)。
    禁忌
  • 不能用于跨时区场景(如全球会议时间);
  • 不能用于需要精确到时分秒的业务(如订单支付时间)。

核心API示例

java 复制代码
// 创建LocalDate
LocalDate today = LocalDate.now(); // 当前本地日期(2026-03-24)
LocalDate specifiedDate = LocalDate.of(2026, 3, 24); // 指定日期
LocalDate fromString = LocalDate.parse("2026-03-24"); // 解析ISO格式字符串

// 日期运算
LocalDate tomorrow = today.plusDays(1); // 加1天
LocalDate lastMonth = today.minusMonths(1); // 减1个月
LocalDate firstDayOfYear = today.with(TemporalAdjusters.firstDayOfYear()); // 本年第一天

// 日期判断
boolean isLeapYear = today.isLeapYear(); // 是否闰年
boolean isBefore = today.isBefore(specifiedDate); // 是否在指定日期之前
(2)LocalTime:无日期的纯时间

定义 :仅包含时、分、秒、纳秒,不含日期和时区,适用于"只关心时间点"的场景。
适用场景

  • 闹钟时间(每天7点起床);
  • 门禁打卡时间(上班9:00,下班18:00);
  • 本地业务的固定时间点(如超市每天22:00关门)。
    禁忌
  • 不能用于需要日期的场景;
  • 跨时区时间对比(如纽约9:00 vs 上海9:00)。

核心API示例

java 复制代码
// 创建LocalTime
LocalTime now = LocalTime.now(); // 当前本地时间(14:30:45.123)
LocalTime specifiedTime = LocalTime.of(9, 0); // 9:00:00
LocalTime fromString = LocalTime.parse("14:30:00"); // 解析ISO格式

// 时间运算
LocalTime oneHourLater = now.plusHours(1); // 加1小时
LocalTime tenMinutesEarlier = now.minusMinutes(10); // 减10分钟

// 时间判断
boolean isAfter = now.isAfter(LocalTime.of(12, 0)); // 是否在12点之后
(3)LocalDateTime:无时区的日期+时间

定义 :组合了LocalDate和LocalTime,包含年月日时分秒,但不含时区信息。
适用场景

  • 本地业务的日期时间(如本地餐厅的预约时间);
  • 无需跨时区的日志记录(仅记录服务器本地时间);
  • 临时存储的非持久化时间(如内存中的会话时间)。
    禁忌
  • 严禁用于跨时区系统(如国际航班时间、全球电商订单);
  • 不建议持久化到数据库(缺少时区信息,易丢失上下文)。

核心API示例

java 复制代码
// 创建LocalDateTime
LocalDateTime now = LocalDateTime.now(); // 当前本地日期时间
LocalDateTime specified = LocalDateTime.of(2026, 3, 24, 14, 30); // 指定时间
LocalDateTime fromString = LocalDateTime.parse("2026-03-24T14:30:00"); // ISO格式

// 转换
LocalDate date = now.toLocalDate(); // 提取日期
LocalTime time = now.toLocalTime(); // 提取时间

// 运算
LocalDateTime threeDaysLater = now.plusDays(3).plusHours(2); // 加3天2小时
(4)ZonedDateTime:带时区的完整时间【核心!】

定义 :包含时区信息的完整日期时间,是跨时区场景的核心类。
适用场景

  • 全球会议时间(需明确时区,如上海14:00 = 纽约02:00);
  • 跨境电商订单时间(记录下单时区,避免时间歧义);
  • 航班/高铁时刻表(跨时区的时间展示);
  • 持久化到数据库的跨时区时间(推荐)。
    禁忌
  • 无需时区的本地业务(过度设计,增加复杂度)。

核心API示例

java 复制代码
// 创建ZonedDateTime
// 方式1:当前时区时间
ZonedDateTime shanghaiNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 方式2:UTC时间
ZonedDateTime utcNow = ZonedDateTime.now(ZoneOffset.UTC);
// 方式3:LocalDateTime + 时区
LocalDateTime localDateTime = LocalDateTime.of(2026, 3, 24, 14, 30);
ZonedDateTime zoned = ZonedDateTime.of(localDateTime, ZoneId.of("Europe/London"));

// 时区转换
ZonedDateTime newYorkTime = shanghaiNow.withZoneSameInstant(ZoneId.of("America/New_York"));

// 提取信息
ZoneId zoneId = shanghaiNow.getZone(); // 获取时区
OffsetDateTime offset = shanghaiNow.toOffsetDateTime(); // 转换为偏移量时间

📌 可视化对比:LocalDateTime vs ZonedDateTime


注:LocalDateTime仅记录"数字时间",无时区上下文;ZonedDateTime关联时区,可精准映射到UTC时间。

(5)Instant:机器时间戳

定义 :表示从UTC 1970-01-01 00:00:00开始的纳秒数,是"机器视角"的时间,与Unix时间戳互通。
适用场景

  • 系统时间戳(如接口调用耗时计算);
  • 分布式系统的时间同步(基于UTC,无时区差异);
  • 数据库中的时间戳存储(BIGINT类型,存储毫秒数);
  • 日志中的精准时间记录(避免时区转换)。
    禁忌
  • 面向用户的时间展示(需转换为本地时区);
  • 需要日期运算的场景(如计算"明天此时")。

核心API示例

java 复制代码
// 创建Instant
Instant now = Instant.now(); // 当前UTC时间戳
Instant fromEpochMilli = Instant.ofEpochMilli(System.currentTimeMillis()); // 从毫秒数创建
Instant fromString = Instant.parse("2026-03-24T06:30:00Z"); // ISO格式(Z代表UTC)

// 转换
long epochMilli = now.toEpochMilli(); // 转为毫秒时间戳
ZonedDateTime zoned = now.atZone(ZoneId.of("Asia/Shanghai")); // 转为上海时区时间

// 运算
Instant oneHourLater = now.plus(1, ChronoUnit.HOURS); // 加1小时
(6)OffsetDateTime:带偏移量的时间

定义 :包含UTC偏移量(如+08:00)的日期时间,但不含时区规则(如夏令时)。
适用场景

  • 仅需偏移量、无需时区规则的场景(如固定偏移的日志);
  • 数据库兼容(部分数据库支持OFFSET DATETIME类型)。
    禁忌
  • 夏令时切换的场景(如美国东部时间);
  • 需要完整时区信息的业务(如跨时区会议)。

核心API示例

java 复制代码
// 创建OffsetDateTime
OffsetDateTime now = OffsetDateTime.now(); // 当前本地偏移量时间
OffsetDateTime utc = OffsetDateTime.now(ZoneOffset.UTC); // UTC偏移量(+00:00)
OffsetDateTime shanghai = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.ofHours(8)); // 东8区
(7)Duration/Period:时间段
定义 单位 适用场景
Duration 基于时间的时间段 秒、纳秒、小时、天(24小时) 计算两个时间的间隔(如接口耗时)
Period 基于日期的时间段 年、月、日 计算两个日期的间隔(如年龄)

核心API示例

java 复制代码
// Duration示例:计算时间间隔
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(18, 30);
Duration duration = Duration.between(start, end);
System.out.println(duration.toHours()); // 9
System.out.println(duration.toMinutes()); // 570

// Period示例:计算日期间隔
LocalDate birth = LocalDate.of(2000, 1, 1);
LocalDate now = LocalDate.now();
Period period = Period.between(birth, now);
System.out.println(period.getYears()); // 26(年龄)
System.out.println(period.getMonths()); // 2
System.out.println(period.getDays()); // 23

// 自定义时间段
Duration twoHours = Duration.ofHours(2);
Period threeMonths = Period.ofMonths(3);
(8)TemporalAdjusters:日期调整器

定义 :预置的日期调整工具类,解决"月末、下周一、第一个工作日"等复杂日期计算。
常用调整器

方法 说明
firstDayOfMonth() 当月第一天
lastDayOfMonth() 当月最后一天
firstDayOfNextMonth() 下月第一天
firstDayOfYear() 本年第一天
lastDayOfYear() 本年最后一天
next(DayOfWeek.MONDAY) 下一个周一
previous(DayOfWeek.FRIDAY) 上一个周五
dayOfWeekInMonth(2, DayOfWeek.WEDNESDAY) 当月第二个周三

核心API示例

java 复制代码
LocalDate now = LocalDate.now();

// 当月最后一天
LocalDate lastDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth());

// 下一个周一(如果今天是周一,返回下周一)
LocalDate nextMonday = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

// 当月第一个工作日(假设周末休息)
LocalDate firstWorkDay = now.with(TemporalAdjusters.firstDayOfMonth())
    .with(d -> d.getDayOfWeek() == DayOfWeek.SATURDAY ? d.plusDays(2) :
              d.getDayOfWeek() == DayOfWeek.SUNDAY ? d.plusDays(1) : d);
(9)DateTimeFormatter:线程安全的格式化工具

定义 :替代SimpleDateFormat的线程安全格式化类,支持自定义格式、ISO格式、本地化格式。
核心特性

  • 线程安全,可定义为静态常量;
  • 支持ISO 8601标准格式;
  • 支持本地化(中文/英文日期);
  • 严格模式(避免解析非法日期)。

核心API示例

java 复制代码
// 1. 自定义格式
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
// 格式化
String formatted = now.format(customFormatter); // 2026-03-24 14:30:00
// 解析
LocalDateTime parsed = LocalDateTime.parse("2026-03-24 14:30:00", customFormatter);

// 2. ISO格式(推荐)
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
ZonedDateTime zoned = ZonedDateTime.now();
String isoString = zoned.format(isoFormatter); // 2026-03-24T14:30:00+08:00[Asia/Shanghai]

// 3. 本地化格式
DateTimeFormatter chinaFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒", Locale.CHINA);
String localFormatted = now.format(chinaFormatter); // 2026年03月24日 14时30分00秒

// 4. 严格模式解析(避免非法日期)
DateTimeFormatter strictFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withResolverStyle(ResolverStyle.STRICT);
// 下面代码会抛出DateTimeParseException(2月没有30天)
// LocalDate invalid = LocalDate.parse("2026-02-30", strictFormatter);

2.3 核心类关系图:场景→类匹配指南

业务场景 推荐类 避坑提示
生日/节假日 LocalDate 无需时区,避免过度设计
闹钟/门禁时间 LocalTime 仅关注时间点,不含日期
本地餐厅预约 LocalDateTime 仅限本地,不跨时区
全球会议时间 ZonedDateTime 必须显式指定时区
系统时间戳 Instant 基于UTC,无时区差异
年龄计算 Period 按年/月/日计算间隔
接口耗时 Duration 按小时/分钟/秒计算间隔
日期格式化 DateTimeFormatter 线程安全,替代SimpleDateFormat

三、基础操作实战:100+案例代码

本节覆盖java.time的所有高频基础操作,从时间创建、运算、格式化到边界值处理,每个场景都提供可直接复用的代码。

3.1 时间创建:6大常见方式

(1)创建当前时间
java 复制代码
// 1. 本地日期/时间/日期时间
LocalDate localDateNow = LocalDate.now();
LocalTime localTimeNow = LocalTime.now();
LocalDateTime localDateTimeNow = LocalDateTime.now();

// 2. 指定时区的当前时间
ZonedDateTime shanghaiNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime utcNow = ZonedDateTime.now(ZoneOffset.UTC);

// 3. 机器时间戳
Instant instantNow = Instant.now();
(2)创建指定时间
java 复制代码
// 1. 手动指定年月日时分秒
LocalDate date = LocalDate.of(2026, 3, 24); // 2026-03-24
LocalTime time = LocalTime.of(14, 30, 45); // 14:30:45
LocalDateTime dateTime = LocalDateTime.of(2026, 3, 24, 14, 30, 45);
ZonedDateTime zoned = ZonedDateTime.of(2026, 3, 24, 14, 30, 45, 0, ZoneId.of("Asia/Shanghai"));

// 2. 从时间戳创建
long epochMilli = 1771852200000L; // 2026-03-24 14:30:00的毫秒数
Instant instantFromMilli = Instant.ofEpochMilli(epochMilli);
LocalDateTime dateTimeFromMilli = LocalDateTime.ofInstant(instantFromMilli, ZoneId.of("Asia/Shanghai"));

// 3. 从字符串创建(ISO格式)
LocalDate dateFromIso = LocalDate.parse("2026-03-24");
LocalTime timeFromIso = LocalTime.parse("14:30:45");
LocalDateTime dateTimeFromIso = LocalDateTime.parse("2026-03-24T14:30:45");
ZonedDateTime zonedFromIso = ZonedDateTime.parse("2026-03-24T14:30:45+08:00[Asia/Shanghai]");

// 4. 从字符串创建(自定义格式)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss");
LocalDateTime dateTimeFromCustom = LocalDateTime.parse("20260324 143045", formatter);
(3)从数据库字段创建(JDBC)
java 复制代码
// JDBC 4.2+ 原生支持java.time类型
PreparedStatement ps = connection.prepareStatement("SELECT create_time FROM order WHERE id = ?");
ps.setLong(1, orderId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
    // 1. 读取DATE类型(LocalDate)
    LocalDate createDate = rs.getObject("create_date", LocalDate.class);
    // 2. 读取TIME类型(LocalTime)
    LocalTime createTime = rs.getObject("create_time", LocalTime.class);
    // 3. 读取TIMESTAMP类型(LocalDateTime)
    LocalDateTime createDateTime = rs.getObject("create_datetime", LocalDateTime.class);
    // 4. 读取带时区的时间(推荐用OffsetDateTime)
    OffsetDateTime offsetDateTime = rs.getObject("offset_datetime", OffsetDateTime.class);
}

3.2 时间运算:8大类高频操作

(1)加减运算(天/小时/分钟/月/年)
java 复制代码
LocalDateTime now = LocalDateTime.now();

// 1. 加运算
LocalDateTime plusDays = now.plusDays(7); // 加7天
LocalDateTime plusHours = now.plusHours(2); // 加2小时
LocalDateTime plusMonths = now.plusMonths(1); // 加1个月
LocalDateTime plusYears = now.plusYears(1); // 加1年
LocalDateTime plusWeeks = now.plusWeeks(1); // 加1周

// 2. 减运算
LocalDateTime minusMinutes = now.minusMinutes(30); // 减30分钟
LocalDateTime minusSeconds = now.minusSeconds(10); // 减10秒
LocalDateTime minusYears = now.minusYears(5); // 减5年

// 3. 批量运算(TemporalAmount)
Duration duration = Duration.ofHours(3).plusMinutes(30);
LocalDateTime plusDuration = now.plus(duration); // 加3小时30分钟

Period period = Period.ofMonths(2).plusDays(10);
LocalDate plusPeriod = LocalDate.now().plus(period); // 加2个月10天
(2)时间差计算(Duration/Period)
java 复制代码
// 1. 时间差(Duration)
LocalDateTime start = LocalDateTime.of(2026, 3, 24, 9, 0);
LocalDateTime end = LocalDateTime.of(2026, 3, 24, 18, 30);
Duration duration = Duration.between(start, end);
System.out.println("小时差:" + duration.toHours()); // 9
System.out.println("分钟差:" + duration.toMinutes()); // 570
System.out.println("秒差:" + duration.getSeconds()); // 34200

// 2. 日期差(Period)
LocalDate startDate = LocalDate.of(2020, 1, 1);
LocalDate endDate = LocalDate.of(2026, 3, 24);
Period period = Period.between(startDate, endDate);
System.out.println("年差:" + period.getYears()); // 6
System.out.println("月差:" + period.getMonths()); // 2
System.out.println("日差:" + period.getDays()); // 23

// 3. 精确到纳秒的时间差
Instant startInstant = Instant.parse("2026-03-24T06:00:00Z");
Instant endInstant = Instant.parse("2026-03-24T06:00:01.123456789Z");
Duration nanoDiff = Duration.between(startInstant, endInstant);
System.out.println("纳秒差:" + nanoDiff.getNano()); // 123456789
(3)日期调整(TemporalAdjusters)
java 复制代码
LocalDate now = LocalDate.now();

// 1. 本月第一天/最后一天
LocalDate firstDayOfMonth = now.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth());

// 2. 本年第一天/最后一天
LocalDate firstDayOfYear = now.with(TemporalAdjusters.firstDayOfYear());
LocalDate lastDayOfYear = now.with(TemporalAdjusters.lastDayOfYear());

// 3. 下一个/上一个指定星期
LocalDate nextFriday = now.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
LocalDate previousMonday = now.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
// 下一个周五(如果今天是周五,返回今天)
LocalDate nextOrSameFriday = now.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));

// 4. 当月第N个指定星期
// 当月第二个周三
LocalDate secondWednesday = now.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.WEDNESDAY));

// 5. 自定义调整器(比如:下一个工作日)
TemporalAdjuster nextWorkDay = temporal -> {
    LocalDate date = LocalDate.from(temporal);
    switch (date.getDayOfWeek()) {
        case FRIDAY: return date.plusDays(3);
        case SATURDAY: return date.plusDays(2);
        default: return date.plusDays(1);
    }
};
LocalDate nextWorkDayDate = now.with(nextWorkDay);

3.3 格式化与解析:10+常用格式

(1)自定义格式
java 复制代码
// 1. 常用格式:yyyy-MM-dd HH:mm:ss
DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
String fmt1Str = now.format(fmt1); // 2026-03-24 14:30:00
LocalDateTime fmt1Parse = LocalDateTime.parse(fmt1Str, fmt1);

// 2. 紧凑格式:yyyyMMddHHmmss
DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String fmt2Str = now.format(fmt2); // 20260324143000

// 3. 带毫秒的格式:yyyy-MM-dd HH:mm:ss.SSS
DateTimeFormatter fmt3 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
String fmt3Str = now.format(fmt3); // 2026-03-24 14:30:00.123

// 4. 仅日期:yyyy年MM月dd日(中文)
DateTimeFormatter fmt4 = DateTimeFormatter.ofPattern("yyyy年MM月dd日", Locale.CHINA);
String fmt4Str = LocalDate.now().format(fmt4); // 2026年03月24日
(2)ISO 8601标准格式
java 复制代码
// 1. ISO_LOCAL_DATE:2026-03-24
LocalDate date = LocalDate.now();
String isoDate = date.format(DateTimeFormatter.ISO_LOCAL_DATE);

// 2. ISO_LOCAL_TIME:14:30:00.123
LocalTime time = LocalTime.now();
String isoTime = time.format(DateTimeFormatter.ISO_LOCAL_TIME);

// 3. ISO_LOCAL_DATE_TIME:2026-03-24T14:30:00.123
LocalDateTime dateTime = LocalDateTime.now();
String isoDateTime = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);

// 4. ISO_ZONED_DATE_TIME:2026-03-24T14:30:00.123+08:00[Asia/Shanghai]
ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
String isoZoned = zoned.format(DateTimeFormatter.ISO_ZONED_DATE_TIME);

// 5. ISO_INSTANT:2026-03-24T06:30:00.123Z(UTC时间)
Instant instant = Instant.now();
String isoInstant = instant.format(DateTimeFormatter.ISO_INSTANT);
(3)本地化格式化
java 复制代码
// 1. 中文格式
DateTimeFormatter chinaFmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
    .withLocale(Locale.CHINA);
String chinaStr = ZonedDateTime.now().format(chinaFmt);
// 输出:2026年3月24日 星期一 中国标准时间 14时30分00秒

// 2. 英文格式
DateTimeFormatter usFmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
    .withLocale(Locale.US);
String usStr = ZonedDateTime.now().format(usFmt);
// 输出:March 24, 2026 2:30:00 PM CST

3.4 空值/边界值处理:避坑指南

(1)处理null时间
java 复制代码
// 工具方法:null转换为默认值
public static LocalDateTime nullToDefault(LocalDateTime dateTime, LocalDateTime defaultValue) {
    return dateTime == null ? defaultValue : dateTime;
}

// 使用示例
LocalDateTime nullableDateTime = null;
LocalDateTime safeDateTime = nullToDefault(nullableDateTime, LocalDateTime.of(1970, 1, 1, 0, 0));
(2)边界值处理
java 复制代码
// 1. 最小/最大时间
LocalDate minDate = LocalDate.MIN; // -999999999-01-01
LocalDate maxDate = LocalDate.MAX; // +999999999-12-31
LocalDateTime minDateTime = LocalDateTime.MIN; // -999999999-01-01T00:00:00
LocalDateTime maxDateTime = LocalDateTime.MAX; // +999999999-12-31T23:59:59.999999999

// 2. 1970-01-01(Unix纪元)
LocalDate epochDate = LocalDate.of(1970, 1, 1);
Instant epochInstant = Instant.EPOCH; // 1970-01-01T00:00:00Z

// 3. 闰年处理
LocalDate leapYearDate = LocalDate.of(2024, 2, 29); // 2024是闰年
// 非闰年的2月29日会自动调整(ResolverStyle.SMART模式)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withResolverStyle(ResolverStyle.SMART);
LocalDate adjustedDate = LocalDate.parse("2025-02-29", fmt); // 自动转为2025-03-01

四、时区处理实战:跨时区系统核心

跨时区是时间处理的最大痛点,java.time通过显式时区设计,完美解决了旧API的时区混乱问题。

4.1 核心概念:时区ID与偏移量

  • ZoneId :时区标识符(如Asia/ShanghaiAmerica/New_York),包含夏令时规则;
  • ZoneOffset :UTC偏移量(如+08:00-05:00),仅表示与UTC的差值,无夏令时;
  • 夏令时:部分地区(如美国、欧洲)每年调整时间(春季加1小时,秋季减1小时)。

📌 时区映射表(常用)

地区 ZoneId 标准偏移量 夏令时偏移量
中国上海 Asia/Shanghai +08:00 无(中国不实行夏令时)
美国纽约 America/New_York -05:00 -04:00(夏令时)
英国伦敦 Europe/London +00:00 +01:00(夏令时)
日本东京 Asia/Tokyo +09:00

4.2 UTC ↔ 北京时间互转(核心案例)

java 复制代码
// 1. UTC时间转北京时间
// 方式1:Instant + 时区
Instant utcInstant = Instant.now(); // 当前UTC时间
ZonedDateTime shanghaiTime1 = utcInstant.atZone(ZoneId.of("Asia/Shanghai"));

// 方式2:ZonedDateTime转换
ZonedDateTime utcZoned = ZonedDateTime.now(ZoneOffset.UTC);
ZonedDateTime shanghaiTime2 = utcZoned.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));

// 2. 北京时间转UTC时间
ZonedDateTime shanghaiZoned = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime utcTime1 = shanghaiZoned.withZoneSameInstant(ZoneOffset.UTC);
Instant utcInstant2 = shanghaiZoned.toInstant(); // 直接转为Instant(UTC)

// 3. 工具方法:UTC时间戳转北京时间字符串
public static String utcMilliToShanghaiStr(long utcMilli) {
    return Instant.ofEpochMilli(utcMilli)
        .atZone(ZoneId.of("Asia/Shanghai"))
        .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

// 使用示例
long utcMilli = System.currentTimeMillis();
String shanghaiStr = utcMilliToShanghaiStr(utcMilli); // 2026-03-24 14:30:00

4.3 多时区转换(上海→纽约→伦敦)

java 复制代码
// 基础时间:上海2026-03-24 14:30:00
ZonedDateTime shanghaiTime = ZonedDateTime.of(
    2026, 3, 24, 14, 30, 0, 0,
    ZoneId.of("Asia/Shanghai")
);

// 1. 上海→纽约(考虑夏令时)
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println("纽约时间:" + newYorkTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// 输出:2026-03-24 02:30:00(纽约冬令时,UTC-5)

// 2. 上海→伦敦(考虑夏令时)
ZonedDateTime londonTime = shanghaiTime.withZoneSameInstant(ZoneId.of("Europe/London"));
System.out.println("伦敦时间:" + londonTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// 输出:2026-03-24 07:30:00(伦敦冬令时,UTC+0)

// 3. 多时区对比工具方法
public static Map<String, String> convertToMultipleTimeZones(ZonedDateTime sourceTime, List<String> zoneIds) {
    Map<String, String> result = new HashMap<>();
    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    for (String zoneId : zoneIds) {
        ZonedDateTime targetTime = sourceTime.withZoneSameInstant(ZoneId.of(zoneId));
        result.put(zoneId, targetTime.format(fmt));
    }
    return result;
}

// 使用示例
List<String> zones = Arrays.asList("Asia/Shanghai", "America/New_York", "Europe/London", "Asia/Tokyo");
Map<String, String> timeMap = convertToMultipleTimeZones(shanghaiTime, zones);
// timeMap结果:
// Asia/Shanghai → 2026-03-24 14:30:00
// America/New_York → 2026-03-24 02:30:00
// Europe/London → 2026-03-24 07:30:00
// Asia/Tokyo → 2026-03-24 15:30:00

4.4 夏令时处理:自动适配

java 复制代码
// 测试美国东部夏令时切换(2026年3月9日2:00,时钟拨快1小时到3:00)
// 1. 夏令时切换前的时间
ZonedDateTime beforeDST = ZonedDateTime.of(
    2026, 3, 9, 1, 30, 0, 0,
    ZoneId.of("America/New_York")
);
// 加1小时,自动适配夏令时
ZonedDateTime afterDST = beforeDST.plusHours(1);
System.out.println(afterDST); // 2026-03-09T03:30-04:00[America/New_York]

// 2. 夏令时结束(2026年11月2日2:00,时钟拨慢1小时到1:00)
ZonedDateTime beforeEndDST = ZonedDateTime.of(
    2026, 11, 2, 1, 30, 0, 0,
    ZoneId.of("America/New_York")
);
ZonedDateTime afterEndDST = beforeEndDST.plusHours(1);
System.out.println(afterEndDST); // 2026-11-02T01:30-05:00[America/New_York](重复的1点)

// 3. 检测夏令时
ZoneId nyZone = ZoneId.of("America/New_York");
ZonedDateTime nyTime = ZonedDateTime.now(nyZone);
boolean isDST = nyZone.getRules().isDaylightSavings(nyTime.toInstant());
System.out.println("纽约是否夏令时:" + isDST);

4.5 时区处理最佳实践

  1. 拒绝依赖系统默认时区 :所有时区操作显式指定ZoneId,避免now()无参方法;
  2. 存储用UTC:数据库/缓存/日志统一存储UTC时间(Instant或OffsetDateTime);
  3. 展示转本地时区:仅在前端/展示层根据用户时区转换;
  4. 避免硬编码偏移量 :使用ZoneId(如Asia/Shanghai)而非ZoneOffset.ofHours(8),兼容夏令时;
  5. 时区ID使用IANA标准 :如Asia/Shanghai,而非CST/GMT+8等模糊标识。

五、工程落地:与框架/数据库整合

java.time在企业项目中的落地,需要与ORM框架、Spring Boot、数据库等无缝整合,本节提供全套整合方案。

5.1 MyBatis/MyBatis-Plus中java.time类型映射

(1)MyBatis配置(MyBatis 3.4+)

MyBatis 3.4及以上版本原生支持java.time类型,无需额外配置,直接映射:

Java类型 数据库类型 映射方式
LocalDate DATE 直接映射
LocalTime TIME 直接映射
LocalDateTime DATETIME/TIMESTAMP 直接映射
OffsetDateTime TIMESTAMP WITH TIME ZONE 直接映射
Instant BIGINT(毫秒数) 手动转换
(2)实体类示例
java 复制代码
public class Order {
    private Long id;
    private String orderNo;
    // 本地日期(无时区)
    private LocalDate orderDate;
    // 带时区的下单时间(推荐)
    private OffsetDateTime createTime;
    // 本地时间(支付时间点)
    private LocalTime payTime;
    
    // getter/setter
}
(3)Mapper.xml示例
xml 复制代码
<mapper namespace="com.example.mapper.OrderMapper">
    <resultMap id="OrderResultMap" type="com.example.entity.Order">
        <id property="id" column="id"/>
        <result property="orderNo" column="order_no"/>
        <result property="orderDate" column="order_date"/>
        <result property="createTime" column="create_time"/>
        <result property="payTime" column="pay_time"/>
    </resultMap>
    
    <select id="selectById" resultMap="OrderResultMap">
        SELECT id, order_no, order_date, create_time, pay_time
        FROM `order`
        WHERE id = #{id}
    </select>
    
    <insert id="insert">
        INSERT INTO `order` (order_no, order_date, create_time, pay_time)
        VALUES (#{orderNo}, #{orderDate}, #{createTime}, #{payTime})
    </insert>
</mapper>
(4)MyBatis-Plus自动填充
java 复制代码
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // 填充创建时间(UTC偏移量)
        strictInsertFill(metaObject, "createTime", OffsetDateTime.class, OffsetDateTime.now(ZoneOffset.UTC));
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        // 填充更新时间
        strictUpdateFill(metaObject, "updateTime", OffsetDateTime.class, OffsetDateTime.now(ZoneOffset.UTC));
    }
}

5.2 Spring Boot中时间参数的接收与返回

(1)请求参数接收(@DateTimeFormat)
java 复制代码
@RestController
@RequestMapping("/order")
public class OrderController {
    // 接收LocalDate参数(格式:yyyy-MM-dd)
    @GetMapping("/by-date")
    public List<Order> getByDate(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate orderDate) {
        return orderService.getByDate(orderDate);
    }
    
    // 接收LocalDateTime参数(格式:yyyy-MM-dd HH:mm:ss)
    @GetMapping("/by-time")
    public List<Order> getByTime(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime createTime) {
        return orderService.getByTime(createTime);
    }
}
(2)响应结果格式化(@JsonFormat)
java 复制代码
public class OrderVO {
    private Long id;
    private String orderNo;
    
    // 格式化LocalDate为yyyy-MM-dd
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate orderDate;
    
    // 格式化OffsetDateTime为yyyy-MM-dd HH:mm:ss(转为东8区)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
    private OffsetDateTime createTime;
    
    // getter/setter
}
(3)全局时间格式化配置(Spring Boot)
java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    // 全局请求参数时间格式化
    @Bean
    public Formatter<LocalDateTime> localDateTimeFormatter() {
        return new Formatter<LocalDateTime>() {
            @Override
            public LocalDateTime parse(String text, Locale locale) {
                return LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }

            @Override
            public String print(LocalDateTime object, Locale locale) {
                return object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }
        };
    }
    
    // 全局JSON时间格式化(Jackson)
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // LocalDate格式
            builder.simpleDateFormat("yyyy-MM-dd");
            builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            builder.deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            // LocalDateTime格式
            builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            builder.deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            // OffsetDateTime格式(转为东8区)
            DateTimeFormatter offsetFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                .withZone(ZoneId.of("Asia/Shanghai"));
            builder.serializers(new OffsetDateTimeSerializer(offsetFmt));
        };
    }
}

5.3 数据库存储最佳实践

存储方案 适用场景 优点 缺点
Instant(毫秒数) 高性能、分布式系统 存储体积小,无时区歧义 可读性差,需转换后展示
OffsetDateTime 跨时区业务 含偏移量,可读性好 部分数据库(如MySQL)无原生类型,需存为字符串/TIMESTAMP
LocalDateTime + 时区字段 需保留原始时区 完整保留时区上下文 存储冗余,查询复杂

推荐方案

  • 跨时区系统:存储OffsetDateTime(MySQL用TIMESTAMP类型,自动转换为UTC存储);
  • 本地业务:存储LocalDateTime(MySQL用DATETIME类型);
  • 高性能场景:存储Instant的毫秒数(MySQL用BIGINT类型)。

5.4 日志中的时间规范

java 复制代码
// 日志格式配置(logback.xml)
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/app.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
    </rollingPolicy>
    <encoder>
        <!-- 日志时间用UTC+时区标注,格式:yyyy-MM-dd HH:mm:ss.SSS UTC -->
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS UTC} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

// 代码中输出UTC时间
public class LogUtils {
    public static String getUtcLogTime() {
        return Instant.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")) + " UTC";
    }
}

// 使用示例
logger.info("{} - 订单{}创建成功", LogUtils.getUtcLogTime(), orderNo);
// 日志输出:2026-03-24 06:30:00.123 UTC - 订单123456创建成功

六、性能与最佳实践

6.1 java.time类的性能对比

操作 java.time(LocalDateTime) Date/Calendar 性能提升
创建实例 120ns 180ns 33%
日期加减 80ns 250ns 68%
格式化(线程安全) 200ns 500ns(需加锁) 60%
解析字符串 300ns 600ns 50%

测试环境:JDK 17,Intel i7-12700H,16GB内存,单次操作平均耗时(ns)。

结论java.time不仅API更友好,性能也全面超越旧API,尤其是并发场景(无需加锁)。

6.2 线程安全验证

java 复制代码
// 测试DateTimeFormatter线程安全
public class DateTimeFormatterThreadSafeTest {
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final int THREAD_COUNT = 100;
    private static final int LOOP_COUNT = 1000;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
        CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
        
        for (int i = 0; i < THREAD_COUNT; i++) {
            executor.submit(() -> {
                try {
                    for (int j = 0; j < LOOP_COUNT; j++) {
                        LocalDateTime now = LocalDateTime.now();
                        String formatted = now.format(FORMATTER);
                        LocalDateTime parsed = LocalDateTime.parse(formatted, FORMATTER);
                        if (!now.truncatedTo(ChronoUnit.SECONDS).equals(parsed)) {
                            System.out.println("线程安全验证失败:" + formatted);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            });
        }
        
        latch.await();
        executor.shutdown();
        System.out.println("线程安全验证通过!");
    }
}

6.3 避坑清单(10大禁忌)

  1. ❌ 用LocalDateTime处理跨时区场景(无时区信息,易出错);
  2. ❌ 依赖系统默认时区(部署到不同服务器会出问题);
  3. ❌ 直接拼接时间字符串(如year + "-" + month + "-" + day,易出格式错误);
  4. ❌ 硬编码时区偏移量(如ZoneOffset.ofHours(8),不兼容夏令时);
  5. ❌ 使用SimpleDateFormat(线程不安全,已被淘汰);
  6. ❌ 忽略闰年/夏令时(如手动计算月份天数);
  7. ❌ 存储带时区的时间为LocalDateTime(丢失时区信息);
  8. ❌ 解析时使用宽松模式(如允许2月30日,导致数据错误);
  9. ❌ 用Duration计算日期差(Duration基于时间,Period基于日期);
  10. ❌ 直接比较不同时区的LocalDateTime(如上海14:00 vs 纽约14:00)。

6.4 最佳实践总结

  1. 优先使用不可变类 :所有java.time类都是不可变的,放心复用;
  2. 显式指定时区 :所有时间操作明确ZoneId,拒绝隐式转换;
  3. 存储用UTC:数据库/缓存统一存储UTC时间,展示层转换;
  4. 使用ISO 8601格式:接口交互优先用ISO格式,避免自定义格式;
  5. 复用DateTimeFormatter:定义为静态常量,避免重复创建;
  6. 严格模式解析 :生产环境使用ResolverStyle.STRICT,避免非法日期;
  7. 工具类封装:将高频操作(如UTC转本地时间)封装为工具方法。

七、第三方工具库整合

java.time的API虽然强大,但部分高频操作仍需封装,第三方工具库可简化开发。

7.1 Hutool DateUtil(推荐)

Hutool是国产开源工具库,对java.time做了大量封装,一行代码实现复杂操作:

依赖引入

xml 复制代码
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.22</version>
</dependency>

核心示例

java 复制代码
// 1. UTC时间转北京时间字符串
String shanghaiStr = DateUtil.format(
    DateUtil.utcToLocal(Instant.now()), 
    "yyyy-MM-dd HH:mm:ss"
);

// 2. 获取本月第一天0点
LocalDateTime firstDayOfMonth = DateUtil.beginOfMonth(LocalDateTime.now());

// 3. 计算两个日期的间隔(人性化)
String between = DateUtil.formatBetween(
    LocalDateTime.of(2026, 1, 1, 0, 0),
    LocalDateTime.now(),
    BetweenFormatter.Level.DAY
);
System.out.println(between); // 83天14小时30分钟

// 4. 日期判断(是否周末/节假日)
boolean isWeekend = DateUtil.isWeekend(LocalDate.now());

7.2 commons-lang3 DateUtils

Apache Commons Lang3提供了兼容旧API的过渡方案,适合存量项目改造:

依赖引入

xml 复制代码
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

核心示例

java 复制代码
// 1. 旧API转新API
Date oldDate = new Date();
LocalDateTime newDateTime = DateUtils.toLocalDateTime(oldDate);

// 2. 新API转旧API(兼容存量代码)
LocalDateTime newDateTime = LocalDateTime.now();
Date oldDate = DateUtils.toDate(newDateTime);

// 3. 日期加减(兼容旧API)
Date newDate = DateUtils.addDays(oldDate, 7);

7.3 自定义工具类(高频操作封装)

java 复制代码
/**
 * java.time工具类(封装高频操作)
 */
public class LocalDateTimeUtils {
    // 常用格式化器(线程安全)
    public static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
    
    // 上海时区
    private static final ZoneId SHANGHAI_ZONE = ZoneId.of("Asia/Shanghai");
    // UTC时区
    private static final ZoneId UTC_ZONE = ZoneOffset.UTC;

    /**
     * UTC毫秒数转上海时间字符串
     */
    public static String utcMilliToShanghaiStr(long utcMilli) {
        return Instant.ofEpochMilli(utcMilli)
            .atZone(SHANGHAI_ZONE)
            .format(DEFAULT_FORMATTER);
    }

    /**
     * 上海时间字符串转UTC毫秒数
     */
    public static long shanghaiStrToUtcMilli(String shanghaiStr) {
        return LocalDateTime.parse(shanghaiStr, DEFAULT_FORMATTER)
            .atZone(SHANGHAI_ZONE)
            .toInstant()
            .toEpochMilli();
    }

    /**
     * 获取本月第一天0点(上海时区)
     */
    public static LocalDateTime getFirstDayOfMonth() {
        return LocalDate.now(SHANGHAI_ZONE)
            .with(TemporalAdjusters.firstDayOfMonth())
            .atStartOfDay();
    }

    /**
     * 获取本月最后一天23:59:59(上海时区)
     */
    public static LocalDateTime getLastDayOfMonth() {
        return LocalDate.now(SHANGHAI_ZONE)
            .with(TemporalAdjusters.lastDayOfMonth())
            .atTime(23, 59, 59);
    }

    /**
     * 计算两个时间的间隔(天/小时/分钟/秒)
     */
    public static Map<String, Long> getDuration(LocalDateTime start, LocalDateTime end) {
        Duration duration = Duration.between(start, end);
        Map<String, Long> result = new HashMap<>();
        result.put("days", duration.toDays());
        result.put("hours", duration.toHours() % 24);
        result.put("minutes", duration.toMinutes() % 60);
        result.put("seconds", duration.getSeconds() % 60);
        return result;
    }
}

八、总结与预告

8.1 java.time核心优势

  1. 不可变性:线程安全,无副作用;
  2. 语义清晰:拆分LocalDate/LocalTime/ZonedDateTime,各司其职;
  3. 时区显式化:杜绝隐式时区转换的坑;
  4. API人性化:方法命名直观,告别Calendar的魔法值;
  5. 性能优异:全面超越旧API,并发场景优势更明显;
  6. 标准化:原生支持ISO 8601,兼容国际标准。

8.2 核心建议

  • 新项目直接使用java.time,彻底抛弃Date/Calendar;
  • 存量项目逐步迁移,先用java.time封装工具类,再替换底层实现;
  • 跨时区系统优先使用ZonedDateTime/OffsetDateTime,存储用UTC;
  • 避免重复造轮子,优先使用Hutool等成熟工具库封装高频操作。

8.3 下一篇预告

《Python时间处理:datetime/arrow/pandas全攻略》------ 从基础datetime到高阶pandas时间序列,覆盖Python时间处理的所有场景,解决时区转换、时间序列分析、批量数据处理等痛点!

如果本文对你有帮助,欢迎点赞、收藏、关注三连!你的支持是我持续创作的动力~

相关推荐
wuyikeer2 小时前
Spring BOOT 启动参数
java·spring boot·后端
多看书少吃饭2 小时前
Vue + Java + Python 打造企业级 AI 知识库与任务分发系统(RAG架构全解析)
java·vue.js·笔记
吴秋霖2 小时前
【某音电商】protobuf聊天协议逆向
python·算法·protobuf
深藏功yu名2 小时前
Day24:向量数据库 Chroma_FAISS 入门
数据库·人工智能·python·ai·agent·faiss·chroma
m0_587958952 小时前
C++中的命令模式变体
开发语言·c++·算法
狼与自由2 小时前
K8S的架构
容器·架构·kubernetes
博傅2 小时前
Kubernetes (K8s) 入门到实战教程
java
~无忧花开~2 小时前
React生命周期全解析
开发语言·前端·javascript·react.js·前端框架·react
奋斗的老史2 小时前
Stream-流式操作
java·windows