在過(guò)去的Java版本中,日期和時(shí)間的處理主要依賴于java.util.Date和java.util.Calendar類,然而隨著業(yè)務(wù)系統(tǒng)的復(fù)雜以及技術(shù)層面的提升,這些傳統(tǒng)的日期時(shí)間類暴露出了若干顯著的不足之處。隨著Java8的發(fā)布,其引入了一套全新的日期時(shí)間API,徹底改變了我們處理日期和時(shí)間的方式。
相比較Java8中新引入的java.time包下的時(shí)間處理類,傳統(tǒng)的日期時(shí)間處理類在易用性,線程安全,不支持市時(shí)區(qū)等缺點(diǎn)。
Date currentDate = new Date(); // 輸出原始的日期時(shí)間,通常不是人類可讀格式 Fri Mar 08 03:13:47 CST 2024System.out.println(currentDate);// 要改變?nèi)掌诘哪硞€(gè)部分,必須先將其轉(zhuǎn)換為 Calendar,然后設(shè)置 Calendar calendar = Calendar.getInstance(); calendar.setTime(currentDate); // 修改日期的天數(shù) calendar.add(Calendar.DAY_OF_MONTH, 1);
Date類內(nèi)部維護(hù)了一個(gè) long 類型的瞬時(shí)值,當(dāng)調(diào)用如setTime()方法來(lái)更新這個(gè)瞬時(shí)值時(shí),不同的線程同時(shí)調(diào)用就會(huì)互相覆蓋彼此的值,造成數(shù)據(jù)不一致。
Calendar類不僅包含了日期和時(shí)間信息,還有一系列內(nèi)部狀態(tài)變量,如年、月、日、小時(shí)、分鐘、秒等。Calendar類的方法通常會(huì)修改這些內(nèi)部狀態(tài),例如 add()、set() 等。在多線程環(huán)境下,若多個(gè)線程嘗試同時(shí)修改同一個(gè) Calendar 實(shí)例,也會(huì)導(dǎo)致不可預(yù)期的結(jié)果。
SimpleDateFormat類在執(zhí)行格式化和解析日期時(shí)間操作時(shí),內(nèi)部會(huì)維護(hù)一個(gè) Calendar對(duì)象以及其他一些狀態(tài)變量。在 format() 或 parse() 方法執(zhí)行過(guò)程中,這些狀態(tài)會(huì)被更新以完成格式轉(zhuǎn)換。并且SimpleDateFormat中的方法并非原子操作,因此在多線程并發(fā)調(diào)用時(shí),可能在一個(gè)線程還未完成整個(gè)操作時(shí)就被另一個(gè)線程打斷,導(dǎo)致錯(cuò)誤的日期時(shí)間處理結(jié)果。
Java8中引入的LocalDate,LocalTime,LocalDateTime這幾個(gè)位于java.time下的類克服了上述傳統(tǒng)類別的局限性,提供了更強(qiáng)大、直觀和精準(zhǔn)的日期時(shí)間處理能力,成為現(xiàn)代Java開發(fā)中處理日期時(shí)間首選的工具類。相比較傳統(tǒng)的日期時(shí)間類,具備以下顯著優(yōu)勢(shì):
java.time下主要有如下一些關(guān)鍵類:
這些類共同構(gòu)成了一個(gè)強(qiáng)大、靈活且易于使用的日期時(shí)間處理體系,大大改善了Java在處理日期時(shí)間問(wèn)題時(shí)的效率和準(zhǔn)確性。接下來(lái)我們?cè)谑褂蒙戏謩e介紹這些類,以及使用他們的方式,感受他們的強(qiáng)大。
LocalTime localTime = LocalTime.now(); System.out.println("localTime:"+localTime); LocalDate localDate = LocalDate.now(); System.out.println("localDate:"+localDate); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println("localDateTime:"+localDateTime);
輸出為:
localTime:15:28:45.241181localDate:2024-03-11localDateTime:2024-03-11T15:28:45.260655
針對(duì)LocalTime,LocalDateTime獲取當(dāng)前時(shí)刻默認(rèn)會(huì)帶有毫秒,如果不需要毫秒的話,可以通過(guò)設(shè)置納秒為0 保留秒 1秒 = 十億納秒 。例如:
LocalTime localTime = LocalTime.now().withNano(0);System.out.println("localTime:"+localTime); LocalDateTime localDateTime = LocalDateTime.now().withNano(0); System.out.println("localDateTime:"+localDateTime);
輸出為:
localTime:15:32:31localDateTime:2024-03-11T15:32:31
而對(duì)于LocalDateTime獲取當(dāng)前日期,默認(rèn)toString會(huì)帶有T分隔日期和時(shí)間,在項(xiàng)目中,可以通過(guò)全局序列化,進(jìn)行統(tǒng)一的時(shí)間格式輸出為 yyyy-MM-dd HH:mm:ss。但是一般不建議這么干,畢竟改變?nèi)中蛄谢渲茫ㄗh不使用toString,可以使用DateTimeFormatter進(jìn)行自定義轉(zhuǎn)換。
// of方法直接傳遞對(duì)應(yīng)的年、月、日 LocalDate localDate = LocalDate.of(2024, 3, 11); System.out.println("localDate:"+localDate); localDate = LocalDate.of(2024, Month.MARCH, 11); System.out.println("localDate:"+localDate); localDate = LocalDate.ofYearDay(2024, 71); System.out.println("localDate:"+localDate); // 北京時(shí)間對(duì)應(yīng)的時(shí)區(qū) ZoneId chinaTimeZone = ZoneId.of("Asia/Shanghai"); // 創(chuàng)建一個(gè) Instant,這里使用當(dāng)前時(shí)間的 InstantInstant instant = Instant.now(); localDate = LocalDate.ofInstant(instant, chinaTimeZone); System.out.println("localDate:"+localDate); // 使用ofEpochDay()方法,則EpochDay為從公元1970年1月1日(Unix紀(jì)元)開始的第多少天 localDate = LocalDate.ofEpochDay(LocalDate.now().toEpochDay()); System.out.println("localDate:"+localDate); LocalTime localTime = LocalTime.of(1, 30); System.out.println("localTime:"+localTime); localTime = LocalTime.of(1, 30, 30); System.out.println("localTime:"+localTime); localTime = LocalTime.ofInstant(instant, chinaTimeZone); System.out.println("localTime:"+localTime); // 根據(jù)一天中的總秒數(shù)構(gòu)建時(shí)間localTime = LocalTime.ofSecondOfDay(localTime.toSecondOfDay()); System.out.println("localTime:"+localTime); LocalDateTime localDateTime = LocalDateTime.of(2024, 3, 11, 1, 30, 30); System.out.println("localDateTime:"+localDateTime); localDateTime = LocalDateTime.of(2024, Month.MARCH, 11, 1, 30, 30); System.out.println("localDateTime:"+localDateTime); // 使用LocalDate和LocalTime組合構(gòu)造 localDateTime = LocalDateTime.of(localDate, localTime); System.out.println("localDateTime:"+localDateTime); localDateTime = LocalDateTime.ofInstant(instant, chinaTimeZone); System.out.println("localDateTime:"+localDateTime);
輸出為:
localDate:2024-03-11localDate:2024-03-11localDate:2024-03-11localDate:2024-03-11localDate:2024-03-11localTime:01:30localTime:01:30:30localTime:16:41:37.893310localTime:16:41:37localDateTime:2024-03-11T01:30:30localDateTime:2024-03-11T01:30:30localDateTime:2024-03-11T16:41:37localDateTime:2024-03-11T16:41:37.893310
from()方法將TemporalAccessor類型(如ZonedDateTime)轉(zhuǎn)換為相對(duì)應(yīng)的日期或者時(shí)間。TemporalAccessor接口是一個(gè)用于讀取或?qū)懭肴掌凇r(shí)間或者日期時(shí)間的通用接口。
// 創(chuàng)建一個(gè)ZonedDateTime實(shí)例 ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); LocalTime localTime = LocalTime.from(zonedDateTime); System.out.println("localTime:"+localTime); LocalDate localDate = LocalDate.from(zonedDateTime); System.out.println("localDate:"+localDate); LocalDateTime localDateTime = LocalDateTime.from(zonedDateTime); System.out.println("localDateTime:"+localDateTime);
輸出為:
localTime:17:18:27.942911localDate:2024-03-11localDateTime:2024-03-11T17:18:27.942911
將字符串按照指定格式(可選)解析為對(duì)應(yīng)的日期時(shí)間類。
LocalTime localTime = LocalTime.parse("17:25:30"); System.out.println("localTime:"+localTime); localTime = LocalTime.parse("17:25:30", DateTimeFormatter.ofPattern("HH:mm:ss")); System.out.println("localTime:"+localTime); LocalDate localDate = LocalDate.parse("2024-03-11"); System.out.println("localDate:"+localDate); localDate = LocalDate.parse("2024/03/11", DateTimeFormatter.ofPattern("yyyy/MM/dd")); System.out.println("localDate:"+localDate); LocalDateTime localDateTime = LocalDateTime.parse("2024-03-11T17:25:30"); System.out.println("localDateTime:"+localDateTime); localDateTime = LocalDateTime.parse("2024/03/11 17:25:30", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")); System.out.println("localDateTime:"+localDateTime);
輸出為:
localTime:17:25:30localTime:17:25:30localDate:2024-03-11localDate:2024-03-11localDateTime:2024-03-11T17:25:30localDateTime:2024-03-11T17:25:30
// LocalTime + LocalDate = LocalDateTimeLocalDateTime localDateTime = LocalTime.now().atDate(LocalDate.now());System.out.println("localDateTime:"+localDateTime);localDateTime = LocalDate.now().atTime(LocalTime.now());System.out.println("localDateTime:"+localDateTime);localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.now());System.out.println("localDateTime:"+localDateTime);// LocalDateTime 轉(zhuǎn) LocalDateLocalDate localDate = LocalDateTime.now().toLocalDate();System.out.println("localDate:"+localDate);// LocalDateTime 轉(zhuǎn) LocalTimeLocalTime localTime = LocalDateTime.now().toLocalTime();System.out.println("localTime:"+localTime);// 獲取今日開始時(shí)間 2024-03-11T00:00localDateTime = LocalDate.now().atStartOfDay();System.out.println("localDateTime:"+localDateTime);// 獲取今日開始時(shí)間 2024-03-11T00:00LocalDateTime startDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);System.out.println("startDateTime:"+ startDateTime);// 獲取今日結(jié)束時(shí)間 2024-03-11T23:59:59.999999999LocalDateTime endDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);System.out.println("endDateTime:"+ endDateTime);
輸出為:
localDateTime:2024-03-11T18:04:22.348539localDateTime:2024-03-11T18:04:22.370562localDateTime:2024-03-11T18:04:22.370768localDate:2024-03-11localTime:18:04:22.371062localDateTime:2024-03-11T00:00startDateTime:2024-03-11T00:00endDateTime:2024-03-11T23:59:59.999999999
主要使用format 和 parse 進(jìn)行轉(zhuǎn)換,使用方法基本相同。使用 DateTimeFormatter.ofPattern() 定義時(shí)間格式,再進(jìn)行轉(zhuǎn)換。DateTimeFormatter線程安全。
// LocalTime 轉(zhuǎn) String 自定義輸出格式,例如:**時(shí)**分**秒 該轉(zhuǎn)化的 00 不會(huì)被省略String localTimeStr = LocalTime.now().format(DateTimeFormatter.ofPattern("HH時(shí)mm分ss秒"));System.out.println("localTimeStr:"+localTimeStr);String localDateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));System.out.println("localDateStr:"+localDateStr);// LocalDateTime 轉(zhuǎn) StringString localDateTimeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));System.out.println("localDateTimeStr:"+localDateTimeStr);// String 轉(zhuǎn) LocalDateTimeLocalDateTime localDateTime = LocalDateTime.parse("2023-04-14 15:59:40", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));System.out.println("localDateTime:"+localDateTime);
輸出結(jié)果:
localTimeStr:19時(shí)02分58秒localDateStr:2024-03-11localDateTimeStr:2024-03-11 19:02:58localDateTime:2023-04-14T15:59:40
// Date 轉(zhuǎn) LocalDateTimeDate currentDate = new Date();// 轉(zhuǎn)換為Instant Instant instant = currentDate.toInstant();// 通過(guò)zoneId設(shè)置時(shí)區(qū)(這里使用系統(tǒng)時(shí)區(qū)),轉(zhuǎn)換為帶帶時(shí)區(qū)的 ZoneDateTimeZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());// 然后通過(guò)ZonedDateTime轉(zhuǎn)換為L(zhǎng)ocalDateTimeLocalDateTime localDateTime = zonedDateTime.toLocalDateTime();System.out.println("localDateTime:"+localDateTime);// LocalDateTime 轉(zhuǎn) Date,同理也是通過(guò)ZonedDateTime轉(zhuǎn)換為DateDate localDateTimeToDate = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());System.out.println(localDateTimeToDate);// Date轉(zhuǎn)LocalDate 同理 LocalDateTime轉(zhuǎn)換LocalDate localDate = currentDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();System.out.println("localDate:"+localDate);// LocalDate 轉(zhuǎn) Date 需要先將 LocalDate 轉(zhuǎn) LocalDateTimeDate localDateToDate = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
這里介紹一下ZoneId。java.time.ZoneId是Java 8中java.time包中用于表示時(shí)區(qū)的類。時(shí)區(qū)是地球上的地理位置,用于確定在該位置觀察太陽(yáng)升落以及規(guī)定當(dāng)?shù)鼐用裆詈蜕虡I(yè)活動(dòng)時(shí)間的標(biāo)準(zhǔn)時(shí)間。ZoneId使用IANA時(shí)區(qū)數(shù)據(jù)庫(kù)提供的時(shí)區(qū)標(biāo)識(shí)符,這個(gè)標(biāo)識(shí)符是唯一的,這些標(biāo)識(shí)符通常是地區(qū)/城市對(duì),例如“Asia/Shanghai”代表中國(guó)上海所在的時(shí)區(qū),America/New_York代表美國(guó)紐約城市。其實(shí)例獲取有兩種方式:
時(shí)間戳轉(zhuǎn)換。
long timeMillis = System.currentTimeMillis();// 時(shí)間戳(Long) 轉(zhuǎn) LocalDateTimeLocalDateTime localDateTime = Instant.ofEpochMilli(timeMillis).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();System.out.println("localDateTime:"+localDateTime);localDateTime = Instant.ofEpochMilli(timeMillis).atZone(ZoneId.systemDefault()).toLocalDateTime();System.out.println("localDateTime:"+localDateTime);// LocalDateTime 轉(zhuǎn) 時(shí)間戳(Long) 秒級(jí)long localDateTimeToSecond = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));System.out.println("localDateTimeToSecond:"+ localDateTimeToSecond);// LocalDateTime 轉(zhuǎn) 時(shí)間戳(Long) 毫秒級(jí)long localDateTimeToMilliSecond = LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();System.out.println("localDateTimeToMilliSecond:"+ localDateTimeToMilliSecond);// 時(shí)間戳(Long) 轉(zhuǎn) LocalDateLocalDate localDate = Instant.ofEpochMilli(timeMillis).atZone(ZoneOffset.ofHours(8)).toLocalDate();System.out.println("localDate:"+ localDate);// LocalDate 轉(zhuǎn) 時(shí)間戳(Long) 秒級(jí)long localDateToSecond = LocalDate.now().atStartOfDay().toEpochSecond(ZoneOffset.ofHours(8));System.out.println("localDateToSecond:"+ localDateToSecond);// LocalDate 轉(zhuǎn) 時(shí)間戳(Long) 毫秒級(jí)long localDateToMilliSecond = LocalDate.now().atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();System.out.println("localDateToMilliSecond:"+ localDateToMilliSecond);
輸出結(jié)果為:
localDateTime:2024-03-11T19:37:02.335localDateTime:2024-03-11T19:37:02.335localDateTimeToSecond:1710157022localDateTimeToMilliSecond:1710157022365localDate:2024-03-11localDateToSecond:1710086400localDateToMilliSecond:1710086400000
java.time.ZoneOffset是Java8中java.time包內(nèi)用來(lái)表示時(shí)區(qū)偏移量的類,它表示的是格林尼治標(biāo)準(zhǔn)時(shí)間或協(xié)調(diào)世界時(shí)間(UTC)基礎(chǔ)上的固定偏移量。每一個(gè)時(shí)區(qū)都可以通過(guò)一個(gè)或多個(gè)偏移量來(lái)表示,比如“+02:00”表示比UTC時(shí)間快兩個(gè)小時(shí)的時(shí)區(qū)偏移。其實(shí)例創(chuàng)建有如下方式:
java.time 包中日期時(shí)間類(如 LocalDateTime、LocalDate 和 LocalTime)可以通過(guò)plusXxx() 和 minusXxx() 方法,用于對(duì)日期時(shí)間對(duì)象進(jìn)行加減操作,以增加或減少指定的時(shí)間或日期單位。
1、LocalDateTime 加減:
// LocalDateTime 加減LocalDateTime localDateTime = LocalDateTime.now();// 以下為增加時(shí)、分、秒LocalDateTime plusLocalDateTime = localDateTime.plusHours(1).plusMinutes(1).plusSeconds(1);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(1, ChronoUnit.HOURS).plus(1, ChronoUnit.MINUTES).plus(1, ChronoUnit.SECONDS);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(Duration.ofHours(1)).plus(Duration.of(1, ChronoUnit.MINUTES)).plus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("plusLocalDateTime:"+plusLocalDateTime);// 以下為增加年、月、日plusLocalDateTime = localDateTime.plusYears(1).plusMonths(1).plusWeeks(1).plusDays(1);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(1, ChronoUnit.YEARS).plus(1, ChronoUnit.MONTHS).plus(1, ChronoUnit.WEEKS).plus(1, ChronoUnit.DAYS);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(Duration.of(1, ChronoUnit.YEARS)).plus(Duration.of(1, ChronoUnit.MONTHS)).plus(Duration.of(1, ChronoUnit.WEEKS)).plus(Duration.ofDays(1));System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(Period.ofYears(1)).plus(Period.ofMonths(1)).plus(Period.ofWeeks(1)).plus(Period.ofDays(1));System.out.println("plusLocalDateTime:"+plusLocalDateTime);// 以下為減少時(shí)、分、秒LocalDateTime minusLocalDateTime = localDateTime.minusHours(1).minusMinutes(1).minusSeconds(1);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(1, ChronoUnit.HOURS).minus(1, ChronoUnit.MINUTES).minus(1, ChronoUnit.SECONDS);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(Duration.ofHours(1)).minus(Duration.of(1, ChronoUnit.MINUTES)).minus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("minusLocalDateTime:"+minusLocalDateTime);// 以下為減少年、月、日minusLocalDateTime = localDateTime.minusYears(1).minusMonths(1).minusWeeks(1).minusDays(1);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(1, ChronoUnit.YEARS).minus(1, ChronoUnit.MONTHS).minus(1, ChronoUnit.WEEKS).minus(1, ChronoUnit.DAYS);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(Duration.of(1, ChronoUnit.YEARS)).minus(Duration.of(1, ChronoUnit.MONTHS)).minus(Duration.of(1, ChronoUnit.WEEKS)).minus(Duration.ofDays(1));System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(Period.ofYears(1)).minus(Period.ofMonths(1)).minus(Period.ofWeeks(1)).minus(Period.ofDays(1));System.out.println("plusLocalDateTime:"+minusLocalDateTime);
2、LocalDate 加減:
// LocalDate 加減LocalDate localDate = LocalDate.now();LocalDate plusLocalDate = localDate.plusYears(1).plusMonths(1).plusWeeks(1).plusDays(1);System.out.println("plusLocalDate:"+plusLocalDate);plusLocalDate = localDate.plus(1, ChronoUnit.YEARS).plus(1, ChronoUnit.MONTHS).plus(1, ChronoUnit.WEEKS).plus(1, ChronoUnit.DAYS);System.out.println("plusLocalDate:"+plusLocalDate);plusLocalDate = localDate.plus(Duration.of(1, ChronoUnit.YEARS)).plus(Duration.of(1, ChronoUnit.MONTHS)).plus(Duration.of(1, ChronoUnit.WEEKS)).plus(Duration.ofDays(1));System.out.println("plusLocalDate:"+plusLocalDate);plusLocalDate = localDate.plus(Period.ofYears(1)).plus(Period.ofMonths(1)).plus(Period.ofWeeks(1)).plus(Period.ofDays(1));System.out.println("plusLocalDate:"+plusLocalDate);LocalDate minusLocalDate = localDate.minusYears(1).minusMonths(1).minusWeeks(1).minusDays(1);System.out.println("minusLocalDate:"+minusLocalDate);minusLocalDate = localDate.minus(1, ChronoUnit.YEARS).minus(1, ChronoUnit.MONTHS).minus(1, ChronoUnit.WEEKS).minus(1, ChronoUnit.DAYS);System.out.println("minusLocalDate:"+minusLocalDate);minusLocalDate = localDate.minus(Duration.of(1, ChronoUnit.YEARS)).minus(Duration.of(1, ChronoUnit.MONTHS)).minus(Duration.of(1, ChronoUnit.WEEKS)).minus(Duration.ofDays(1));System.out.println("minusLocalDate:"+minusLocalDate);minusLocalDate = localDate.minus(Period.ofYears(1)).minus(Period.ofMonths(1)).minus(Period.ofWeeks(1)).minus(Period.ofDays(1));System.out.println("minusLocalDate:"+minusLocalDate);
3、LocalTime 加減:
// LocalTime 加減LocalTime localTime = LocalTime.now();LocalTime plusLocalTime = localTime.plusHours(1).plusMinutes(1).plusSeconds(1);System.out.println("plusLocalTime:"+plusLocalTime);plusLocalTime = localTime.plus(1, ChronoUnit.HOURS).plus(1, ChronoUnit.MINUTES).plus(1, ChronoUnit.SECONDS);System.out.println("plusLocalTime:"+plusLocalTime);plusLocalTime = localTime.plus(Duration.ofHours(1)).plus(Duration.of(1, ChronoUnit.MINUTES)).plus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("plusLocalTime:"+plusLocalTime);LocalTime minusLocalTime = localTime.minusHours(1).minusMinutes(1).minusSeconds(1);System.out.println("minusLocalTime:"+minusLocalTime);minusLocalTime = localTime.minus(1, ChronoUnit.HOURS).minus(1, ChronoUnit.MINUTES).minus(1, ChronoUnit.SECONDS);System.out.println("minusLocalDateTime:"+minusLocalTime);minusLocalTime = localTime.minus(Duration.ofHours(1)).minus(Duration.of(1, ChronoUnit.MINUTES)).minus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("minusLocalDateTime:"+minusLocalTime);
LocalDate、LocalTime、LocalDateTime、ZonedDateTime可以通過(guò)相對(duì)應(yīng)的withXxx()方法修改指定的值。
1、LocalDate:
LocalDate localDate = LocalDate.of(2024, 3, 12);LocalDate newDate = localDate.withYear(2025).withMonth(4).with(ChronoField.DAY_OF_MONTH, 13);System.out.println("newDate:"+newDate);
2、LocalTime:
LocalTime localTime = LocalTime.of(17, 25, 30);LocalTime newTime = localTime.withHour(18).withMinute(26).with(ChronoField.SECOND_OF_MINUTE, 31);System.out.println("newTime:"+newTime);
3、LocalDateTime:
LocalDateTime localDateTime = LocalDateTime.of(2024, 3, 12, 17, 25, 30);LocalDateTime newDateTime = localDateTime.withYear(2025).withMonth(4).with(ChronoField.DAY_OF_MONTH, 13).withHour(18).withMinute(26).with(ChronoField.SECOND_OF_MINUTE, 31);System.out.println("newDateTime:"+ newDateTime);
4、ZonedDateTime:
ZonedDateTime zonedDateTime = ZonedDateTime.of(2024, 3, 12, 17, 25, 30, 0, ZoneId.of("Europe/London"));ZonedDateTime newZonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York"));System.out.println("newZonedDateTime:"+ newZonedDateTime);
除此之外,調(diào)整日期時(shí)間還可以通過(guò)TemporalAdjusters,TemporalAdjuster 是一個(gè)函數(shù)式接口,用于根據(jù)給定的規(guī)則調(diào)整日期時(shí)間對(duì)象。Java8的 java.time.temporal 包中預(yù)定義了一系列常用的 TemporalAdjuster 實(shí)現(xiàn),例如獲取下一個(gè)工作日、月初、月末等。
LocalDate date = LocalDate.of(2024, 3, 11);// 下一個(gè)工作日LocalDate nextWorkingDay = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 如果11號(hào)不是周一,則返回下一個(gè)周一的日期// 下一個(gè)月的第一天LocalDate firstDayNextMonth = date.with(TemporalAdjusters.firstDayOfMonth()); // 返回4月1日// 當(dāng)月的最后一個(gè)工作日LocalDate lastWorkingDay = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)); // 返回3月最后一個(gè)周五的日期// 自定義 TemporalAdjusterTemporalAdjuster adjuster = temporal -> { return temporal.plusDays(10).with(TemporalAdjusters.lastDayOfMonth());};LocalDate tenthDayNextMonthEnd = date.with(adjuster); // 返回4月最后一個(gè)日期,前提是先加10天
在Java8及其以后版本的日期時(shí)間API中,isBefore() 和 isAfter() 方法是 java.time 包中的 LocalDate、LocalTime、LocalDateTime、ZonedDateTime 等日期時(shí)間類所共有的方法,用于比較兩個(gè)日期時(shí)間對(duì)象的先后順序。
isBefore():
LocalDate date1 = LocalDate.of(2024, 3, 11);LocalDate date2 = LocalDate.of(2024, 3, 12);boolean isEarlier = date1.isBefore(date2); // 返回 true,因?yàn)?date1 在 date2 之前
isAfter():
LocalDateTime time1 = LocalDateTime.of(2024, 3, 11, 10, 0);LocalDateTime time2 = LocalDateTime.of(2024, 3, 11, 9, 0);boolean isLater = time1.isAfter(time2); // 返回 true,因?yàn)?time1 在 time2 之后
compareTo()在Java 8的 java.time 包中,大部分日期時(shí)間類如 LocalDate、LocalTime、LocalDateTime、ZonedDateTime 都實(shí)現(xiàn)了 Comparable 接口,從而可以直接使用 compareTo() 方法進(jìn)行比較。compareTo() 方法用于比較兩個(gè)日期時(shí)間對(duì)象的先后順序,返回值含義如下:
LocalDate date1 = LocalDate.of(2024, 3, 11);LocalDate date2 = LocalDate.of(2024, 3, 12);int comparisonResult = date1.compareTo(date2);if (comparisonResult < 0) { System.out.println("date1 is before date2");} else if (comparisonResult > 0) { System.out.println("date1 is after date2");} else { System.out.println("date1 is equal to date2");}LocalDateTime dateTime1 = LocalDateTime.of(2024, 3, 11, 10, 30);LocalDateTime dateTime2 = LocalDateTime.of(2024, 3, 11, 11, 00);int timeComparisonResult = dateTime1.compareTo(dateTime2);
在Java8的 java.time 包中,各個(gè)日期時(shí)間類如 LocalDate、LocalTime、LocalDateTime 提供了一系列 get 方法,用于獲取特定字段的值。
獲取日期中的特定字段:
LocalDate date = LocalDate.of(2024, 3, 11);int dayOfMonth = date.getDayOfMonth(); // 獲取當(dāng)月的第幾天,此處返回11int monthValue = date.getMonthValue(); // 獲取月份值,此處返回3Month month = date.getMonth(); // 獲取Month枚舉,此處返回Marchint year = date.getYear(); // 獲取年份,此處返回2024
對(duì)于時(shí)間部分,類似地可以獲取小時(shí)、分鐘、秒和納秒:
LocalTime time = LocalTime.of(19, 30, 45);int hour = time.getHour(); // 獲取小時(shí)數(shù),此處返回10int minute = time.getMinute(); // 獲取分鐘數(shù),此處返回30int second = time.getSecond(); // 獲取秒數(shù),此處返回45int nano = time.getNano(); // 獲取納秒數(shù)
SpringBoot默認(rèn)集成了Jackson作為JSON處理庫(kù),Jackson已經(jīng)能自動(dòng)處理 LocalDate、LocalTime 和 LocalDateTime 類型。
如果需要使用自定義日期時(shí)間格式,我們有兩種方式:
手動(dòng)更改全局配置: 如果需要自定義日期格式,可以通過(guò) ObjectMapper 的配置類來(lái)注冊(cè)自定義的日期格式化器:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId></dependency>
@Configurationpublic class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder -> { builder.simpleDateFormat("yyyy-MM-dd"); // 使用Java 8時(shí)間API的日期格式器 builder.dateFormat(new StdDateFormat().withColonInTimeZone(true)); // 注冊(cè)LocalDateTime的序列化和反序列化模塊 builder.modules(new JavaTimeModule()); }; }}
手動(dòng)綁定格式化配置SpringBoot支持自動(dòng)綁定HTTP請(qǐng)求參數(shù)到控制器方法參數(shù)中,包括 LocalDate、LocalTime 和 LocalDateTime 類型。客戶端需發(fā)送符合日期格式的字符串,Spring Boot會(huì)自動(dòng)轉(zhuǎn)換成相應(yīng)類型。
@PostMapping("/events") public ResponseEntity<Event> createEvent(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @RequestParam @DateTimeFormat(pattern = "HH:mm:ss") LocalTime startTime, @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime timestamp) { // ... }
或者請(qǐng)求或者響應(yīng)VO中:
public static class ResponseVO{ @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date; @DateTimeFormat(pattern = "HH:mm:ss") private LocalTime startTime; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime timestamp; }
在MyBatis中查詢MySQL數(shù)據(jù)庫(kù)時(shí),使用Java 8的 java.time.LocalDate、java.time.LocalTime 和 java.time.LocalDateTime類型。
LocalDate 對(duì)應(yīng)MySQL的DATE類型。LocalTime 對(duì)應(yīng)MySQL 的 TIME 類型。LocalDateTime 對(duì)應(yīng)MySQL的 DATETIME或TIMESTAMP類型。
CREATE TABLE `test_date`( `id` BIGINT ( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵', test_local_date DATE , test_local_time TIME, test_local_date_time DATETIME, PRIMARY KEY ( `id` ))ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT = '日期時(shí)間測(cè)試';
@Datapublic class TestDate implements Serializable { /** * 自增主鍵 */ private Long id; private LocalDate testLocalDate; private LocalTime testLocalTime; private LocalDateTime testLocalDateTime; private static final long serialVersionUID = 1L;}
3.MyBatis配置:
@Testpublic void testInsertDate(){ TestDate testDate = new TestDate(); testDate.setTestLocalDate(LocalDate.of(2024, 3, 12)); testDate.setTestLocalTime(LocalTime.of(20,10,30)); testDate.setTestLocalDateTime(LocalDateTime.of(2024, 3, 12,20,10,30,0)); testDateMapper.insert(testDate);}@Testpublic void testQueryDate(){ TestDate testDate = testDateMapper.selectByPrimaryKey(1L); System.out.println("testLocalDate:"+testDate.getTestLocalDate()); System.out.println("testLocalTime:"+testDate.getTestLocalTime()); System.out.println("testLocalDateTime:"+testDate.getTestLocalDateTime());}
image.png
本文探討了Java 8引入的全新日期時(shí)間API相較于傳統(tǒng)的Date和Calendar類的優(yōu)勢(shì)及實(shí)際應(yīng)用。鑒于Java 8新日期時(shí)間API在設(shè)計(jì)上的先進(jìn)性和易用性,強(qiáng)烈建議積極采納并替換掉陳舊的Date和Calendar類,轉(zhuǎn)而采用如LocalDate、LocalDateTime、ZonedDateTime等現(xiàn)代日期時(shí)間類。
Java 8新日期時(shí)間API提供了更為清晰、直觀的操作接口,支持不可變對(duì)象設(shè)計(jì)模式,增強(qiáng)了類型安全性,并具備豐富的日期時(shí)間運(yùn)算、解析與格式化功能,顯著提高了代碼質(zhì)量與可讀性。此外,新API對(duì)日期時(shí)間單位的精確度控制、時(shí)區(qū)管理以及與其他日期時(shí)間規(guī)范的兼容性等方面均表現(xiàn)出卓越的表現(xiàn)力和靈活性,使得我們?cè)谔幚砀黝悘?fù)雜日期時(shí)間邏輯時(shí)能夠更加得心應(yīng)手,提升開發(fā)效率。
本文鏈接:http://www.www897cc.com/showinfo-26-76496-0.html還在用Calendar操作Date?Java8都被放棄了,你還不知道Java8中全新的日期時(shí)間API
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com