主頁 > 後端開發 > ??高級JAVA開發必備技能??java8 新日期時間API((五)JSR-310:實戰+原始碼分析),5萬字詳解(JAVA 小虛竹,建議收藏)

??高級JAVA開發必備技能??java8 新日期時間API((五)JSR-310:實戰+原始碼分析),5萬字詳解(JAVA 小虛竹,建議收藏)

2021-09-20 11:19:46 後端開發

??作者簡介: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());

image-20210919141925941

原始碼決議:

/**
 * 當前時間,默認時區
 *
 * @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());
    }

image-2021081495878

最終呼叫的也是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);

image-20210919143205699

原始碼決議:

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);

image-20210919144624993

原始碼決議:

	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());

image-20210919145102122

原始碼決議:

public static LocalDateTime of(ZonedDateTime zonedDateTime) {
		if (null == zonedDateTime) {
			return null;
		}
		return zonedDateTime.toLocalDateTime();
	}

這里先判斷了引數 是否空值

然后呼叫zonedDateTime.toLocalDateTime()

我們知道zonedDateTimeLocalDateTime 是可以直接轉化的

方法明細-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);

image-20210919150621318

原始碼決議:

	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);

image-20210919151333112

原始碼決議:

	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);

image-20210919152034372

原始碼決議:

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 ,封裝了一些常用的方法

image-20210919153217935

如果不是前兩者的話,就呼叫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);

image-20210919154701692

居然沒有報錯,這是為什么呢

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屬性的

image-20210919155438622

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

image-20210919155550186

其他欄位獲取方式也差不多,

方法明細-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());

image-20210919160344393

原始碼決議:

	/**
	 * 決議日期時間字串為{@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);

image-20210919160344393

原始碼決議:

	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 應該不會陌生,StringCharSequence 的實作介面

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

image-20210919161934470

最后呼叫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);

會報錯:

image-202107208183

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);

image-20210720504067

方法明細-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 應該不會陌生,StringCharSequence 的實作介面

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;
   }
}

養成好習慣,先判斷引數TemporalAccessorDateTimeFormatter 是否為空

如果DateTimeFormatter 為空,則給默認值DateTimeFormatter.ISO_LOCAL_DATE_TIME

such as ‘2011-12-03T10:15:30’

然后呼叫格式化方法formatter.format(time) 如果time 不是LocalDateTime 物件,則會報錯,然后在catch里做了兼容處理,對LocalDateLocalTime 物件可以進行格式化處理,其他的直接回傳例外,

方法明細-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);

image-20210831233938785

方法明細-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

image-20210919181335622

這個回傳的資料為1

image-20210919181709928

這兩個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提供的方法

PeriodChronoPeriod 的實作類,類里包含兩個變數years ,monthsdays ,所以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);

image-20210830224610563

方法明細-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));

image-20210714941902

方法明細-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()); // 毫秒

image-20210720905353

總結

本文的實戰+原始碼分析只是拋轉引玉,實戰讓大家能知道如何去使用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常見設計模式總結

下一篇:【小明】談談你對Spring三級快取和回圈依賴的理解【建議收藏】

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more