最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java利用java.time處理時間的方法全解析

 更新時間:2026年03月25日 09:03:46   作者:無心水  
但凡做過Java開發(fā)的程序員,幾乎都踩過時間處理的坑,這篇文章主要為大家詳細介紹了Java利用java.time處理時間的相關(guān)用法,希望對大家有所幫助

一、引言:Java時間處理的“血淚史”

但凡做過Java開發(fā)的程序員,幾乎都踩過時間處理的坑。從JDK 1.0的Date到JDK 1.1的Calendar,舊版時間API的設(shè)計缺陷像“達摩克利斯之劍”,隨時可能引發(fā)線上故障。

1.1 舊API的三大致命缺陷

(1)線程安全噩夢:SimpleDateFormat

SimpleDateFormat是最典型的反面教材,這個類并非線程安全,但很多開發(fā)者習(xí)慣將其定義為靜態(tài)常量復(fù)用:

// 錯誤示范:靜態(tài)SimpleDateFormat引發(fā)線程安全問題
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); // 多線程調(diào)用必出問題
    }
}

線上環(huán)境中,多線程并發(fā)調(diào)用時,會出現(xiàn)日期解析錯誤、返回亂碼甚至空指針異常。筆者曾遇到過生產(chǎn)環(huán)境因這個問題,導(dǎo)致訂單創(chuàng)建時間顯示為“1970-01-01”的嚴重故障,排查了整整8小時。

(2)API設(shè)計:Calendar的“迷之操作”

Calendar類為了兼容各種歷法,設(shè)計得極其晦澀:

  • 月份從0開始(1月是0,12月是11),新手必踩;
  • 獲取/設(shè)置字段需要通過常量(Calendar.MONTH),代碼可讀性差;
  • 時區(qū)處理隱式化,默認依賴系統(tǒng)時區(qū),跨時區(qū)場景極易出錯;
  • 實例是可變的,修改后會影響原對象,引發(fā)隱蔽的邏輯錯誤。
// Calendar的設(shè)計示例
Calendar cal = Calendar.getInstance();
cal.set(2026, 2, 24); // 本意是2026年3月24日,實際是2026年2月24日!
cal.add(Calendar.HOUR, 24); // 加24小時,卻可能因夏令時變成23/25小時

(3)時區(qū)處理混亂:Date的“偽UTC”

java.util.Date本質(zhì)上是一個時間戳(從1970-01-01 UTC開始的毫秒數(shù)),但它的toString()方法會默認轉(zhuǎn)換為系統(tǒng)本地時區(qū),導(dǎo)致開發(fā)者誤以為Date對象包含時區(qū)信息:

Date date = new Date();
System.out.println(date); // 輸出帶本地時區(qū)的字符串,易誤導(dǎo)開發(fā)者
// 實際輸出:Mon Mar 24 14:30:00 CST 2026(CST是中國標準時間)

跨時區(qū)系統(tǒng)中,這種設(shè)計極易導(dǎo)致“存儲的是UTC時間,展示時卻未轉(zhuǎn)換”的問題。

1.2 java.time的誕生:JSR 310拯救Java時間處理

2014年,Java 8正式引入java.time包(JSR 310),由Joda-Time的作者Stephen Colebourne主導(dǎo)設(shè)計,徹底解決了舊API的所有痛點:

  • 不可變性:所有時間類都是不可變對象,線程安全;
  • 職責(zé)單一:拆分LocalDate/LocalTime/ZonedDateTime等類,各司其職;
  • 時區(qū)顯式化:時區(qū)處理必須顯式指定,避免隱式轉(zhuǎn)換;
  • API人性化:方法命名直觀(plusDays()/withMonth()),告別魔法值;
  • 兼容ISO 8601:原生支持國際標準時間格式。

如今,Java 8已成為企業(yè)開發(fā)的標配,java.time也取代舊API成為主流。本文將從核心類設(shè)計、實戰(zhàn)案例、工程落地等維度,全方位解析java.time的使用技巧。

二、java.time核心類體系:萬字拆解

java.time的設(shè)計遵循“單一職責(zé)”和“不可變”原則,核心類可分為四大類:日期時間類、時區(qū)/偏移量類、時間段類、工具類。

2.1 類設(shè)計三大核心原則

原則說明優(yōu)勢
不可變性所有操作返回新對象,原對象不變線程安全,無副作用
職責(zé)單一日期/時間/時區(qū)拆分,不混用語義清晰,避免歧義
時區(qū)顯式化時區(qū)相關(guān)操作必須顯式指定ZoneId杜絕隱式時區(qū)轉(zhuǎn)換坑

2.2 核心類逐個解析(附場景+禁忌)

(1)LocalDate:無時區(qū)的純?nèi)掌?/p>

定義:僅包含年、月、日,不含時間和時區(qū),適用于“只關(guān)心日期”的場景。

適用場景

  • 生日、節(jié)假日、訂單日期(僅需記錄年月日);
  • 數(shù)據(jù)庫中存儲的“日期型”字段(DATE類型);
  • 無需跨時區(qū)的本地業(yè)務(wù)日期(如本地超市的促銷日期)。

禁忌

  • 不能用于跨時區(qū)場景(如全球會議時間);
  • 不能用于需要精確到時分秒的業(yè)務(wù)(如訂單支付時間)。

核心API示例

// 創(chuàng)建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:無日期的純時間

定義:僅包含時、分、秒、納秒,不含日期和時區(qū),適用于“只關(guān)心時間點”的場景。

適用場景

  • 鬧鐘時間(每天7點起床);
  • 門禁打卡時間(上班9:00,下班18:00);
  • 本地業(yè)務(wù)的固定時間點(如超市每天22:00關(guān)門)。

禁忌

  • 不能用于需要日期的場景;
  • 跨時區(qū)時間對比(如紐約9:00 vs 上海9:00)。

核心API示例

// 創(chuàng)建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:無時區(qū)的日期+時間

定義:組合了LocalDate和LocalTime,包含年月日時分秒,但不含時區(qū)信息。

適用場景

  • 本地業(yè)務(wù)的日期時間(如本地餐廳的預(yù)約時間);
  • 無需跨時區(qū)的日志記錄(僅記錄服務(wù)器本地時間);
  • 臨時存儲的非持久化時間(如內(nèi)存中的會話時間)。

禁忌

  • 嚴禁用于跨時區(qū)系統(tǒng)(如國際航班時間、全球電商訂單);
  • 不建議持久化到數(shù)據(jù)庫(缺少時區(qū)信息,易丟失上下文)。

核心API示例

// 創(chuàng)建LocalDateTime
LocalDateTime now = LocalDateTime.now(); // 當前本地日期時間
LocalDateTime specified = LocalDateTime.of(2026, 3, 24, 14, 30); // 指定時間
LocalDateTime fromString = LocalDateTime.parse("2026-03-24T14:30:00"); // ISO格式

// 轉(zhuǎn)換
LocalDate date = now.toLocalDate(); // 提取日期
LocalTime time = now.toLocalTime(); // 提取時間

// 運算
LocalDateTime threeDaysLater = now.plusDays(3).plusHours(2); // 加3天2小時

(4)ZonedDateTime:帶時區(qū)的完整時間【核心!】

定義:包含時區(qū)信息的完整日期時間,是跨時區(qū)場景的核心類。

適用場景

  • 全球會議時間(需明確時區(qū),如上海14:00 = 紐約02:00);
  • 跨境電商訂單時間(記錄下單時區(qū),避免時間歧義);
  • 航班/高鐵時刻表(跨時區(qū)的時間展示);
  • 持久化到數(shù)據(jù)庫的跨時區(qū)時間(推薦)。

禁忌:無需時區(qū)的本地業(yè)務(wù)(過度設(shè)計,增加復(fù)雜度)。

核心API示例

// 創(chuàng)建ZonedDateTime
// 方式1:當前時區(qū)時間
ZonedDateTime shanghaiNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 方式2:UTC時間
ZonedDateTime utcNow = ZonedDateTime.now(ZoneOffset.UTC);
// 方式3:LocalDateTime + 時區(qū)
LocalDateTime localDateTime = LocalDateTime.of(2026, 3, 24, 14, 30);
ZonedDateTime zoned = ZonedDateTime.of(localDateTime, ZoneId.of("Europe/London"));

// 時區(qū)轉(zhuǎn)換
ZonedDateTime newYorkTime = shanghaiNow.withZoneSameInstant(ZoneId.of("America/New_York"));

// 提取信息
ZoneId zoneId = shanghaiNow.getZone(); // 獲取時區(qū)
OffsetDateTime offset = shanghaiNow.toOffsetDateTime(); // 轉(zhuǎn)換為偏移量時間

可視化對比:LocalDateTime vs ZonedDateTime

注:LocalDateTime僅記錄“數(shù)字時間”,無時區(qū)上下文;ZonedDateTime關(guān)聯(lián)時區(qū),可精準映射到UTC時間。

(5)Instant:機器時間戳

定義:表示從UTC 1970-01-01 00:00:00開始的納秒數(shù),是“機器視角”的時間,與Unix時間戳互通。

適用場景

  • 系統(tǒng)時間戳(如接口調(diào)用耗時計算);
  • 分布式系統(tǒng)的時間同步(基于UTC,無時區(qū)差異);
  • 數(shù)據(jù)庫中的時間戳存儲(BIGINT類型,存儲毫秒數(shù));
  • 日志中的精準時間記錄(避免時區(qū)轉(zhuǎn)換)。

禁忌

  • 面向用戶的時間展示(需轉(zhuǎn)換為本地時區(qū));
  • 需要日期運算的場景(如計算“明天此時”)。

核心API示例

// 創(chuàng)建Instant
Instant now = Instant.now(); // 當前UTC時間戳
Instant fromEpochMilli = Instant.ofEpochMilli(System.currentTimeMillis()); // 從毫秒數(shù)創(chuàng)建
Instant fromString = Instant.parse("2026-03-24T06:30:00Z"); // ISO格式(Z代表UTC)

// 轉(zhuǎn)換
long epochMilli = now.toEpochMilli(); // 轉(zhuǎn)為毫秒時間戳
ZonedDateTime zoned = now.atZone(ZoneId.of("Asia/Shanghai")); // 轉(zhuǎn)為上海時區(qū)時間

// 運算
Instant oneHourLater = now.plus(1, ChronoUnit.HOURS); // 加1小時

(6)OffsetDateTime:帶偏移量的時間

定義:包含UTC偏移量(如+08:00)的日期時間,但不含時區(qū)規(guī)則(如夏令時)。

適用場景

  • 僅需偏移量、無需時區(qū)規(guī)則的場景(如固定偏移的日志);
  • 數(shù)據(jù)庫兼容(部分數(shù)據(jù)庫支持OFFSET DATETIME類型)。

禁忌

  • 夏令時切換的場景(如美國東部時間);
  • 需要完整時區(qū)信息的業(yè)務(wù)(如跨時區(qū)會議)。

核心API示例

// 創(chuàng)建OffsetDateTime
OffsetDateTime now = OffsetDateTime.now(); // 當前本地偏移量時間
OffsetDateTime utc = OffsetDateTime.now(ZoneOffset.UTC); // UTC偏移量(+00:00)
OffsetDateTime shanghai = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.ofHours(8)); // 東8區(qū)

(7)Duration/Period:時間段

定義單位適用場景
Duration基于時間的時間段秒、納秒、小時、天(24小時)計算兩個時間的間隔(如接口耗時)
Period基于日期的時間段年、月、日計算兩個日期的間隔(如年齡)

核心API示例

// 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:日期調(diào)整器

定義:預(yù)置的日期調(diào)整工具類,解決“月末、下周一、第一個工作日”等復(fù)雜日期計算。

常用調(diào)整器

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

核心API示例

LocalDate now = LocalDate.now();

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

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

// 當月第一個工作日(假設(shè)周末休息)
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格式、本地化格式。

核心特性

  • 線程安全,可定義為靜態(tài)常量;
  • 支持ISO 8601標準格式;
  • 支持本地化(中文/英文日期);
  • 嚴格模式(避免解析非法日期)。

核心API示例

// 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 核心類關(guān)系圖:場景→類匹配指南

業(yè)務(wù)場景推薦類避坑提示
生日/節(jié)假日LocalDate無需時區(qū),避免過度設(shè)計
鬧鐘/門禁時間LocalTime僅關(guān)注時間點,不含日期
本地餐廳預(yù)約LocalDateTime僅限本地,不跨時區(qū)
全球會議時間ZonedDateTime必須顯式指定時區(qū)
系統(tǒng)時間戳Instant基于UTC,無時區(qū)差異
年齡計算Period按年/月/日計算間隔
接口耗時Duration按小時/分鐘/秒計算間隔
日期格式化DateTimeFormatter線程安全,替代SimpleDateFormat

三、基礎(chǔ)操作實戰(zhàn):100+案例代碼

本節(jié)覆蓋java.time的所有高頻基礎(chǔ)操作,從時間創(chuàng)建、運算、格式化到邊界值處理,每個場景都提供可直接復(fù)用的代碼。

3.1 時間創(chuàng)建:6大常見方式

(1)創(chuàng)建當前時間

// 1. 本地日期/時間/日期時間
LocalDate localDateNow = LocalDate.now();
LocalTime localTimeNow = LocalTime.now();
LocalDateTime localDateTimeNow = LocalDateTime.now();

// 2. 指定時區(qū)的當前時間
ZonedDateTime shanghaiNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime utcNow = ZonedDateTime.now(ZoneOffset.UTC);

// 3. 機器時間戳
Instant instantNow = Instant.now();

(2)創(chuàng)建指定時間

// 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. 從時間戳創(chuàng)建
long epochMilli = 1771852200000L; // 2026-03-24 14:30:00的毫秒數(shù)
Instant instantFromMilli = Instant.ofEpochMilli(epochMilli);
LocalDateTime dateTimeFromMilli = LocalDateTime.ofInstant(instantFromMilli, ZoneId.of("Asia/Shanghai"));

// 3. 從字符串創(chuàng)建(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. 從字符串創(chuàng)建(自定義格式)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss");
LocalDateTime dateTimeFromCustom = LocalDateTime.parse("20260324 143045", formatter);

(3)從數(shù)據(jù)庫字段創(chuàng)建(JDBC)

// 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. 讀取帶時區(qū)的時間(推薦用OffsetDateTime)
    OffsetDateTime offsetDateTime = rs.getObject("offset_datetime", OffsetDateTime.class);
}

3.2 時間運算:8大類高頻操作

(1)加減運算(天/小時/分鐘/月/年)

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)

// 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)日期調(diào)整(TemporalAdjusters)

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. 自定義調(diào)整器(比如:下一個工作日)
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)自定義格式

// 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標準格式

// 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)本地化格式化

// 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時間

// 工具方法:null轉(zhuǎn)換為默認值
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)邊界值處理

// 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日會自動調(diào)整(ResolverStyle.SMART模式)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withResolverStyle(ResolverStyle.SMART);
LocalDate adjustedDate = LocalDate.parse("2025-02-29", fmt); // 自動轉(zhuǎn)為2025-03-01

四、時區(qū)處理實戰(zhàn):跨時區(qū)系統(tǒng)核心

跨時區(qū)是時間處理的最大痛點,java.time通過顯式時區(qū)設(shè)計,完美解決了舊API的時區(qū)混亂問題。

4.1 核心概念:時區(qū)ID與偏移量

  • ZoneId:時區(qū)標識符(如Asia/ShanghaiAmerica/New_York),包含夏令時規(guī)則;
  • ZoneOffset:UTC偏移量(如+08:00、-05:00),僅表示與UTC的差值,無夏令時;
  • 夏令時:部分地區(qū)(如美國、歐洲)每年調(diào)整時間(春季加1小時,秋季減1小時)。

時區(qū)映射表(常用)

地區(qū)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 ↔ 北京時間互轉(zhuǎn)(核心案例)

// 1. UTC時間轉(zhuǎn)北京時間
// 方式1:Instant + 時區(qū)
Instant utcInstant = Instant.now(); // 當前UTC時間
ZonedDateTime shanghaiTime1 = utcInstant.atZone(ZoneId.of("Asia/Shanghai"));

// 方式2:ZonedDateTime轉(zhuǎn)換
ZonedDateTime utcZoned = ZonedDateTime.now(ZoneOffset.UTC);
ZonedDateTime shanghaiTime2 = utcZoned.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));

// 2. 北京時間轉(zhuǎn)UTC時間
ZonedDateTime shanghaiZoned = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime utcTime1 = shanghaiZoned.withZoneSameInstant(ZoneOffset.UTC);
Instant utcInstant2 = shanghaiZoned.toInstant(); // 直接轉(zhuǎn)為Instant(UTC)

// 3. 工具方法:UTC時間戳轉(zhuǎn)北京時間字符串
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 多時區(qū)轉(zhuǎn)換(上海→紐約→倫敦)

// 基礎(chǔ)時間:上海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. 多時區(qū)對比工具方法
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結(jié)果:
// 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 夏令時處理:自動適配

// 測試美國東部夏令時切換(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. 夏令時結(jié)束(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](重復(fù)的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 時區(qū)處理最佳實踐

  • 拒絕依賴系統(tǒng)默認時區(qū):所有時區(qū)操作顯式指定ZoneId,避免now()無參方法;
  • 存儲用UTC:數(shù)據(jù)庫/緩存/日志統(tǒng)一存儲UTC時間(Instant或OffsetDateTime);
  • 展示轉(zhuǎn)本地時區(qū):僅在前端/展示層根據(jù)用戶時區(qū)轉(zhuǎn)換;
  • 避免硬編碼偏移量:使用ZoneId(如Asia/Shanghai)而非ZoneOffset.ofHours(8),兼容夏令時;
  • 時區(qū)ID使用IANA標準:如Asia/Shanghai,而非CST/GMT+8等模糊標識。

五、工程落地:與框架/數(shù)據(jù)庫整合

java.time在企業(yè)項目中的落地,需要與ORM框架、Spring Boot、數(shù)據(jù)庫等無縫整合,本節(jié)提供全套整合方案。

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

(1)MyBatis配置(MyBatis 3.4+)

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

Java類型數(shù)據(jù)庫類型映射方式
LocalDateDATE直接映射
LocalTimeTIME直接映射
LocalDateTimeDATETIME/TIMESTAMP直接映射
OffsetDateTimeTIMESTAMP WITH TIME ZONE直接映射
InstantBIGINT(毫秒數(shù))手動轉(zhuǎn)換

(2)實體類示例

public class Order {
    private Long id;
    private String orderNo;
    // 本地日期(無時區(qū))
    private LocalDate orderDate;
    // 帶時區(qū)的下單時間(推薦)
    private OffsetDateTime createTime;
    // 本地時間(支付時間點)
    private LocalTime payTime;
    
    // getter/setter
}

(3)Mapper.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自動填充

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // 填充創(chuàng)建時間(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中時間參數(shù)的接收與返回

(1)請求參數(shù)接收(@DateTimeFormat)

@RestController
@RequestMapping("/order")
public class OrderController {
    // 接收LocalDate參數(shù)(格式:yyyy-MM-dd)
    @GetMapping("/by-date")
    public List<Order> getByDate(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate orderDate) {
        return orderService.getByDate(orderDate);
    }
    
    // 接收LocalDateTime參數(shù)(格式: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)響應(yīng)結(jié)果格式化(@JsonFormat)

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(轉(zhuǎn)為東8區(qū))
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
    private OffsetDateTime createTime;
    
    // getter/setter
}

(3)全局時間格式化配置(Spring Boot)

@Configuration
public class WebConfig implements WebMvcConfigurer {
    // 全局請求參數(shù)時間格式化
    @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格式(轉(zhuǎn)為東8區(qū))
            DateTimeFormatter offsetFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                .withZone(ZoneId.of("Asia/Shanghai"));
            builder.serializers(new OffsetDateTimeSerializer(offsetFmt));
        };
    }
}

5.3 數(shù)據(jù)庫存儲最佳實踐

存儲方案適用場景優(yōu)點缺點
Instant(毫秒數(shù))高性能、分布式系統(tǒng)存儲體積小,無時區(qū)歧義可讀性差,需轉(zhuǎn)換后展示
OffsetDateTime跨時區(qū)業(yè)務(wù)含偏移量,可讀性好部分數(shù)據(jù)庫(如MySQL)無原生類型,需存為字符串/TIMESTAMP
LocalDateTime + 時區(qū)字段需保留原始時區(qū)完整保留時區(qū)上下文存儲冗余,查詢復(fù)雜

推薦方案

  • 跨時區(qū)系統(tǒng):存儲OffsetDateTime(MySQL用TIMESTAMP類型,自動轉(zhuǎn)換為UTC存儲);
  • 本地業(yè)務(wù):存儲LocalDateTime(MySQL用DATETIME類型);
  • 高性能場景:存儲Instant的毫秒數(shù)(MySQL用BIGINT類型)。

5.4 日志中的時間規(guī)范

// 日志格式配置(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+時區(qū)標注,格式: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("{} - 訂單{}創(chuàng)建成功", LogUtils.getUtcLogTime(), orderNo);
// 日志輸出:2026-03-24 06:30:00.123 UTC - 訂單123456創(chuàng)建成功

六、性能與最佳實踐

6.1 java.time類的性能對比

操作java.time(LocalDateTime)Date/Calendar性能提升
創(chuàng)建實例120ns180ns33%
日期加減80ns250ns68%
格式化(線程安全)200ns500ns(需加鎖)60%
解析字符串300ns600ns50%

測試環(huán)境:JDK 17,Intel i7-12700H,16GB內(nèi)存,單次操作平均耗時(ns)。

結(jié)論java.time不僅API更友好,性能也全面超越舊API,尤其是并發(fā)場景(無需加鎖)。

6.2 線程安全驗證

// 測試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處理跨時區(qū)場景(無時區(qū)信息,易出錯);
  2. 依賴系統(tǒng)默認時區(qū)(部署到不同服務(wù)器會出問題);
  3. 直接拼接時間字符串(如year + "-" + month + "-" + day,易出格式錯誤);
  4. 硬編碼時區(qū)偏移量(如ZoneOffset.ofHours(8),不兼容夏令時);
  5. 使用SimpleDateFormat(線程不安全,已被淘汰);
  6. 忽略閏年/夏令時(如手動計算月份天數(shù));
  7. 存儲帶時區(qū)的時間為LocalDateTime(丟失時區(qū)信息);
  8. 解析時使用寬松模式(如允許2月30日,導(dǎo)致數(shù)據(jù)錯誤);
  9. 用Duration計算日期差(Duration基于時間,Period基于日期);
  10. 直接比較不同時區(qū)的LocalDateTime(如上海14:00 vs 紐約14:00)。

6.4 最佳實踐總結(jié)

  1. 優(yōu)先使用不可變類:所有java.time類都是不可變的,放心復(fù)用;
  2. 顯式指定時區(qū):所有時間操作明確ZoneId,拒絕隱式轉(zhuǎn)換;
  3. 存儲用UTC:數(shù)據(jù)庫/緩存統(tǒng)一存儲UTC時間,展示層轉(zhuǎn)換;
  4. 使用ISO 8601格式:接口 交互優(yōu)先用ISO格式,避免自定義格式;
  5. 復(fù)用DateTimeFormatter:定義為靜態(tài)常量,避免重復(fù)創(chuàng)建;
  6. 嚴格模式解析:生產(chǎn)環(huán)境使用ResolverStyle.STRICT,避免非法日期;
  7. 工具類封裝:將高頻操作(如UTC轉(zhuǎn)本地時間)封裝為工具方法。

七、第三方工具庫整合

java.time的API雖然強大,但部分高頻操作仍需封裝,第三方工具庫可簡化開發(fā)。

7.1 Hutool DateUtil(推薦)

Hutool是國產(chǎn)開源工具庫,對java.time做了大量封裝,一行代碼實現(xiàn)復(fù)雜操作:

依賴引入

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.22</version>
</dependency>

核心示例

// 1. UTC時間轉(zhuǎn)北京時間字符串
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. 日期判斷(是否周末/節(jié)假日)
boolean isWeekend = DateUtil.isWeekend(LocalDate.now());

7.2 commons-lang3 DateUtils

Apache Commons Lang3提供了兼容舊API的過渡方案,適合存量項目改造:

依賴引入

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

核心示例

// 1. 舊API轉(zhuǎn)新API
Date oldDate = new Date();
LocalDateTime newDateTime = DateUtils.toLocalDateTime(oldDate);

// 2. 新API轉(zhuǎn)舊API(兼容存量代碼)
LocalDateTime newDateTime = LocalDateTime.now();
Date oldDate = DateUtils.toDate(newDateTime);

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

7.3 自定義工具類(高頻操作封裝)

/**
 * 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");
    
    // 上海時區(qū)
    private static final ZoneId SHANGHAI_ZONE = ZoneId.of("Asia/Shanghai");
    // UTC時區(qū)
    private static final ZoneId UTC_ZONE = ZoneOffset.UTC;

    /**
     * UTC毫秒數(shù)轉(zhuǎn)上海時間字符串
     */
    public static String utcMilliToShanghaiStr(long utcMilli) {
        return Instant.ofEpochMilli(utcMilli)
            .atZone(SHANGHAI_ZONE)
            .format(DEFAULT_FORMATTER);
    }

    /**
     * 上海時間字符串轉(zhuǎn)UTC毫秒數(shù)
     */
    public static long shanghaiStrToUtcMilli(String shanghaiStr) {
        return LocalDateTime.parse(shanghaiStr, DEFAULT_FORMATTER)
            .atZone(SHANGHAI_ZONE)
            .toInstant()
            .toEpochMilli();
    }

    /**
     * 獲取本月第一天0點(上海時區(qū))
     */
    public static LocalDateTime getFirstDayOfMonth() {
        return LocalDate.now(SHANGHAI_ZONE)
            .with(TemporalAdjusters.firstDayOfMonth())
            .atStartOfDay();
    }

    /**
     * 獲取本月最后一天23:59:59(上海時區(qū))
     */
    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;
    }
}

八、總結(jié)與預(yù)告

8.1 java.time核心優(yōu)勢

  1. 不可變性:線程安全,無副作用;
  2. 語義清晰:拆分LocalDate/LocalTime/ZonedDateTime,各司其職;
  3. 時區(qū)顯式化:杜絕隱式時區(qū)轉(zhuǎn)換的坑;
  4. API人性化:方法命名直觀,告別Calendar的魔法值;
  5. 性能優(yōu)異:全面超越舊API,并發(fā)場景優(yōu)勢更明顯;
  6. 標準化:原生支持ISO 8601,兼容國際標準。

8.2 核心建議

  • 新項目直接使用java.time,徹底拋棄Date/Calendar;
  • 存量項目逐步遷移,先用java.time封裝工具類,再替換底層實現(xiàn);
  • 跨時區(qū)系統(tǒng)優(yōu)先使用ZonedDateTime/OffsetDateTime,存儲用UTC;
  • 避免重復(fù)造輪子,優(yōu)先使用Hutool等成熟工具庫封裝高頻操作。

以上就是Java利用java.time處理時間的方法全解析的詳細內(nèi)容,更多關(guān)于Java時間處理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot深入刨析數(shù)據(jù)層技術(shù)

    SpringBoot深入刨析數(shù)據(jù)層技術(shù)

    這篇文章主要介紹了SpringBoot數(shù)據(jù)層技術(shù)的解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • JAVA正則表達式校驗qq號碼的方法

    JAVA正則表達式校驗qq號碼的方法

    Java作為一種開發(fā)語言,有許多值得推薦的地方,但是它一直以來沒有自帶對正則表達式的支持。下面小編給大家?guī)砹薐AVA正則表達式校驗qq號碼的方法,需要的朋友參考下吧
    2018-04-04
  • SpringBoot整合SpringSecurity實現(xiàn)權(quán)限控制之實現(xiàn)多標簽頁

    SpringBoot整合SpringSecurity實現(xiàn)權(quán)限控制之實現(xiàn)多標簽頁

    這篇文章主要介紹了SpringBoot整合SpringSecurity實現(xiàn)權(quán)限控制之實現(xiàn)多標簽頁,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-11-11
  • springboot 中文件上傳下載實例代碼

    springboot 中文件上傳下載實例代碼

    Spring Boot是由Pivotal團隊提供的全新框架,其設(shè)計目的是用來簡化新Spring應(yīng)用的初始搭建以及開發(fā)過程。這篇文章主要介紹了springboot 中文件上傳下載實例代碼,需要的朋友可以參考下
    2017-11-11
  • 詳解Java中的線程讓步y(tǒng)ield()與線程休眠sleep()方法

    詳解Java中的線程讓步y(tǒng)ield()與線程休眠sleep()方法

    Java中的線程讓步會讓線程讓出優(yōu)先級,而休眠則會讓線程進入阻塞狀態(tài)等待被喚醒,這里我們對比線程等待的wait()方法,來詳解Java中的線程讓步y(tǒng)ield()與線程休眠sleep()方法
    2016-07-07
  • IDEA中的.iml文件和.idea文件夾使用方式

    IDEA中的.iml文件和.idea文件夾使用方式

    這篇文章主要介紹了IDEA中的.iml文件和.idea文件夾使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • java 工廠模式的實例詳解

    java 工廠模式的實例詳解

    這篇文章主要介紹了java 工廠模式的實例詳解的相關(guān)資料,這里舉例說明該如何實現(xiàn)工廠模式,需要的朋友可以參考下
    2017-09-09
  • Springboot shiro認證授權(quán)實現(xiàn)原理及實例

    Springboot shiro認證授權(quán)實現(xiàn)原理及實例

    這篇文章主要介紹了Springboot shiro認證授權(quán)實現(xiàn)原理及實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • Java static方法用法實戰(zhàn)案例總結(jié)

    Java static方法用法實戰(zhàn)案例總結(jié)

    這篇文章主要介紹了Java static方法用法,結(jié)合具體案例形式總結(jié)分析了java static方法功能、使用方法及相關(guān)操作注意事項,需要的朋友可以參考下
    2019-09-09
  • SpringBoot3集成和使用Jasypt的代碼詳解

    SpringBoot3集成和使用Jasypt的代碼詳解

    隨著信息安全的日益受到重視,加密敏感數(shù)據(jù)在應(yīng)用程序中變得越來越重要,Jasypt作為一個簡化Java應(yīng)用程序中數(shù)據(jù)加密的工具,為開發(fā)者提供了一種便捷而靈活的加密解決方案,本文將深入解析Jasypt的工作原理,需要的朋友可以參考下
    2024-01-01

最新評論

南京市| 屏边| 霍邱县| 临朐县| 南昌县| 南投市| 武夷山市| 白山市| 晋宁县| 抚顺市| 永州市| 铁岭县| 新野县| 聊城市| 罗江县| 都江堰市| 新竹市| 繁峙县| 富锦市| 普定县| 泾川县| 祁连县| 寻甸| 五大连池市| 五寨县| 大厂| 鹤山市| 阳朔县| 台中市| 柯坪县| 吐鲁番市| 阜新| 沙洋县| 桦甸市| 安阳县| 邮箱| 衡阳县| 庆城县| 鸡泽县| 金寨县| 新沂市|