??作者簡介:Java領域優質創作者🏆,CSDN博客專家認證🏆
??技識訓,該賞
??點贊 👍 收藏 ?再看,養成習慣
大家好,我是小虛竹,之前有粉絲私聊我,問能不能把JAVA8 新的日期時間API(JSR-310)知識點梳理出來,答案是肯定的,誰讓我寵粉呢,由于內容偏多(超十萬字了),會拆成多篇來寫,
閑話就聊到這,請看下面的正文,
使用場景
對JDK8+中的日期時間工具類封裝
專案參考
此博文的依據:hutool-5.6.5版本原始碼
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.6.5</version>
</dependency>
方法摘要
| 方法 | 描述 |
|---|---|
| cn.hutool.core.date.LocalDateTimeUtil.now() |
當前時間,默認時區
|
| cn.hutool.core.date.LocalDateTimeUtil.of(java.time.Instant) |
{@link Instant}轉{@link LocalDateTime},使用默認時區
|
| cn.hutool.core.date.LocalDateTimeUtil.ofUTC(java.time.Instant) |
{@link Instant}轉{@link LocalDateTime},使用UTC時區
|
| cn.hutool.core.date.LocalDateTimeUtil.of(java.time.ZonedDateTime) |
{@link ZonedDateTime}轉{@link LocalDateTime}
|
| cn.hutool.core.date.LocalDateTimeUtil.of(java.time.Instant, java.time.ZoneId) |
{@link Instant}轉{@link LocalDateTime}
|
| cn.hutool.core.date.LocalDateTimeUtil.of(java.time.Instant, java.util.TimeZone) |
{@link Instant}轉{@link LocalDateTime}
|
| cn.hutool.core.date.LocalDateTimeUtil.of(long) |
毫秒轉{@link LocalDateTime},使用默認時區
注意:此方法使用默認時區,如果非UTC,會產生時間偏移 |
| cn.hutool.core.date.LocalDateTimeUtil.ofUTC(long) |
毫秒轉{@link LocalDateTime},使用UTC時區
|
| cn.hutool.core.date.LocalDateTimeUtil.of(long, java.time.ZoneId) |
毫秒轉{@link LocalDateTime},根據時區不同,結果會產生時間偏移
|
| cn.hutool.core.date.LocalDateTimeUtil.of(long, java.util.TimeZone) |
毫秒轉{@link LocalDateTime},結果會產生時間偏移
|
| cn.hutool.core.date.LocalDateTimeUtil.of(java.util.Date) |
{@link Date}轉{@link LocalDateTime},使用默認時區
|
| cn.hutool.core.date.LocalDateTimeUtil.of(java.time.temporal.TemporalAccessor) |
{@link TemporalAccessor}轉{@link LocalDateTime},使用默認時區
|
| cn.hutool.core.date.LocalDateTimeUtil.ofDate(java.time.temporal.TemporalAccessor) |
{@link TemporalAccessor}轉{@link LocalDate},使用默認時區
|
| cn.hutool.core.date.LocalDateTimeUtil.parse(java.lang.CharSequence) |
決議日期時間字串為{@link LocalDateTime},僅支持yyyy-MM-dd’T’HH:mm:ss格式,例如:2007-12-03T10:15:30
|
| cn.hutool.core.date.LocalDateTimeUtil.parse(java.lang.CharSequence, java.time.format.DateTimeFormatter) |
決議日期時間字串為{@link LocalDateTime},格式支持日期時間、日期、時間
|
| cn.hutool.core.date.LocalDateTimeUtil.parse(java.lang.CharSequence, java.lang.String) |
決議日期時間字串為{@link LocalDateTime}
|
| cn.hutool.core.date.LocalDateTimeUtil.parseDate(java.lang.CharSequence) |
決議日期時間字串為{@link LocalDate},僅支持yyyy-MM-dd’T’HH:mm:ss格式,例如:2007-12-03T10:15:30
|
| cn.hutool.core.date.LocalDateTimeUtil.parseDate(java.lang.CharSequence, java.time.format.DateTimeFormatter) |
決議日期時間字串為{@link LocalDate},格式支持日期
|
| cn.hutool.core.date.LocalDateTimeUtil.parseDate(java.lang.CharSequence, java.lang.String) |
決議日期字串為{@link LocalDate}
|
| cn.hutool.core.date.LocalDateTimeUtil.formatNormal(java.time.LocalDateTime) |
格式化日期時間為yyyy-MM-dd HH:mm:ss格式
|
| cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDateTime, java.time.format.DateTimeFormatter) |
格式化日期時間為指定格式
|
| cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDateTime, java.lang.String) |
格式化日期時間為指定格式
|
| cn.hutool.core.date.LocalDateTimeUtil.formatNormal(java.time.LocalDate) |
格式化日期時間為yyyy-MM-dd格式
|
| cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDate, java.time.format.DateTimeFormatter) |
格式化日期時間為指定格式
|
| cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDate, java.lang.String) |
格式化日期時間為指定格式
|
| cn.hutool.core.date.LocalDateTimeUtil.offset(java.time.LocalDateTime, long, java.time.temporal.TemporalUnit) |
日期偏移,根據field不同加不同值(偏移會修改傳入的物件)
|
| cn.hutool.core.date.LocalDateTimeUtil.between(java.time.LocalDateTime, java.time.LocalDateTime) |
獲取兩個日期的差,如果結束時間早于開始時間,獲取結果為負,
回傳結果為{@link Duration}物件,通過呼叫toXXX方法回傳相差單位 |
| cn.hutool.core.date.LocalDateTimeUtil.between(java.time.LocalDateTime, java.time.LocalDateTime, java.time.temporal.ChronoUnit) |
獲取兩個日期的差,如果結束時間早于開始時間,獲取結果為負,
回傳結果為時間差的long值 |
| cn.hutool.core.date.LocalDateTimeUtil.betweenPeriod(java.time.LocalDate, java.time.LocalDate) |
獲取兩個日期的表象時間差,如果結束時間早于開始時間,獲取結果為負,
比如2011年2月1日,和2021年8月11日,日相差了10天,月相差6月 |
| cn.hutool.core.date.LocalDateTimeUtil.beginOfDay(java.time.LocalDateTime) |
修改為一天的開始時間,例如:2020-02-02 00:00:00,000
|
| cn.hutool.core.date.LocalDateTimeUtil.endOfDay(java.time.LocalDateTime) |
修改為一天的結束時間,例如:2020-02-02 23:59:59,999
|
| cn.hutool.core.date.LocalDateTimeUtil.toEpochMilli(java.time.temporal.TemporalAccessor) |
{@link TemporalAccessor}轉換為 時間戳(從1970-01-01T00:00:00Z開始的毫秒數)
|
方法明細-now()
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.now()
方法描述
當前時間,默認時區
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|
回傳值:
{@link LocalDateTime}
參考案例:
Assert.assertNotNull(LocalDateTimeUtil.now());
System.out.println(LocalDateTimeUtil.now());

原始碼決議:
/**
* 當前時間,默認時區
*
* @return {@link LocalDateTime}
*/
public static LocalDateTime now() {
return LocalDateTime.now();
}
LocalDateTime.now() 的原始碼
public static LocalDateTime now() {
return now(Clock.systemDefaultZone());
}
Clock.systemDefaultZone()
用的是系統默認的時區ZoneId.systemDefault()
public static Clock systemDefaultZone() {
return new SystemClock(ZoneId.systemDefault());
}

最終呼叫的也是System.currentTimeMillis()
方法明細-of(java.time.Instant)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(java.time.Instant)
方法描述
{@link Instant}轉{@link LocalDateTime},使用默認時區
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| Instant instant |
instant {@link Instant}
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2020-01-23 12:23:56";
final DateTime dt = DateUtil.parse(dateStr);
LocalDateTime of = LocalDateTimeUtil.of(dt.toInstant());
System.out.println(of);

原始碼決議:
public static LocalDateTime of(Instant instant) {
return of(instant, ZoneId.systemDefault());
}
這里使用了默認時區,所以列印出來的日期時間是帶時區的,
public static LocalDateTime of(Instant instant, ZoneId zoneId) {
if (null == instant) {
return null;
}
return LocalDateTime.ofInstant(instant, ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()));
}
方法明細-ofUTC(java.time.Instant)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.ofUTC(java.time.Instant)
方法描述
{@link Instant}轉{@link LocalDateTime},使用UTC時區
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| Instant instant |
instant {@link Instant}
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2020-01-23T12:23:56";
final DateTime dt = DateUtil.parse(dateStr);
LocalDateTime of = LocalDateTimeUtil.ofUTC(dt.toInstant());
Assert.assertEquals(dateStr, of.toString());
System.out.println(of);

原始碼決議:
public static LocalDateTime ofUTC(Instant instant) {
return of(instant, ZoneId.of("UTC"));
}
這里使用了UTC 時區,然后呼叫下面的LocalDateTime.ofInstant
public static LocalDateTime of(Instant instant, ZoneId zoneId) {
if (null == instant) {
return null;
}
return LocalDateTime.ofInstant(instant, ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()));
}
方法明細-of(java.time.ZonedDateTime)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(java.time.ZonedDateTime)
方法描述
{@link ZonedDateTime}轉{@link LocalDateTime}
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| ZonedDateTime zonedDateTime |
zonedDateTime {@link ZonedDateTime}
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-21T11:23:56";
final DateTime dt = DateUtil.parse(dateStr);
//使用默認時區
LocalDateTime localDateTime = LocalDateTimeUtil.of(dt);
System.out.println(localDateTime);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
System.out.println(zonedDateTime);
zonedDateTime = localDateTime.atZone( ZoneId.of("Asia/Shanghai"));
System.out.println(zonedDateTime);
LocalDateTime of = LocalDateTimeUtil.of(zonedDateTime);
Assert.assertNotNull(of);
Assert.assertEquals("2021-05-21T11:23:56", of.toString());

原始碼決議:
public static LocalDateTime of(ZonedDateTime zonedDateTime) {
if (null == zonedDateTime) {
return null;
}
return zonedDateTime.toLocalDateTime();
}
這里先判斷了引數 是否空值
然后呼叫zonedDateTime.toLocalDateTime()
我們知道zonedDateTime 和LocalDateTime 是可以直接轉化的
方法明細-of(java.time.Instant, java.time.ZoneId)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(java.time.Instant, java.time.ZoneId)
方法描述
{@link Instant}轉{@link LocalDateTime}
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| Instant instant |
instant {@link Instant}
|
| ZoneId zoneId |
zoneId 時區
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-21T11:23:56";
final DateTime dt = DateUtil.parse(dateStr);
LocalDateTime of = LocalDateTimeUtil.of(dt.getTime(), ZoneId.of("UTC"));
Assert.assertNotNull(of);
Assert.assertEquals(dateStr, of.toString());
of = LocalDateTimeUtil.of(dt.getTime(), ZoneId.of("Asia/Shanghai"));
Assert.assertNotNull(of);
Assert.assertEquals("2021-05-21T19:23:56", of.toString());
原始碼決議:
public static LocalDateTime of(Instant instant, ZoneId zoneId) {
if (null == instant) {
return null;
}
return LocalDateTime.ofInstant(instant, ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()));
}
這里先判斷了引數 是否空值
然后執行了LocalDateTime.ofInstant(instant, ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()))
這里可拆分兩部分:
1、ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault())
2、LocalDateTime.ofInstant(instant, zoneId)
ObjectUtil.defaultIfNull(zoneId, ZoneId.systemDefault()) 的原始碼
public static <T> T defaultIfNull(final T object, final T defaultValue) {
return (null != object) ? object : defaultValue;
}
這個很好理解,判斷值是否為null ,如果是回傳默認值,如果不是,原值回傳,
**LocalDateTime.ofInstant(instant, zoneId) ** 是jdk8自帶的源生方法
方法明細-of(java.time.Instant, java.util.TimeZone)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(java.time.Instant, java.util.TimeZone)
方法描述
{@link Instant}轉{@link LocalDateTime}
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| Instant instant |
instant {@link Instant}
|
| TimeZone timeZone |
timeZone 時區
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-21T11:23:56";
// 通過轉換獲取的Instant為UTC時間
Instant instant1 = DateUtil.parse(dateStr).toInstant();
LocalDateTime localDateTime = LocalDateTimeUtil.of(instant1,TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
Assert.assertEquals("2021-05-21T19:23:56", localDateTime.toString());
System.out.println(localDateTime);

原始碼決議:
public static LocalDateTime of(Instant instant, TimeZone timeZone) {
if (null == instant) {
return null;
}
return of(instant, ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault()).toZoneId());
}
這里先判斷了引數 是否空值
然后執行了LocalDateTime.ofInstant(timeZone, zoneId)
這里可拆分兩部分:
1、ObjectUtil.defaultIfNull(timeZone, ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault()).toZoneId())
2、LocalDateTime.ofInstant(timeZone, zoneId)
ObjectUtil.defaultIfNull(timeZone, ObjectUtil.defaultIfNull(timeZone, TimeZone.getDefault()).toZoneId()) 的原始碼
public static <T> T defaultIfNull(final T object, final T defaultValue) {
return (null != object) ? object : defaultValue;
}
這個很好理解,判斷值是否為null ,如果是回傳默認值,如果不是,原值回傳,
**LocalDateTime.ofInstant(instant, zoneId) ** 是jdk8自帶的源生方法
方法明細-of(long)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(long)
方法描述
毫秒轉{@link LocalDateTime},使用默認時區
注意:此方法使用默認時區,如果非UTC,會產生時間偏移
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| long epochMilli |
epochMilli 從1970-01-01T00:00:00Z開始計數的毫秒數
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-22 10:23:56";
Long time = DateUtil.parse(dateStr).getTime();
// 使用默認時區
LocalDateTime localDateTime = LocalDateTimeUtil.of(time);
Assert.assertEquals("2021-05-22T10:23:56", localDateTime.toString());
System.out.println(localDateTime);

原始碼決議:
public static LocalDateTime of(long epochMilli) {
return of(Instant.ofEpochMilli(epochMilli));
}
這是把long轉成Instant
public static Instant ofEpochMilli(long epochMilli) {
long secs = Math.floorDiv(epochMilli, 1000);
int mos = (int)Math.floorMod(epochMilli, 1000);
return create(secs, mos * 1000_000);
}
然后再呼叫of(Instant)
public static LocalDateTime of(Instant instant) {
return of(instant, ZoneId.systemDefault());
}
方法明細-ofUTC(long)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.ofUTC(long)
方法描述
毫秒轉{@link LocalDateTime},使用UTC時區
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| long epochMilli |
epochMilli 從1970-01-01T00:00:00Z開始計數的毫秒數
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-22T10:23:56";
Long time = DateUtil.parse(dateStr).getTime();
// 使用UTC時區
LocalDateTime localDateTime = LocalDateTimeUtil.ofUTC(time);
Assert.assertEquals("2021-05-22T10:23:56", localDateTime.toString());
System.out.println(localDateTime);

原始碼決議:
public static LocalDateTime ofUTC(long epochMilli) {
return ofUTC(Instant.ofEpochMilli(epochMilli));
}
這是把long轉成Instant
public static Instant ofEpochMilli(long epochMilli) {
long secs = Math.floorDiv(epochMilli, 1000);
int mos = (int)Math.floorMod(epochMilli, 1000);
return create(secs, mos * 1000_000);
}
然后再呼叫ofUTC(Instant)
public static LocalDateTime ofUTC(Instant instant) {
return of(instant, ZoneId.of("UTC"));
}
方法明細-of(long, java.time.ZoneId)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(long, java.time.ZoneId)
方法描述
毫秒轉{@link LocalDateTime},根據時區不同,結果會產生時間偏移
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| long epochMilli |
epochMilli 從1970-01-01T00:00:00Z開始計數的毫秒數
|
| ZoneId zoneId |
zoneId 時區
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-22T10:23:56";
Long time = DateUtil.parse(dateStr).getTime();
LocalDateTime localDateTime = LocalDateTimeUtil.of(time,ZoneId.of("Asia/Shanghai"));
Assert.assertEquals("2021-05-22T18:23:56", localDateTime.toString());
原始碼決議:
/**
* 毫秒轉{@link LocalDateTime},根據時區不同,結果會產生時間偏移
*
* @param epochMilli 從1970-01-01T00:00:00Z開始計數的毫秒數
* @param zoneId 時區
* @return {@link LocalDateTime}
*/
public static LocalDateTime of(long epochMilli, ZoneId zoneId) {
return of(Instant.ofEpochMilli(epochMilli), zoneId);
}
這是把long轉成Instant
public static Instant ofEpochMilli(long epochMilli) {
long secs = Math.floorDiv(epochMilli, 1000);
int mos = (int)Math.floorMod(epochMilli, 1000);
return create(secs, mos * 1000_000);
}
然后再呼叫of(Instant, zoneId)
上面的方法已經分析多次,就不再重復水字數,
方法明細-of(long, java.util.TimeZone)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(long, java.util.TimeZone)
方法描述
毫秒轉{@link LocalDateTime},結果會產生時間偏移
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| long epochMilli |
epochMilli 從1970-01-01T00:00:00Z開始計數的毫秒數
|
| TimeZone timeZone |
timeZone 時區
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-22T10:23:56";
Long time = DateUtil.parse(dateStr).getTime();
LocalDateTime localDateTime = LocalDateTimeUtil.of(time, TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
Assert.assertEquals("2021-05-22T18:23:56", localDateTime.toString());
原始碼決議:
public static LocalDateTime of(long epochMilli, TimeZone timeZone) {
return of(Instant.ofEpochMilli(epochMilli), timeZone);
}
這是把long轉成Instant
public static Instant ofEpochMilli(long epochMilli) {
long secs = Math.floorDiv(epochMilli, 1000);
int mos = (int)Math.floorMod(epochMilli, 1000);
return create(secs, mos * 1000_000);
}
然后再呼叫of(Instant, timeZone)
上面的方法已經分析多次,就不再重復水字數,
方法明細-of(java.util.Date)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(java.util.Date)
方法描述
{@link Date}轉{@link LocalDateTime},使用默認時區
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| Date date |
date Date物件
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-22 10:23:56";
DateTime date = DateUtil.parse(dateStr);
//使用默認時區
LocalDateTime localDateTime = LocalDateTimeUtil.of(date);
Assert.assertEquals("2021-05-22T10:23:56", localDateTime.toString());
原始碼決議:
public static LocalDateTime of(Date date) {
if (null == date) {
return null;
}
if (date instanceof DateTime) {
return of(date.toInstant(), ((DateTime) date).getZoneId());
}
return of(date.toInstant());
}
此方法是把Date 強轉為LocalDateTime
好習慣,先判斷引數date是否為空
if (date instanceof DateTime) {
return of(date.toInstant(), ((DateTime) date).getZoneId());
}
這個DateTime 是hutool自己封裝的物件,繼承于Date ,封裝了一些常用的方法

如果不是前兩者的話,就呼叫of(date.Instant)
方法明細-of(java.time.temporal.TemporalAccessor)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.of(java.time.temporal.TemporalAccessor)
方法描述
{@link TemporalAccessor}轉{@link LocalDateTime},使用默認時區
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| TemporalAccessor temporalAccessor |
temporalAccessor {@link TemporalAccessor}
|
回傳值:
{@link LocalDateTime}
參考案例:
String dateStr = "2021-05-22T10:23:56";
//使用默認時區
TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_DATE_TIME.parse(dateStr);
LocalDateTime localDateTime = LocalDateTimeUtil.of(temporalAccessor);
Assert.assertEquals("2021-05-22T10:23:56", localDateTime.toString());
原始碼決議:
public static LocalDateTime of(TemporalAccessor temporalAccessor) {
if (null == temporalAccessor) {
return null;
}
if(temporalAccessor instanceof LocalDate){
return ((LocalDate)temporalAccessor).atStartOfDay();
}
return LocalDateTime.of(
TemporalAccessorUtil.get(temporalAccessor, ChronoField.YEAR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.MONTH_OF_YEAR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.DAY_OF_MONTH),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.HOUR_OF_DAY),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.MINUTE_OF_HOUR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.SECOND_OF_MINUTE),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.NANO_OF_SECOND)
);
}
因為入參TemporalAccessor time的實作類常用的有如下幾個(java8提供的):
- LocalDateTime
- LocalDate
- LocalTime
好習慣,先判斷引數temporalAccessor是否為空
然后判斷temporalAccessor是否為LocalDate物件,如果是則呼叫LocalDate.atStartOfDay(),回傳值是localDate+‘00:00’
//LocalDate
public LocalDateTime atStartOfDay() {
return LocalDateTime.of(this, LocalTime.MIDNIGHT);
}
/**
* The time of midnight at the start of the day, '00:00'.
*/
public static final LocalTime MIDNIGHT;
public static LocalDateTime of(LocalDate date, LocalTime time) {
Objects.requireNonNull(date, "date");
Objects.requireNonNull(time, "time");
return new LocalDateTime(date, time);
}
最后通過LocalDateTime.of 方法獲取LocalDateTime物件的值,
但是博主發現一個問題,LocalTime 是沒有年月日的,那怎么轉化為LocalDateTime ,我們來寫個demo看看效果
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTimeUtil.of(localTime);
System.out.println(localDateTime);

居然沒有報錯,這是為什么呢
return LocalDateTime.of(
TemporalAccessorUtil.get(temporalAccessor, ChronoField.YEAR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.MONTH_OF_YEAR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.DAY_OF_MONTH),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.HOUR_OF_DAY),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.MINUTE_OF_HOUR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.SECOND_OF_MINUTE),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.NANO_OF_SECOND)
);
這里也是hutool自己封裝的方法,都是呼叫**public static int get(TemporalAccessor temporalAccessor, TemporalField field) ** 原始碼如下
public static int get(TemporalAccessor temporalAccessor, TemporalField field) {
if (temporalAccessor.isSupported(field)) {
return temporalAccessor.get(field);
}
return (int)field.range().getMinimum();
}
這個代碼很好理解,就是取temporalAccessor 物件對應的屬性值,如果不存在,則取這個屬性值的最小值,
斷點看效果:
1、localtime是不存在year屬性的

2、取這個欄位的最小值,

其他欄位獲取方式也差不多,
方法明細-ofDate(java.time.temporal.TemporalAccessor)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.ofDate(java.time.temporal.TemporalAccessor)
方法描述
{@link TemporalAccessor}轉{@link LocalDate},使用默認時區
支持版本及以上
5.3.10
引數描述:
| 引數名 | 描述 |
|---|---|
| TemporalAccessor temporalAccessor |
temporalAccessor {@link TemporalAccessor}
|
回傳值:
{@link LocalDate}
參考案例:
String dateStr = "2021-05-22T10:23:56";
//使用默認時區
TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_DATE_TIME.parse(dateStr);
LocalDate localDate = LocalDateTimeUtil.ofDate(temporalAccessor);
Assert.assertEquals("2021-05-22", localDate.toString());
原始碼決議:
public static LocalDate ofDate(TemporalAccessor temporalAccessor) {
if (null == temporalAccessor) {
return null;
}
if(temporalAccessor instanceof LocalDateTime){
return ((LocalDateTime)temporalAccessor).toLocalDate();
}
return LocalDate.of(
TemporalAccessorUtil.get(temporalAccessor, ChronoField.YEAR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.MONTH_OF_YEAR),
TemporalAccessorUtil.get(temporalAccessor, ChronoField.DAY_OF_MONTH)
);
}
因為入參TemporalAccessor time的實作類常用的有如下幾個(java8提供的):
- LocalDateTime
- LocalDate
- LocalTime
好習慣,先判斷引數temporalAccessor是否為空
然后判斷temporalAccessor是否為LocalDateTime物件,如果是則呼叫LocalDateTime.toLocalDate(),回傳值是localDate,因為LocalDateTime=localDate+LocalTime
最后通過LocalDate.of 方法獲取LocalDate物件的值,
方法明細-parse(java.lang.CharSequence)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.parse(java.lang.CharSequence)
方法描述
決議日期時間字串為{@link LocalDateTime},僅支持yyyy-MM-dd’T’HH:mm:ss格式,例如:2007-12-03T10:15:30
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| CharSequence text |
text 日期時間字串
|
回傳值:
{@link LocalDateTime}
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
Assert.assertEquals("2020-01-23T12:23:56", localDateTime.toString());

原始碼決議:
/**
* 決議日期時間字串為{@link LocalDateTime},僅支持yyyy-MM-dd'T'HH:mm:ss格式,例如:2007-12-03T10:15:30
*
* @param text 日期時間字串
* @return {@link LocalDateTime}
*/
public static LocalDateTime parse(CharSequence text) {
return parse(text, (DateTimeFormatter)null);
}
請看下面的原始碼分析,
方法明細-parse(java.lang.CharSequence, java.time.format.DateTimeFormatter)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.parse(java.lang.CharSequence, java.time.format.DateTimeFormatter)
方法描述
決議日期時間字串為{@link LocalDateTime},格式支持日期時間、日期、時間
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| CharSequence text |
text 日期時間字串 當formatter為null時,字串要符合格式2020-01-23T12:23:56
|
| DateTimeFormatter formatter |
formatter 日期格式化器,預定義的格式見:{@link DateTimeFormatter}
|
回傳值:
{@link LocalDateTime}
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56", DateTimeFormatter.ISO_DATE_TIME);
Assert.assertEquals("2020-01-23T12:23:56", localDateTime.toString());
System.out.println(localDateTime);

原始碼決議:
public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
if (null == text) {
return null;
}
if (null == formatter) {
return LocalDateTime.parse(text);
}
return of(formatter.parse(text));
}
如果有同學對CharSequence 物件陌生的話,那對String 應該不會陌生,String 是CharSequence 的實作介面
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
...
}
DateTimeFormatter 是jdk8提供的日期時間格式化器,用來替換我們的老朋友 simpledateformat ,
好習慣,先判斷引數CharSequence是否為空
然后再判斷引數DateTimeFormatter 是否為空,如果為空,則直接呼叫LocalDateTime.parse(text)
來看看原始碼
public static LocalDateTime parse(CharSequence text) {
return parse(text, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
這里用的日期格式化字串要像這種格式的:
such as ‘2011-12-03T10:15:30’
那如果傳入的CharSequence引數不是這種格式的字串,會是什么結果
//符合格式2020-01-23T12:23:56
LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56", DateTimeFormatter.ISO_DATE_TIME);
Assert.assertEquals("2020-01-23T12:23:56", localDateTime.toString());
System.out.println(localDateTime);
//不符合格式的
DateTimeFormatter dateTimeFormatter = null;
localDateTime = LocalDateTimeUtil.parse("2020-01-23", dateTimeFormatter);
System.out.println(localDateTime);
執行結果,在預料之中,直接報錯,這里是個坑,大家要注意
java.time.format.DateTimeParseException: Text ‘2020-01-23’ could not be parsed at index 10

最后呼叫of(formatter.parse(text))
formatter.parse(text) 回傳結果是TemporalAccessor,不一定是我們想要的LocalDateTime ,所以還要再通過of轉一下
formatter.parse(text) 是原生JDK8自帶的方法,
方法明細-parse(java.lang.CharSequence, java.lang.String)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.parse(java.lang.CharSequence, java.lang.String)
方法描述
決議日期時間字串為{@link LocalDateTime}
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| CharSequence text |
text 日期時間字串
|
| String format |
format 日期格式,類似于yyyy-MM-dd HH:mm:ss,SSS
|
回傳值:
{@link LocalDateTime}
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23", DatePattern.NORM_DATE_PATTERN);
Assert.assertEquals("2020-01-23T00:00", localDateTime.toString());
原始碼決議:
public static LocalDateTime parse(CharSequence text, String format) {
if (null == text) {
return null;
}
DateTimeFormatter formatter = null;
if(StrUtil.isNotBlank(format)){
// 修復yyyyMMddHHmmssSSS格式不能決議的問題
// fix issue#1082
//see https://stackoverflow.com/questions/22588051/is-java-time-failing-to-parse-fraction-of-second
// jdk8 bug at: https://bugs.openjdk.java.net/browse/JDK-8031085
if(StrUtil.startWithIgnoreEquals(format, DatePattern.PURE_DATETIME_PATTERN)){
final String fraction = StrUtil.removePrefix(format, DatePattern.PURE_DATETIME_PATTERN);
if(ReUtil.isMatch("[S]{1,2}", fraction)){
//將yyyyMMddHHmmssS、yyyyMMddHHmmssSS的日期統一替換為yyyyMMddHHmmssSSS格式,用0補
text += StrUtil.repeat('0', 3-fraction.length());
}
formatter = new DateTimeFormatterBuilder()
.appendPattern(DatePattern.PURE_DATETIME_PATTERN)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
} else{
formatter = DateTimeFormatter.ofPattern(format);
}
}
return parse(text, formatter);
}
養成好習慣,先判斷引數CharSequence和format是否為空
這邊針對jdk8的一個bug進行了兼容處理
1、在正常配置按照標準格式的字串日期,是能夠正常轉換的,如果月,日,時,分,秒在不足兩位的情況需要補0,否則的話會轉換失敗,拋出例外,
DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
LocalDateTime dt1 = LocalDateTime.parse("2021-7-20 23:46:43.946", DATE_TIME_FORMATTER);
System.out.println(dt1);
會報錯:

java.time.format.DateTimeParseException: Text '2021-7-20 23:46:43.946' could not be parsed at index 5
分析原因:是格式字串與實際的時間不匹配
“yyyy-MM-dd HH:mm:ss.SSS”
“2021-7-20 23:46:43.946”
中間的月份格式是MM,實際時間是7
解決方案:保持格式字串與實際的時間匹配
DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
LocalDateTime dt1 = LocalDateTime.parse("2021-07-20 23:46:43.946", DATE_TIME_FORMATTER);
System.out.println(dt1);

方法明細-parseDate(java.lang.CharSequence)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.parseDate(java.lang.CharSequence)
方法描述
決議日期時間字串為{@link LocalDate},僅支持yyyy-MM-dd’T’HH:mm:ss格式,例如:2007-12-03T10:15:30
支持版本及以上
5.3.10
引數描述:
| 引數名 | 描述 |
|---|---|
| CharSequence text |
text 日期時間字串
|
回傳值:
{@link LocalDate}
參考案例:
LocalDate localDate = LocalDateTimeUtil.parseDate("2020-01-23"); Assert.assertEquals("2020-01-23", localDate.toString());
原始碼決議:
/**
* 決議日期時間字串為{@link LocalDate},僅支持yyyy-MM-dd'T'HH:mm:ss格式,例如:2007-12-03T10:15:30
*
* @param text 日期時間字串
* @return {@link LocalDate}
* @since 5.3.10
*/
public static LocalDate parseDate(CharSequence text) {
return parseDate(text, (DateTimeFormatter)null);
}
請看下面的原始碼分析,
方法明細-parseDate(java.lang.CharSequence, java.time.format.DateTimeFormatter)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.parseDate(java.lang.CharSequence, java.time.format.DateTimeFormatter)
方法描述
決議日期時間字串為{@link LocalDate},格式支持日期
支持版本及以上
5.3.10
引數描述:
| 引數名 | 描述 |
|---|---|
| CharSequence text |
text 日期時間字串
|
| DateTimeFormatter formatter |
formatter 日期格式化器,預定義的格式見:{@link DateTimeFormatter}
|
回傳值:
{@link LocalDate}
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("12:23:56", DatePattern.NORM_TIME_PATTERN);
Assert.assertEquals("12:23:56", localDateTime.toLocalTime().toString());
原始碼決議:
public static LocalDate parseDate(CharSequence text, DateTimeFormatter formatter) {
if (null == text) {
return null;
}
if (null == formatter) {
return LocalDate.parse(text);
}
return ofDate(formatter.parse(text));
}
如果有同學對CharSequence 物件陌生的話,那對String 應該不會陌生,String 是CharSequence 的實作介面
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
...
}
DateTimeFormatter 是jdk8提供的日期時間格式化器,用來替換我們的老朋友 simpledateformat ,
好習慣,先判斷引數CharSequence是否為空
然后再判斷引數DateTimeFormatter 是否為空,如果為空,則直接呼叫LocalDate.parse(text)
來看看原始碼
public static LocalDate parse(CharSequence text) {
return parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
}
這里用的日期格式化字串要像這種格式的:
such as ‘2011-12-03’
最后呼叫of(formatter.parse(text))
formatter.parse(text) 回傳結果是TemporalAccessor,不一定是我們想要的LocalDate ,所以還要再通過of轉一下
formatter.parse(text) 是原生JDK8自帶的方法,
方法明細-parseDate(java.lang.CharSequence, java.lang.String)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.parseDate(java.lang.CharSequence, java.lang.String)
方法描述
決議日期字串為{@link LocalDate}
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| CharSequence text |
text 日期字串
|
| String format |
format 日期格式,類似于yyyy-MM-dd
|
回傳值:
{@link LocalDateTime}
參考案例:
//第一個引數和第二個引數格式保持一致
LocalDate localDate = LocalDateTimeUtil.parseDate("2020-01-23 12:23:56","yyyy-MM-dd hh:mm:ss");
Assert.assertEquals("2020-01-23", localDate.toString());
localDate = LocalDateTimeUtil.parseDate("2020/01/23 12:23:56","yyyy/MM/dd hh:mm:ss");
Assert.assertEquals("2020-01-23", localDate.toString());
原始碼決議:
public static LocalDate parseDate(CharSequence text, String format) {
if (null == text) {
return null;
}
return parseDate(text, DateTimeFormatter.ofPattern(format));
}
請看上面的原始碼分析,
方法明細-formatNormal(java.time.LocalDateTime)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.formatNormal(java.time.LocalDateTime)
方法描述
格式化日期時間為yyyy-MM-dd HH:mm:ss格式
支持版本及以上
5.3.11
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime time |
time {@link LocalDateTime}
|
回傳值:
格式化后的字串
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
String format = LocalDateTimeUtil.formatNormal(localDateTime);
Assert.assertEquals("2020-01-23 12:23:56", format);
原始碼決議:
/**
* 格式化日期時間為yyyy-MM-dd HH:mm:ss格式
*
* @param time {@link LocalDateTime}
* @return 格式化后的字串
* @since 5.3.11
*/
public static String formatNormal(LocalDateTime time) {
return format(time, DatePattern.NORM_DATETIME_FORMATTER);
}
//------------------------------
public static String format(LocalDateTime time, DateTimeFormatter formatter) {
return TemporalAccessorUtil.format(time, formatter);
}
TemporalAccessorUtil 類是hutool封裝的工具類,看下面的原始碼,也是比較好理解的,
//TemporalAccessorUtil
/**
* 格式化日期時間為指定格式
*
* @param time {@link TemporalAccessor}
* @param formatter 日期格式化器,預定義的格式見:{@link DateTimeFormatter}
* @return 格式化后的字串
* @since 5.3.10
*/
public static String format(TemporalAccessor time, DateTimeFormatter formatter) {
if (null == time) {
return null;
}
if(null == formatter){
formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
}
try {
return formatter.format(time);
} catch (UnsupportedTemporalTypeException e){
if(time instanceof LocalDate && e.getMessage().contains("HourOfDay")){
// 用戶傳入LocalDate,但是要求格式化帶有時間部分,轉換為LocalDateTime重試
return formatter.format(((LocalDate) time).atStartOfDay());
}else if(time instanceof LocalTime && e.getMessage().contains("YearOfEra")){
// 用戶傳入LocalTime,但是要求格式化帶有日期部分,轉換為LocalDateTime重試
return formatter.format(((LocalTime) time).atDate(LocalDate.now()));
}
throw e;
}
}
養成好習慣,先判斷引數TemporalAccessor 和DateTimeFormatter 是否為空
如果DateTimeFormatter 為空,則給默認值DateTimeFormatter.ISO_LOCAL_DATE_TIME
such as ‘2011-12-03T10:15:30’
然后呼叫格式化方法formatter.format(time) 如果time 不是LocalDateTime 物件,則會報錯,然后在catch里做了兼容處理,對LocalDate 和 LocalTime 物件可以進行格式化處理,其他的直接回傳例外,
方法明細-format(java.time.LocalDateTime, java.time.format.DateTimeFormatter)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDateTime, java.time.format.DateTimeFormatter)
方法描述
格式化日期時間為指定格式
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime time |
time {@link LocalDateTime}
|
| DateTimeFormatter formatter |
formatter 日期格式化器,預定義的格式見:{@link DateTimeFormatter}
|
回傳值:
格式化后的字串
參考案例:
LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
String format = LocalDateTimeUtil.format(localDateTime, DateTimeFormatter.ISO_DATE_TIME);
Assert.assertEquals("2020-01-23T12:23:56", format);
原始碼決議:
/**
* 格式化日期時間為指定格式
*
* @param time {@link LocalDateTime}
* @param formatter 日期格式化器,預定義的格式見:{@link DateTimeFormatter}
* @return 格式化后的字串
*/
public static String format(LocalDateTime time, DateTimeFormatter formatter) {
return TemporalAccessorUtil.format(time, formatter);
}
請看上面的原始碼分析,
方法明細-format(java.time.LocalDateTime, java.lang.String)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDateTime, java.lang.String)
方法描述
格式化日期時間為指定格式
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime time |
time {@link LocalDateTime}
|
| String format |
format 日期格式,類似于yyyy-MM-dd HH:mm:ss,SSS
|
回傳值:
格式化后的字串
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
String format = LocalDateTimeUtil.format(localDateTime, DatePattern.NORM_DATETIME_PATTERN);
Assert.assertEquals("2020-01-23 12:23:56", format);
原始碼決議:
public static String format(LocalDateTime time, String format) {
if (null == time) {
return null;
}
return format(time, DateTimeFormatter.ofPattern(format));
}
DateTimeFormatter.ofPattern(format) 執行完會回傳DateTimeFormatter
然后會呼叫 format(time, DateTimeFormatter 方法
請看上面的原始碼分析,
方法明細-formatNormal(java.time.LocalDate)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.formatNormal(java.time.LocalDate)
方法描述
格式化日期時間為yyyy-MM-dd格式
支持版本及以上
5.3.11
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDate date |
date {@link LocalDate}
|
回傳值:
格式化后的字串
參考案例:
final LocalDate date = LocalDate.parse("2020-01-23");
String format = LocalDateTimeUtil.format(date, DatePattern.NORM_DATE_PATTERN);
Assert.assertEquals("2020-01-23", format);
format = LocalDateTimeUtil.formatNormal(date);
Assert.assertEquals("2020-01-23", format);
原始碼決議:
public static String formatNormal(LocalDate date) {
return format(date, DatePattern.NORM_DATE_FORMATTER);
}
請看上面的原始碼分析,
方法明細-format(java.time.LocalDate, java.time.format.DateTimeFormatter)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDate, java.time.format.DateTimeFormatter)
方法描述
格式化日期時間為指定格式
支持版本及以上
5.3.10
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDate date |
date {@link LocalDate}
|
| DateTimeFormatter formatter |
formatter 日期格式化器,預定義的格式見:{@link DateTimeFormatter}
|
回傳值:
格式化后的字串
參考案例:
final LocalDate date = LocalDate.parse("2021-05-22");
String format = LocalDateTimeUtil.format(date, DateTimeFormatter.ISO_DATE);
Assert.assertEquals("2021-05-22", format);
原始碼決議:
public static String format(LocalDate date, DateTimeFormatter formatter) {
return TemporalAccessorUtil.format(date, formatter);
}
請看上面的原始碼分析,
方法明細-format(java.time.LocalDate, java.lang.String)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.format(java.time.LocalDate, java.lang.String)
方法描述
格式化日期時間為指定格式
支持版本及以上
5.3.10
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDate date |
date {@link LocalDate}
|
| String format |
format 日期格式,類似于yyyy-MM-dd
|
回傳值:
格式化后的字串
參考案例:
final LocalDate date = LocalDate.parse("2020-01-23");
String format = LocalDateTimeUtil.format(date, DatePattern.NORM_DATE_PATTERN);
Assert.assertEquals("2020-01-23", format);
原始碼決議:
public static String format(LocalDate date, String format) {
if (null == date) {
return null;
}
return format(date, DateTimeFormatter.ofPattern(format));
}
請看上面的原始碼分析,
方法明細-offset(java.time.LocalDateTime, long, java.time.temporal.TemporalUnit)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.offset(java.time.LocalDateTime, long, java.time.temporal.TemporalUnit)
方法描述
日期偏移,根據field不同加不同值(偏移會修改傳入的物件)
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime time |
time {@link LocalDateTime}
|
| long number |
number 偏移量,正數為向后偏移,負數為向前偏移
|
| TemporalUnit field |
field 偏移單位,見{@link ChronoUnit},不能為null
|
回傳值:
偏移后的日期時間
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
LocalDateTime offset = LocalDateTimeUtil.offset(localDateTime, 1, ChronoUnit.DAYS);
// 非同一物件
Assert.assertNotSame(localDateTime, offset);
Assert.assertEquals("2020-01-24T12:23:56", offset.toString());
原始碼決議:
/**
* 日期偏移,根據field不同加不同值(偏移會修改傳入的物件)
*
* @param time {@link LocalDateTime}
* @param number 偏移量,正數為向后偏移,負數為向前偏移
* @param field 偏移單位,見{@link ChronoUnit},不能為null
* @return 偏移后的日期時間
*/
public static LocalDateTime offset(LocalDateTime time, long number, TemporalUnit field) {
if (null == time) {
return null;
}
return time.plus(number, field);
}
time.plus(number, field) 是jdk8提供的方法
LocalDateTime localDateTime = LocalDateTime.of(2021, 8, 30, 23, 14, 20);
LocalDateTime offset = localDateTime.plus(1, ChronoUnit.DAYS);
// 非同一物件
Assert.assertNotSame(localDateTime, offset);
System.out.println(offset);

方法明細-between(java.time.LocalDateTime, java.time.LocalDateTime)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.between(java.time.LocalDateTime, java.time.LocalDateTime)
方法描述
獲取兩個日期的差,如果結束時間早于開始時間,獲取結果為負,
回傳結果為{@link Duration}物件,通過呼叫toXXX方法回傳相差單位 ## 支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime startTimeInclude |
startTimeInclude 開始時間(包含)
|
| LocalDateTime endTimeExclude |
endTimeExclude 結束時間(不包含)
|
回傳值:
時間差 {@link Duration}物件
參考案例:
final Duration between = LocalDateTimeUtil.between(
LocalDateTimeUtil.parse("2019-02-02T00:00:00"),
LocalDateTimeUtil.parse("2020-02-02T00:00:00"));
Assert.assertEquals(365, between.toDays());
原始碼決議:
public static Duration between(LocalDateTime startTimeInclude, LocalDateTime endTimeExclude) {
return TemporalUtil.between(startTimeInclude, endTimeExclude);
}
其中TemporalUtil 是hutool封裝的工具類,原始碼如下:
/**
* 獲取兩個日期的差,如果結束時間早于開始時間,獲取結果為負,
* <p>
* 回傳結果為{@link Duration}物件,通過呼叫toXXX方法回傳相差單位
*
* @param startTimeInclude 開始時間(包含)
* @param endTimeExclude 結束時間(不包含)
* @return 時間差 {@link Duration}物件
*/
public static Duration between(Temporal startTimeInclude, Temporal endTimeExclude) {
return Duration.between(startTimeInclude, endTimeExclude);
}
Duration 適合處理較短的時間,需要更高的精確性,我們能使用between()方法比較兩個瞬間的差,
Duration.between 是JDK8提供的方法,
方法明細-between(java.time.LocalDateTime, java.time.LocalDateTime, java.time.temporal.ChronoUnit)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.between(java.time.LocalDateTime, java.time.LocalDateTime, java.time.temporal.ChronoUnit)
方法描述
獲取兩個日期的差,如果結束時間早于開始時間,獲取結果為負,
回傳結果為時間差的long值
支持版本及以上
5.4.5
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime startTimeInclude |
startTimeInclude 開始時間(包括)
|
| LocalDateTime endTimeExclude |
endTimeExclude 結束時間(不包括)
|
| ChronoUnit unit |
unit 時間差單位
|
回傳值:
時間差
參考案例:
final long betweenWeek = DateUtil.betweenWeek(
DateUtil.parse("2020-11-21"),
DateUtil.parse("2020-11-23"), false);
final long betweenWeek2 = LocalDateTimeUtil.between(
LocalDateTimeUtil.parse("2020-11-21", "yyy-MM-dd"),
LocalDateTimeUtil.parse("2020-11-23", "yyy-MM-dd"),
ChronoUnit.WEEKS);
Assert.assertEquals(betweenWeek, betweenWeek2);
原始碼決議:
public static long between(LocalDateTime startTimeInclude, LocalDateTime endTimeExclude, ChronoUnit unit) {
return TemporalUtil.between(startTimeInclude, endTimeExclude, unit);
}
多了一個時間單位的選項
public static long between(Temporal startTimeInclude, Temporal endTimeExclude, ChronoUnit unit) {
return unit.between(startTimeInclude, endTimeExclude);
}
這個回傳的資料為0

這個回傳的資料為1

這兩個demo的資料,反應出了結果是按時間差單位來產生的,
方法明細-betweenPeriod(java.time.LocalDate, java.time.LocalDate)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.betweenPeriod(java.time.LocalDate, java.time.LocalDate)
方法描述
獲取兩個日期的表象時間差,如果結束時間早于開始時間,獲取結果為負,
比如2011年2月1日,和2021年8月11日,日相差了10天,月相差6月
支持版本及以上
5.4.5
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDate startTimeInclude |
startTimeInclude 開始時間(包括)
|
| LocalDate endTimeExclude |
endTimeExclude 結束時間(不包括)
|
回傳值:
時間差
參考案例:
final LocalDate localDate1= LocalDate.parse("2021-05-22");
final LocalDate localDate2= LocalDate.parse("2021-06-23");
Period period = LocalDateTimeUtil.betweenPeriod(localDate1,localDate2);
Assert.assertEquals(1, period.getMonths());
Assert.assertEquals(1, period.getDays());
period = LocalDateTimeUtil.betweenPeriod(localDate2,localDate1);
Assert.assertEquals(-1, period.getMonths());
Assert.assertEquals(-1, period.getDays());
final LocalDate localDate3= LocalDate.parse("2021-06-22");
period = LocalDateTimeUtil.betweenPeriod(localDate1,localDate3);
Assert.assertEquals(1, period.getMonths());
Assert.assertEquals(0, period.getDays());
原始碼決議:
public static Period betweenPeriod(LocalDate startTimeInclude, LocalDate endTimeExclude) {
return Period.between(startTimeInclude, endTimeExclude);
}
Period.between 是JDK8提供的方法
Period 是ChronoPeriod 的實作類,類里包含兩個變數years ,months 和 days ,所以Period 是由年,月和日組成的時間量,
LocalDate first = LocalDate.of(2021, 8, 29);
LocalDate second = LocalDate.of(2022, 9, 30);
Period period = Period.between(first, second);
System.out.println(period);

方法明細-beginOfDay(java.time.LocalDateTime)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.beginOfDay(java.time.LocalDateTime)
方法描述
修改為一天的開始時間,例如:2020-02-02 00:00:00,000
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime time |
time 日期時間
|
回傳值:
一天的開始時間
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
final LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(localDateTime);
Assert.assertEquals("2020-01-23T00:00", beginOfDay.toString());
原始碼決議:
/**
* 修改為一天的開始時間,例如:2020-02-02 00:00:00,000
*
* @param time 日期時間
* @return 一天的開始時間
*/
public static LocalDateTime beginOfDay(LocalDateTime time) {
return time.with(LocalTime.MIN);
}
LocalDateTime.with 是jdk8提供的方法,
日期時間的加減法及修改
LocalDateTime currentTime = LocalDateTime.now(); // 當前日期和時間
System.out.println("------------------時間的加減法及修改-----------------------");
//3.LocalDateTime的加減法包含了LocalDate和LocalTime的所有加減,上面說過,這里就只做簡單介紹
System.out.println("3.當前時間:" + currentTime);
System.out.println("3.當前時間加5年:" + currentTime.plusYears(5));
System.out.println("3.當前時間加2個月:" + currentTime.plusMonths(2));
System.out.println("3.當前時間減2天:" + currentTime.minusDays(2));
System.out.println("3.當前時間減5個小時:" + currentTime.minusHours(5));
System.out.println("3.當前時間加5分鐘:" + currentTime.plusMinutes(5));
System.out.println("3.當前時間加20秒:" + currentTime.plusSeconds(20));
//還可以靈活運用比如:向后加一年,向前減一天,向后加2個小時,向前減5分鐘,可以進行連寫
System.out.println("3.同時修改(向后加一年,向前減一天,向后加2個小時,向前減5分鐘):" + currentTime.plusYears(1).minusDays(1).plusHours(2).minusMinutes(5));
System.out.println("3.修改年為2025年:" + currentTime.withYear(2025));
System.out.println("3.修改月為12月:" + currentTime.withMonth(12));
System.out.println("3.修改日為27日:" + currentTime.withDayOfMonth(27));
System.out.println("3.修改小時為12:" + currentTime.withHour(12));
System.out.println("3.修改分鐘為12:" + currentTime.withMinute(12));
System.out.println("3.修改秒為12:" + currentTime.withSecond(12));

方法明細-endOfDay(java.time.LocalDateTime)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.endOfDay(java.time.LocalDateTime)
方法描述
修改為一天的結束時間,例如:2020-02-02 23:59:59,999
支持版本及以上
引數描述:
| 引數名 | 描述 |
|---|---|
| LocalDateTime time |
time 日期時間
|
回傳值:
一天的結束時間
參考案例:
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
final LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(localDateTime);
Assert.assertEquals("2020-01-23T23:59:59.999999999", endOfDay.toString());
原始碼決議:
/**
* 修改為一天的結束時間,例如:2020-02-02 23:59:59,999
*
* @param time 日期時間
* @return 一天的結束時間
*/
public static LocalDateTime endOfDay(LocalDateTime time) {
return time.with(LocalTime.MAX);
}
請看上面的原始碼分析,
方法明細-toEpochMilli(java.time.temporal.TemporalAccessor)
方法名稱:cn.hutool.core.date.LocalDateTimeUtil.toEpochMilli(java.time.temporal.TemporalAccessor)
方法描述
{@link TemporalAccessor}轉換為 時間戳(從1970-01-01T00:00:00Z開始的毫秒數)
支持版本及以上
5.4.1
引數描述:
| 引數名 | 描述 |
|---|---|
| TemporalAccessor temporalAccessor |
temporalAccessor Date物件
|
回傳值:
{@link Instant}物件
參考案例:
String dateStr = "2021-05-22";
//TemporalAccessor 的實作類包含Instant LocalDateTime ZonedDateTime OffsetDateTime LocalDate LocalTime OffsetTime
LocalDate localDate = LocalDate.parse(dateStr);
//Date物件
long time = LocalDateTimeUtil.toEpochMilli(localDate);
Assert.assertEquals(DateUtil.parse(dateStr).getTime(), time);
原始碼決議:
public static long toEpochMilli(TemporalAccessor temporalAccessor) {
return TemporalAccessorUtil.toEpochMilli(temporalAccessor);
}
TemporalAccessorUtil.toEpochMilli(temporalAccessor) 是hutool封裝的方法,
public static long toEpochMilli(TemporalAccessor temporalAccessor) {
return toInstant(temporalAccessor).toEpochMilli();
}
把temporalAccessor 物件轉化為Instant 物件,我們來看下是怎么轉化的
public static Instant toInstant(TemporalAccessor temporalAccessor) {
if (null == temporalAccessor) {
return null;
}
Instant result;
if (temporalAccessor instanceof Instant) {
result = (Instant) temporalAccessor;
} else if (temporalAccessor instanceof LocalDateTime) {
result = ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault()).toInstant();
} else if (temporalAccessor instanceof ZonedDateTime) {
result = ((ZonedDateTime) temporalAccessor).toInstant();
} else if (temporalAccessor instanceof OffsetDateTime) {
result = ((OffsetDateTime) temporalAccessor).toInstant();
} else if (temporalAccessor instanceof LocalDate) {
result = ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault()).toInstant();
} else if (temporalAccessor instanceof LocalTime) {
// 指定本地時間轉換 為Instant,取當天日期
result = ((LocalTime) temporalAccessor).atDate(LocalDate.now()).atZone(ZoneId.systemDefault()).toInstant();
} else if (temporalAccessor instanceof OffsetTime) {
// 指定本地時間轉換 為Instant,取當天日期
result = ((OffsetTime) temporalAccessor).atDate(LocalDate.now()).toInstant();
} else {
result = Instant.from(temporalAccessor);
}
return result;
}
就是對應的temporalAccessor 實作類進行處理,從這個原始碼中,我們可以學到:如何把不同的時間型別轉化為Instant 物件,
然后把Instant 物件轉化為毫秒級的long資料
Instant now = Instant.now();
System.out.println("now:"+now);
System.out.println(now.getEpochSecond()); // 秒
System.out.println(now.toEpochMilli()); // 毫秒

總結
本文的實戰+原始碼分析只是拋轉引玉,實戰讓大家能知道如何去使用JSR-310 新的日期時間API,原始碼分析可以讓大家知其所以然,
本篇文章 是JSR-310系列的一個里程碑,感謝大家的一路支持,讓博主堅持把這個系列的文章寫完,
??祝大家中秋快樂??
推薦相關文章
hutool日期時間系列文章
1DateUtil(時間工具類)-當前時間和當前時間戳
2DateUtil(時間工具類)-常用的時間型別Date,DateTime,Calendar和TemporalAccessor(LocalDateTime)轉換
3DateUtil(時間工具類)-獲取日期的各種內容
4DateUtil(時間工具類)-格式化時間
5DateUtil(時間工具類)-決議被格式化的時間
6DateUtil(時間工具類)-時間偏移量獲取
7DateUtil(時間工具類)-日期計算
8ChineseDate(農歷日期工具類)
9LocalDateTimeUtil(JDK8+中的{@link LocalDateTime} 工具類封裝)
10TemporalAccessorUtil{@link TemporalAccessor} 工具類封裝
其他
要探索JDK的核心底層原始碼,那必須掌握native用法
萬字博文教你搞懂java原始碼的日期和時間相關用法
java的SimpleDateFormat執行緒不安全出問題了,虛竹教你多種解決方案
原始碼分析:JDK獲取默認時區的風險和最佳實踐
高級JAVA開發必備技能:時區的規則發生變化時,如何同步JDK的時區規則
今天是持續寫作的第 12 / 100 天,
可以關注我,點贊我、評論我、收藏我啦,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/301515.html
標籤:java
上一篇:Java常見設計模式總結
