十五万字《JDK源码分析》之《JSR-310(java8 新日期时间API)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
img

正文

	@Test
	public void ZoneIdTest2(){
		Set<String> zoneIds = ZoneId.getAvailableZoneIds();
		System.out.println("zoneIds长度:"+zoneIds.size());
		for(String zoneId : zoneIds){
			System.out.println(zoneId);
		}
	}

image-20210810210652005

指定的字符串不能乱写,不然会报错,要在ZoneId.getAvailableZoneIds() 的集合范围里。

从日期中获取时区
System.out.println(ZoneId.from(ZonedDateTime.now()));
			
System.out.println(ZoneId.from(ZoneOffset.of("+8")));

image-202108109411428

从日期中获取时区只支持带有时区的TemporalAccessor ,像LocalDateTime,LocalDate是不可以的,会报错。

		try {
			System.out.println(ZoneId.from(LocalDateTime.now()));
		}catch (Exception e){
			e.printStackTrace();
		}
		try {
			System.out.println(ZoneId.from(LocalDate.now()));
		}catch (Exception e){
			e.printStackTrace();
		}

image-202108109721346

ZoneId是抽象类,它有两个继承实现类:

  • ZoneOffset:时区偏移量
  • ZoneRegion:地理区域

image-20210810927677

ZoneOffset(时区偏移量)

时区偏移量是时区与Greenwich/UTC之间的时间差,一般是固定的小时数和分钟数。

最小/最大偏移量
	@Test
	public void ZoneIdTest5(){
		System.out.println("最小偏移量:" + ZoneOffset.MIN);
		System.out.println("最小偏移量:" + ZoneOffset.MAX);
		System.out.println("中心偏移量:" + ZoneOffset.UTC);
		// 超出最大范围
		System.out.println(ZoneOffset.of("+100"));
	}

image-202108109251152

超出最大范围会报错

时分秒构造偏移量
	@Test
	public void ZoneIdTest6(){
		System.out.println(ZoneOffset.ofHours(10));
		System.out.println(ZoneOffset.ofHoursMinutes(10, 10));
		System.out.println(ZoneOffset.ofHoursMinutesSeconds(10, 10, 10));

		System.out.println(ZoneOffset.ofHours(-10));
	}

image-202108109621080

挺方便的,也简单好理解。偏移量可以精确到秒级。

image-202108109915102

ZoneRegion(地理区域)

ZoneRegion表示地理区域,格式是:洲(州、国家)/城市。最常见的区域分类是时区数据库(TZDB)。

which defines regions such as ‘Europe/Paris’ and ‘Asia/Tokyo’.(TZDB使用“Europe/Paris”和“Asia/Tokyo”来区分地区。)

final class ZoneRegion extends ZoneId implements Serializable {
	...
}

由源码可知,地理区域ZoneRegion是ZoneId的继承实现类。

但是我们发现这个不是对外使用的,ZoneRegion的修饰符是default(只能由同包下的类调用)。只能通过ZoneId来操作。

	@Test
	public void ZoneIdTest7(){
		ZoneId zoneId = ZoneId.systemDefault();
		System.out.println(zoneId);
	}

image-2021081199155

博主在厦门,所以默认获取的时区ID是Asia/Shanghai

ZoneId的实例是ZoneOffset或ZoneRegion

ZoneId of(String zoneId, boolean checkAvailable) 源码分析:

/\*\*
 \* Parses the ID, taking a flag to indicate whether {@code ZoneRulesException}
 \* should be thrown or not, used in deserialization.
 \*
 \* @param zoneId the time-zone ID, not null
 \* @param checkAvailable whether to check if the zone ID is available
 \* @return the zone ID, not null
 \* @throws DateTimeException if the ID format is invalid
 \* @throws ZoneRulesException if checking availability and the ID cannot be found
 \*/
    static ZoneId of(String zoneId, boolean checkAvailable) {
        Objects.requireNonNull(zoneId, "zoneId");
        if (zoneId.length() <= 1 || zoneId.startsWith("+") || zoneId.startsWith("-")) {
            return ZoneOffset.of(zoneId);
        } else if (zoneId.startsWith("UTC") || zoneId.startsWith("GMT")) {
            return ofWithPrefix(zoneId, 3, checkAvailable);
        } else if (zoneId.startsWith("UT")) {
            return ofWithPrefix(zoneId, 2, checkAvailable);
        }
        return ZoneRegion.ofId(zoneId, checkAvailable);
    }


   private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) {
        String prefix = zoneId.substring(0, prefixLength);
        if (zoneId.length() == prefixLength) {
            return ofOffset(prefix, ZoneOffset.UTC);
        }
       ...
    }

image-20210819856624

由源码可知:

  1. zoneId长度小于等于1位,或者以“+”或“-”开头的,创建的是ZoneOffset实例
  2. 以“UTC”,“UT”或“GMT”开头的,创建的是ZoneRegion实例
  3. 不符合以上两种的,创建的是ZoneRegion实例
	@Test
	public void ZoneIdTest8(){

		ZoneId zoneId1 = ZoneId.of("+8");
		ZoneId zoneId2 = ZoneId.of("+08:00");
		ZoneId zoneId3 = ZoneId.of("UT+8");
		ZoneId zoneId4 = ZoneId.of("Asia/Shanghai");
		System.out.println();
	}

image-2021081194068

(二)JSR-310:常用的日期时间API

介绍下java8API比较常用的日期时间API,按java.time 包的类顺序:

  • Clock:时钟
  • Instant:瞬间时间。
  • LocalDate:本地日期。只有表示年月日
  • LocalDateTime:本地日期时间,LocalDate+LocalTime
  • LocalTime:本地时间,只有表示时分秒
  • OffsetDateTime:有时间偏移量的日期时间(不包含基于ZoneRegion的时间偏移量)
  • OffsetTime:有时间偏移量的时间
  • ZonedDateTime:有时间偏移量的日期时间(包含基于ZoneRegion的时间偏移量)

博主把这些类都点开看了,都是属于不可变类。而且官方也说了,java.time包 下的类都是线程安全的。

Clock

Clock类说明
public abstract class Clock {
...
}

Clock 是抽象类,内部提供了四个内部类,这是它的内部实现类

image-2021081496702

  • FixedClock :始终返回相同瞬间的时钟,通常使用于测试。
  • OffsetClock :偏移时钟,时间偏移量的单位是Duration。
  • SystemClock :系统默认本地时钟。
  • TickClock :偏移时钟,时间偏移量的单位是纳秒。

Clock 提供了下面这几个常用的方法(这几个方法在实现类里都有对应的实现):

// 获取时钟的当前Instant对象。
public abstract Instant instant()

// 获取时钟的当前毫秒数值
public long millis()

// 获取用于创建时钟的时区。
public abstract ZoneId	getZone()

// 返回具有指定时区的当前时钟的新实例
public abstract Clock withZone(ZoneId zone)


FixedClock
Clock.fixed
public static Clock fixed(Instant fixedInstant, ZoneId zone)

需要传递instantzone,并将返回具有固定瞬间的时钟。

		Instant instant = Instant.now();
		Clock fixedClock = Clock.fixed(instant, ZoneId.of("Asia/Shanghai"));
		Clock fixedClock1 = Clock.fixed(instant, ZoneId.of("GMT"));
		System.out.println("中国时区的Clock:"+fixedClock);
		System.out.println("GMT时区的Clock:"+fixedClock1);

image-20210814195855581

由运行结果可知,返回的结果是有带对应时区的。

验证获取的时钟会不会改变:

		Clock clock = Clock.systemDefaultZone();
		Clock fixedClock = Clock.fixed(clock.instant(), ZoneId.of("Asia/Shanghai"));
		System.out.println(fixedClock.instant());
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(fixedClock.instant());

image-202108149044323

Clock.fixed 创建一个固定的时钟,clock 对象将始终提供与指定相同的时刻。。如图所示,强制睡眠1秒,但是时刻没变。

Clock.fixed 跟 Offset 方法更配

由上面可知Clock.fixed 得到一个固定的时钟,那要添加时间或者减去时间就要用到Offset 方法

示例代码如下

		Clock clock = Clock.systemDefaultZone();
		Clock fixedClock = Clock.fixed(clock.instant(), ZoneId.of("Asia/Shanghai"));
		System.out.println(fixedClock.instant());
		Clock clockAdd = Clock.offset(clock, Duration.ofMinutes(20));
		Clock clockSub = Clock.offset(clock, Duration.ofMinutes(-10));
		System.out.println("原先的: " + clock.instant());
		System.out.println("加了20分钟: " + clockAdd.instant());
		System.out.println("减了10分钟: " + clockSub.instant());


image-202108141995813

OffsetClock

OffsetClock 是偏移时钟,时间偏移量的单位是Duration。

//Clock
     public static Clock offset(Clock baseClock, Duration offsetDuration) {
        Objects.requireNonNull(baseClock, "baseClock");
        Objects.requireNonNull(offsetDuration, "offsetDuration");
        if (offsetDuration.equals(Duration.ZERO)) {
            return baseClock;
        }
        return new OffsetClock(baseClock, offsetDuration);
    }

由源码可知,使用Clock.offset方法 返回的是OffsetClock实例对象

		Clock clock = Clock.systemDefaultZone();
		Clock fixedClock = Clock.fixed(clock.instant(), ZoneId.of("Asia/Shanghai"));
		System.out.println(fixedClock.instant());
		Clock clockAdd = Clock.offset(clock, Duration.ofMinutes(20));
		System.out.println("原先的: " + clock.instant());
		System.out.println("加了20分钟: " + clockAdd.instant());

image-20210814944060

SystemClock

SystemClock 是系统默认的本地时钟。

		Clock clock = Clock.systemDefaultZone();
		System.out.println(clock.millis());
		Clock utc = Clock.systemUTC();
		System.out.println(utc.millis());
		System.out.println(System.currentTimeMillis());

image-20210814904947

居然完全一样。这就要看下源码了

Clock.systemDefaultZone()

用的是系统默认的时区ZoneId.systemDefault()

    public static Clock systemDefaultZone() {
        return new SystemClock(ZoneId.systemDefault());
    }

image-2021081495878

最终调用的也是System.currentTimeMillis()

Clock.systemUTC()

用的是UTC时区ZoneOffset.UTC

    public static Clock systemUTC() {
        return new SystemClock(ZoneOffset.UTC);
    }

image-2021081495878

最终调用的也是System.currentTimeMillis()

结论

Clock.systemDefaultZone() 和Clock.systemUTC()获取的millis()时间戳是一样的,就是对应时区的差别。

TickClock

TickClock 是偏移时钟,时间偏移量的最小单位是纳秒。

如图所示,Clock主要提供下面三个方法

//构造的时钟的计时单位是自定义的偏移量单位
public static Clock tick(Clock baseClock, Duration tickDuration);
 //构造的时钟的计时单位是分
 public static Clock tickMinutes(ZoneId zone);
//构造的时钟的计时单位是秒
public static Clock tickSeconds(ZoneId zone) ;

image-202108149595

实战:

		Clock tickClock = Clock.tick(Clock.systemDefaultZone(),Duration.ofHours(1L));
		Clock tickMinutes = Clock.tickMinutes(ZoneId.of("Asia/Shanghai"));
		Clock tickSeconds = Clock.tickSeconds(ZoneId.of("Asia/Shanghai"));

		LocalDateTime tickClockLocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(tickClock.millis()),ZoneId.of("Asia/Shanghai"));
		LocalDateTime tickMinutesLocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(tickMinutes.millis()),ZoneId.of("Asia/Shanghai"));
		LocalDateTime tickSecondsLocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(tickSeconds.millis()),ZoneId.of("Asia/Shanghai"));

		System.out.println("tickClock :"+tickClock.millis() +" 转为date时间:"+tickClockLocalDateTime);
		System.out.println("tickMinutes:"+tickMinutes.millis() +" 转为date时间:"+tickMinutesLocalDateTime);
		System.out.println("tickSeconds:"+tickSeconds.millis() +" 转为date时间:"+tickSecondsLocalDateTime);

偏移量的单位支持:天,时,分,秒,豪秒,纳秒

image-20210814909314

image-2021081495696

Instant

Instant类说明
public final class Instant
        implements Temporal, TemporalAdjuster, Comparable<Instant>, Serializable {
        ...
        }

Instant表示瞬间时间。也是不可变类且是线程安全的。其实Java.time 这个包是线程安全的。

Instant是java 8新增的特性,里面有两个核心的字段

	...	
	private final long seconds;
    
    private final int nanos;
	...

一个是单位为秒的时间戳,另一个是单位为纳秒的时间戳。

是不是跟**System.currentTimeMillis()**返回的long时间戳很像,System.currentTimeMillis()返回的是毫秒级,Instant多了更精确的纳秒级时间戳。

Instant常用的用法
 		Instant now = Instant.now();
		System.out.println("now:"+now);
		System.out.println(now.getEpochSecond()); // 秒
		System.out.println(now.toEpochMilli()); // 毫秒

image-20210720905353

Instant是没有时区的,但是Instant加上时区后,可以转化为ZonedDateTime
		Instant ins = Instant.now();
		ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
		System.out.println(zdt);

image-202107205211996

long型时间戳转Instant

要注意long型时间戳的时间单位选择Instant对应的方法转化

//1626796436 为秒级时间戳
Instant ins = Instant.ofEpochSecond(1626796436);
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
System.out.println("秒级时间戳转化:"+zdt);
//1626796436111l 为秒级时间戳
Instant ins1 = Instant.ofEpochMilli(1626796436111l);
ZonedDateTime zdt1 = ins1.atZone(ZoneId.systemDefault());
System.out.println("毫秒级时间戳转化:"+zdt1);

Instant的坑

Instant.now()获取的时间与北京时间相差8个时区,这是一个细节,要避坑。

看源码,用的是UTC时间。

public static Instant now() {
        return Clock.systemUTC().instant();
    }

解决方案:

Instant now = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8));
System.out.println("now:"+now);

image-202107234326190

LocalDate

LocalDate类说明

LocalDate表示本地日期。只有表示年月日。相当于:yyyy-MM-dd。

LocalDate常用的用法
获取当前日期
		LocalDate localDate1 = LocalDate.now();
		LocalDate localDate2 = LocalDate.now(ZoneId.of("Asia/Shanghai"));
		LocalDate localDate3 = LocalDate.now(Clock.systemUTC());

		System.out.println("now :"+localDate1);
		System.out.println("now by zone :"+localDate2);
		System.out.println("now by Clock:"+localDate3);

image-2021081496781

获取localDate对象
		LocalDate localDate1 = LocalDate.of(2021, 8, 14);
		LocalDate localDate2 = LocalDate.parse("2021-08-14");
		System.out.println(localDate1);
		System.out.println(localDate2);

image-2021081497325

获取指定日期的年月日
		LocalDate localDate1 = LocalDate.of(2021, 8, 14);
		// 当前日期年份:2021
		System.out.println(localDate1.getYear());
		// 当前日期月份对象:AUGUST
		System.out.println(localDate1.getMonth());
		// 当前日期月份:8
		System.out.println(localDate1.getMonthValue());
		// 该日期是当前周的第几天:6
		System.out.println(localDate1.getDayOfWeek().getValue());
		// 该日期是当前月的第几天:14
		System.out.println(localDate1.getDayOfMonth());
		// 该日期是当前年的第几天:226
		System.out.println(localDate1.getDayOfYear());

image-2021081498430

修改年月日
		LocalDate localDate1 = LocalDate.of(2021, 8, 14);
		// 修改该日期的年份:2022-08-14
		System.out.println(localDate1.withYear(2022));
		// 修改该日期的月份:2021-12-14
		System.out.println(localDate1.withMonth(12));
		// 修改该日期在当月的天数:2021-08-01
		System.out.println(localDate1.withDayOfMonth(1));

image-20210814935404

比较日期
		LocalDate localDate1 = LocalDate.of(2021, 8, 14);
		// 比较指定日期和参数日期,返回正数,那么指定日期时间较晚(数字较大):13
		int i = localDate1.compareTo(LocalDate.of(2021, 8, 1));
		System.out.println(i);
		// 比较指定日期是否比参数日期早(true为早):true
		System.out.println(localDate1.isBefore(LocalDate.of(2021,8,31)));
		// 比较指定日期是否比参数日期晚(true为晚):false
		System.out.println(localDate1.isAfter(LocalDate.of(2021,8,31)));
		// 比较两个日期是否相等:true
		System.out.println(localDate1.isEqual(LocalDate.of(2021, 8, 14)));

image-202108149597

LocalDate 和String相互转化、Date和LocalDate相互转化
LocalDate 和String相互转化
		LocalDate localDate1 = LocalDate.of(2021, 8, 14);
		// LocalDate 转 String
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
		String dateString = localDate1.format(dateTimeFormatter);
		System.out.println("LocalDate 转 String:"+dateString);
		// String 转 LocalDate
		String str = "2021-08-14";
		DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
		LocalDate date = LocalDate.parse(str, fmt);
		System.out.println("String 转 LocalDate:"+date);

image-2021081499979

Date和LocalDate相互转化
	// Date 转 LocalDate
		Date now = new Date();
		// 先将Date转换为ZonedDateTime
		Instant instant = now.toInstant();
		ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("Asia/Shanghai"));
		LocalDate localDate = zonedDateTime.toLocalDate();
		// Sat Aug 14 23:16:28 CST 2021
		System.out.println(now);
		// 2021-08-14
		System.out.println(localDate);

		// LocalDate 转 Date
		LocalDate now1 = LocalDate.now();
		ZonedDateTime dateTime = now1.atStartOfDay(ZoneId.of("Asia/Shanghai"));
		Date date1 = Date.from(dateTime.toInstant());
		System.out.println(date1);

image-2021081492237

LocalDateTime

LocalDateTime类说明

表示当前日期时间,相当于:yyyy-MM-ddTHH:mm:ss

LocalDateTime常用的用法
获取当前日期和时间
		LocalDate d = LocalDate.now(); // 当前日期
		LocalTime t = LocalTime.now(); // 当前时间
		LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
		System.out.println(d); // 严格按照ISO 8601格式打印
		System.out.println(t); // 严格按照ISO 8601格式打印
		System.out.println(dt); // 严格按照ISO 8601格式打印

image-20210714857780

由运行结果可行,本地日期时间通过now()获取到的总是以当前默认时区返回的

获取指定日期和时间
		LocalDate d2 = LocalDate.of(2021, 07, 14); // 2021-07-14, 注意07=07月
		LocalTime t2 = LocalTime.of(13, 14, 20); // 13:14:20
		LocalDateTime dt2 = LocalDateTime.of(2021, 07, 14, 13, 14, 20);
		LocalDateTime dt3 = LocalDateTime.of(d2, t2);
		System.out.println("指定日期时间:"+dt2);
		System.out.println("指定日期时间:"+dt3);

image-20210714803165

日期时间的加减法及修改
		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

LocalDateTime和Date相互转化
Date转LocalDateTime
		System.out.println("------------------方法一:分步写-----------------------");
		//实例化一个时间对象
		Date date = new Date();
		//返回表示时间轴上同一点的瞬间作为日期对象
		Instant instant = date.toInstant();
		//获取系统默认时区
		ZoneId zoneId = ZoneId.systemDefault();
		//根据时区获取带时区的日期和时间
		ZonedDateTime zonedDateTime = instant.atZone(zoneId);
		//转化为LocalDateTime
		LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
		System.out.println("方法一:原Date = " + date);
		System.out.println("方法一:转化后的LocalDateTime = " + localDateTime);

		System.out.println("------------------方法二:一步到位(推荐使用)-----------------------");
		//实例化一个时间对象
		Date todayDate = new Date();
		//Instant.ofEpochMilli(long l)使用1970-01-01T00:00:00Z的纪元中的毫秒来获取Instant的实例
		LocalDateTime ldt = Instant.ofEpochMilli(todayDate.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
		System.out.println("方法二:原Date = " + todayDate);
		System.out.println("方法二:转化后的LocalDateTime = " + ldt);

image-20210714210839339

LocalDateTime转Date
		System.out.println("------------------方法一:分步写-----------------------");
		//获取LocalDateTime对象,当前时间
		LocalDateTime localDateTime = LocalDateTime.now();
		//获取系统默认时区
		ZoneId zoneId = ZoneId.systemDefault();
		//根据时区获取带时区的日期和时间
		ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
		//返回表示时间轴上同一点的瞬间作为日期对象
		Instant instant = zonedDateTime.toInstant();
		//转化为Date
		Date date = Date.from(instant);
		System.out.println("方法一:原LocalDateTime = " + localDateTime);
		System.out.println("方法一:转化后的Date = " + date);

		System.out.println("------------------方法二:一步到位(推荐使用)-----------------------");
		//实例化一个LocalDateTime对象
		LocalDateTime now = LocalDateTime.now();
		//转化为date
		Date dateResult = Date.from(now.atZone(ZoneId.systemDefault()).toInstant());
		System.out.println("方法二:原LocalDateTime = " + now);
		System.out.println("方法二:转化后的Date = " + dateResult);

image-20210714211035080

LocalTime

LocalTime类说明

LocalTime:本地时间,只有表示时分秒

LocalTime常用的用法
获取当前时间
		LocalTime localTime1 = LocalTime.now();
		LocalTime localTime2 = LocalTime.now(ZoneId.of("Asia/Shanghai"));
		LocalTime localTime3 = LocalTime.now(Clock.systemDefaultZone());

		System.out.println("now :"+localTime1);
		System.out.println("now by zone :"+localTime2);
		System.out.println("now by Clock:"+localTime3);

image-2021081498171

获取LocalTime对象
		LocalTime localTime1 = LocalTime.of(23, 26, 30);
		LocalTime localTime2 = LocalTime.of(23, 26);
		System.out.println(localTime1);
		System.out.println(localTime2);

image-2021081494673

获取指定日期的时分秒
		LocalTime localTime1 = LocalTime.of(23, 26, 30);
		//当前时间的时:23
		System.out.println(localTime1.getHour());
		//当前时间的分:26
		System.out.println(localTime1.getMinute());
		//当前时间的秒:30
		System.out.println(localTime1.getSecond());

image-2021081492055

修改时分秒
		LocalTime localTime1 = LocalTime.of(23, 26, 30);
		//修改时间的时:00:26:30
		System.out.println(localTime1.withHour(0));
		//修改时间的分:23:30:30
		System.out.println(localTime1.withMinute(30));
		//修改时间的秒:23:26:59
		System.out.println(localTime1.withSecond(59));

image-202108149774

比较时间
		LocalTime localTime1 = LocalTime.of(23, 26, 30);
		LocalTime localTime2 = LocalTime.of(23, 26, 32);
		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(localTime1.compareTo(localTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(localTime1.isBefore(localTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(localTime1.isAfter(localTime2));
		// 比较两个时间是否相等:true
		System.out.println(localTime1.equals(LocalTime.of(23, 26, 30)));

image-2021081498214

OffsetDateTime

OffsetDateTime类说明

OffsetDateTime:有时间偏移量的日期时间(不包含基于ZoneRegion的时间偏移量)

public final class OffsetDateTime
        implements Temporal, TemporalAdjuster, Comparable<OffsetDateTime>, Serializable {
    //The minimum supported {@code OffsetDateTime}, '-999999999-01-01T00:00:00+18:00' 
    public static final OffsetDateTime MIN = LocalDateTime.MIN.atOffset(ZoneOffset.MAX);
    // The maximum supported {@code OffsetDateTime}, '+999999999-12-31T23:59:59.999999999-18:00'.
    public static final OffsetDateTime MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN);
        ...
        }

上面的MINMAX 是公有静态变量。

OffsetDateTime常用的用法
获取当前日期时间
		OffsetDateTime offsetDateTime1 = OffsetDateTime.now();
		OffsetDateTime offsetDateTime2 = OffsetDateTime.now(ZoneId.of("Asia/Shanghai"));
		OffsetDateTime offsetDateTime3 = OffsetDateTime.now(Clock.systemUTC());

		System.out.println("now :"+offsetDateTime1);
		System.out.println("now by zone :"+offsetDateTime2);
		System.out.println("now by Clock:"+offsetDateTime3);

image-20210925205912880

获取OffsetDateTime对象
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		OffsetDateTime offsetDateTime1 = OffsetDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		OffsetDateTime offsetDateTime2 = OffsetDateTime. of(2021, 8, 15, 13, 14, 20,0, ZoneOffset.ofHours(8));
		Instant now = Instant.now();
		OffsetDateTime offsetDateTime3 = OffsetDateTime.ofInstant(now, ZoneId.of("Asia/Shanghai"));

		System.out.println(offsetDateTime1);
		System.out.println(offsetDateTime2);
		System.out.println(offsetDateTime3);

image-20210821900413

获取指定日期的年月日时分秒
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		OffsetDateTime offsetDateTime1 = OffsetDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		//当前时间的年:2021
		System.out.println(offsetDateTime1.getYear());
		//当前时间的月:8
		System.out.println(offsetDateTime1.getMonthValue());
		//当前时间的日:15
		System.out.println(offsetDateTime1.getDayOfMonth());
		//当前时间的时:13
		System.out.println(offsetDateTime1.getHour());
		//当前时间的分:14
		System.out.println(offsetDateTime1.getMinute());
		//当前时间的秒:20
		System.out.println(offsetDateTime1.getSecond());

image-2021082193542

修改年月日时分秒
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		OffsetDateTime offsetDateTime1 = OffsetDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		//修改时间的年:2022-08-15T13:14:20+08:00
		System.out.println(offsetDateTime1.withYear(2022));
		//修改时间的月:2021-09-15T13:14:20+08:00
		System.out.println(offsetDateTime1.withMonth(9));
		//修改时间的日:2021-08-30T13:14:20+08:00
		System.out.println(offsetDateTime1.withDayOfMonth(30));
		//修改时间的时:2021-08-15T00:14:20+08:00
		System.out.println(offsetDateTime1.withHour(0));
		//修改时间的分:2021-08-15T13:30:20+08:00
		System.out.println(offsetDateTime1.withMinute(30));
		//修改时间的秒:2021-08-15T13:14:59+08:00
		System.out.println(offsetDateTime1.withSecond(59));

image-2021082194524

比较日期时间
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		OffsetDateTime offsetDateTime1 = OffsetDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		OffsetDateTime offsetDateTime3 = OffsetDateTime.of(localDateTime1, ZoneOffset.ofHours(8));

		LocalDateTime localDateTime2 = LocalDateTime.of(2021, 8, 15, 13, 14, 30);
		OffsetDateTime offsetDateTime2 = OffsetDateTime.of(localDateTime2, ZoneOffset.ofHours(8));

		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(offsetDateTime1.compareTo(offsetDateTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(offsetDateTime1.isBefore(offsetDateTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(offsetDateTime1.isAfter(offsetDateTime2));
		// 比较两个时间是否相等:true
		System.out.println(offsetDateTime1.equals(offsetDateTime3));

image-20210821944542

字符串转化为OffsetDateTime对象
				String str = "2021-08-15T10:15:30+08:00";
		OffsetDateTime offsetDateTime1 = OffsetDateTime.parse(str);
		OffsetDateTime offsetDateTime2 = OffsetDateTime.parse(str,DateTimeFormatter.ISO_OFFSET_DATE_TIME);

		System.out.println(offsetDateTime1);
		System.out.println(offsetDateTime2);

image-2021082196169

OffsetTime

OffsetTime类说明

OffsetTime:有时间偏移量的时间

public final class OffsetTime
        implements Temporal, TemporalAdjuster, Comparable<OffsetTime>, Serializable {
   //The minimum supported {@code OffsetTime}, '00:00:00+18:00'.
    public static final OffsetTime MIN = LocalTime.MIN.atOffset(ZoneOffset.MAX);
    
    //The maximum supported {@code OffsetTime}, '23:59:59.999999999-18:00'.
    public static final OffsetTime MAX = LocalTime.MAX.atOffset(ZoneOffset.MIN);
    ...
}

上面的MINMAX 是公有静态变量。

OffsetTime常用的用法
获取当前时间
		OffsetTime offsetTime1 = OffsetTime.now();
		OffsetTime offsetTime2 = OffsetTime.now(ZoneId.of("Asia/Shanghai"));
		OffsetTime offsetTime3 = OffsetTime.now(Clock.systemUTC());

		System.out.println("now :"+offsetTime1);
		System.out.println("now by zone :"+offsetTime2);
		System.out.println("now by Clock:"+offsetTime3);

image-2021088203

获取OffsetTime对象
		LocalTime localTime1 = LocalTime.of(13, 14, 20);
		OffsetTime offsetTime1 = OffsetTime.of(localTime1, ZoneOffset.ofHours(8));
		OffsetTime offsetTime2 = OffsetTime. of(13, 14, 20,0, ZoneOffset.ofHours(8));
		Instant now = Instant.now();
		OffsetTime offsetTime3 = OffsetTime.ofInstant(now, ZoneId.of("Asia/Shanghai"));

		System.out.println(offsetTime1);
		System.out.println(offsetTime2);
		System.out.println(offsetTime3);

image-20210895380

获取指定时间的时分秒
		LocalTime localTime1 = LocalTime.of( 13, 14, 20);
		OffsetTime offsetTime1 = OffsetTime.of(localTime1, ZoneOffset.ofHours(8));

		//当前时间的时:13
		System.out.println(offsetTime1.getHour());
		//当前时间的分:14
		System.out.println(offsetTime1.getMinute());
		//当前时间的秒:20
		System.out.println(offsetTime1.getSecond());

image-202108802988

修改时分秒
		LocalTime localTime1 = LocalTime.of( 13, 14, 20);
		OffsetTime offsetTime1 = OffsetTime.of(localTime1, ZoneOffset.ofHours(8));

		//修改时间的时:00:14:20+08:00
		System.out.println(offsetTime1.withHour(0));
		//修改时间的分:13:30:20+08:00
		System.out.println(offsetTime1.withMinute(30));
		//修改时间的秒:13:14:59+08:00
		System.out.println(offsetTime1.withSecond(59));

image-202108945483

比较时间
		LocalTime localTime1 = LocalTime.of( 13, 14, 20);
		OffsetTime offsetTime1 = OffsetTime.of(localTime1, ZoneOffset.ofHours(8));
		OffsetTime offsetTime3 = OffsetTime.of(localTime1, ZoneOffset.ofHours(8));

		LocalTime localTime2 = LocalTime.of(13, 14, 30);
		OffsetTime offsetTime2 = OffsetTime.of(localTime2, ZoneOffset.ofHours(8));
		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(offsetTime1.compareTo(offsetTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(offsetTime1.isBefore(offsetTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(offsetTime1.isAfter(offsetTime2));
		// 比较两个时间是否相等:true
		System.out.println(offsetTime1.equals(offsetTime3));

image-2021089109890

ZonedDateTime

ZonedDateTime类说明

表示一个带时区的日期和时间,ZonedDateTime可以理解为LocalDateTime+ZoneId

从源码可以看出来,ZonedDateTime类中定义了LocalDateTime和ZoneId两个变量。

且ZonedDateTime类也是不可变类且是线程安全的。

public final class ZonedDateTime
        implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable {

    /\*\*
 \* Serialization version.
 \*/
    private static final long serialVersionUID = -6260982410461394882L;

    /\*\*
 \* The local date-time.
 \*/
    private final LocalDateTime dateTime;
    /\*\*
 \* The time-zone.
 \*/
    private final ZoneId zone;
    
    ...
}

ZonedDateTime常用的用法
获取当前日期时间
		// 默认时区获取当前时间
		ZonedDateTime zonedDateTime = ZonedDateTime.now();
		// 用指定时区获取当前时间,Asia/Shanghai为上海时区
		ZonedDateTime zonedDateTime1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
		//withZoneSameInstant为转换时区,参数为ZoneId
		ZonedDateTime zonedDateTime2 = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New\_York"));
		System.out.println(zonedDateTime);
		System.out.println(zonedDateTime1);
		System.out.println(zonedDateTime2);

image-202107205246938

		ZonedDateTime zonedDateTime1 = ZonedDateTime.now();
		ZonedDateTime zonedDateTime2 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
		ZonedDateTime zonedDateTime3 = ZonedDateTime.now(Clock.systemUTC());

		System.out.println("now :"+zonedDateTime1);
		System.out.println("now by zone :"+zonedDateTime2);
		System.out.println("now by Clock:"+zonedDateTime3);

image-202108957912

获取ZonedDateTime对象
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		ZonedDateTime zonedDateTime1 = ZonedDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		ZonedDateTime zonedDateTime2 = ZonedDateTime. of(2021, 8, 15, 13, 14, 20,0, ZoneOffset.ofHours(8));
		Instant now = Instant.now();
		ZonedDateTime zonedDateTime3 = ZonedDateTime.ofInstant(now, ZoneId.of("Asia/Shanghai"));

		System.out.println(zonedDateTime1);
		System.out.println(zonedDateTime2);
		System.out.println(zonedDateTime3);

image-2021088020148

获取指定日期的年月日时分秒
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		ZonedDateTime zonedDateTime1 = ZonedDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		//当前时间的年:2021
		System.out.println(zonedDateTime1.getYear());
		//当前时间的月:8
		System.out.println(zonedDateTime1.getMonthValue());
		//当前时间的日:15
		System.out.println(zonedDateTime1.getDayOfMonth());
		//当前时间的时:13
		System.out.println(zonedDateTime1.getHour());
		//当前时间的分:14
		System.out.println(zonedDateTime1.getMinute());
		//当前时间的秒:20
		System.out.println(zonedDateTime1.getSecond());

image-202108219231845

修改年月日时分秒
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		ZonedDateTime zonedDateTime1 = ZonedDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		//修改时间的年:2022-08-15T13:14:20+08:00
		System.out.println(zonedDateTime1.withYear(2022));
		//修改时间的月:2021-09-15T13:14:20+08:00
		System.out.println(zonedDateTime1.withMonth(9));
		//修改时间的日:2021-08-30T13:14:20+08:00
		System.out.println(zonedDateTime1.withDayOfMonth(30));
		//修改时间的时:2021-08-15T00:14:20+08:00
		System.out.println(zonedDateTime1.withHour(0));
		//修改时间的分:2021-08-15T13:30:20+08:00
		System.out.println(zonedDateTime1.withMinute(30));
		//修改时间的秒:2021-08-15T13:14:59+08:00
		System.out.println(zonedDateTime1.withSecond(59));

image-20210821998

比较日期时间
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		ZonedDateTime zonedDateTime1 = ZonedDateTime.of(localDateTime1, ZoneOffset.ofHours(8));

		ZonedDateTime zonedDateTime3 = ZonedDateTime.of(localDateTime1, ZoneOffset.ofHours(8));

		LocalDateTime localDateTime2 = LocalDateTime.of(2021, 8, 15, 13, 14, 30);
		ZonedDateTime zonedDateTime2 = ZonedDateTime.of(localDateTime2, ZoneOffset.ofHours(8));

		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(zonedDateTime1.compareTo(zonedDateTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(zonedDateTime1.isBefore(zonedDateTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(zonedDateTime1.isAfter(zonedDateTime2));
		// 比较两个时间是否相等:true
		System.out.println(zonedDateTime1.equals(zonedDateTime3));

image-20210821907094

LocalDateTime+ZoneId变ZonedDateTime
		LocalDateTime localDateTime = LocalDateTime.now();
		ZonedDateTime zonedDateTime1 = localDateTime.atZone(ZoneId.systemDefault());
		ZonedDateTime zonedDateTime2 = localDateTime.atZone(ZoneId.of("America/New\_York"));
		System.out.println(zonedDateTime1);
		System.out.println(zonedDateTime2);

image-2021072094003

上面的例子说明了,LocalDateTime是可以转成ZonedDateTime的。

(三)JSR-310:格式化和解析

DateTimeFormatter

DateTimeFormatter类说明

DateTimeFormatter的作用是进行格式化日期时间显示,且DateTimeFormatter是不可变类且是线程安全的。

public final class DateTimeFormatter {
...
}

说到时间的格式化显示,就要说老朋友SimpleDateFormat了,之前格式化Date就要用上。但是我们知道SimpleDateFormat是线程不安全的,还不清楚的,请看这篇文章java的SimpleDateFormat线程不安全出问题了,虚竹教你多种解决方案

DateTimeFormatter常用的用法
格式化
		ZonedDateTime zonedDateTime = ZonedDateTime.now();
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");
		System.out.println(formatter.format(zonedDateTime));

		DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
		System.out.println(usFormatter.format(zonedDateTime));

		DateTimeFormatter chinaFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
		System.out.println(chinaFormatter.format(zonedDateTime));

image-202107209416958

解析
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
		String dateTime = "2021年08月22日 13时14分20秒";
		LocalDateTime localDateTime = LocalDateTime.parse(dateTime, formatter);
		System.out.println(localDateTime);

image-2021082291306050

大家有没有注意到,parse方法 是放在LocalDateTime类中 的,而不是DateTimeFormatter类中 。这样的设计符合正常的思路想法,想解析出LocalDateTime 的日期时间,那就用LocalDateTime 。想解析其他的JSR-310的日期时间对象,那就用对应的日期时间对象去解析。

博主把常用的日期时间API都看了,这些里面除了Clock (时钟不需要解析的),其他都有实现parse方法

image-20210824903956

DateTimeFormatter的坑

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

2、YYYY和DD谨慎使用
		LocalDate date = LocalDate.of(2020,12,31);
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYYMM");
		// 结果是 202112
		System.out.println( formatter.format(date));

image-202107208183

Java’s DateTimeFormatter pattern “YYYY” gives you the week-based-year, (by default, ISO-8601 standard) the year of the Thursday of that week.

YYYY是取的当前周所在的年份,week-based year 是 ISO 8601 规定的。2020年12月31号,周算年份,就是2021年

image-2021072059555

 private static void tryit(int Y, int M, int D, String pat) {
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pat);
        LocalDate         dat = LocalDate.of(Y,M,D);
        String            str = fmt.format(dat);
        System.out.printf("Y=%04d M=%02d D=%02d " +
            "formatted with " +
            "\"%s\" -> %s\n",Y,M,D,pat,str);
    }
    public static void main(String[] args){
        tryit(2020,01,20,"MM/DD/YYYY");
        tryit(2020,01,21,"DD/MM/YYYY");
        tryit(2020,01,22,"YYYY-MM-DD");
        tryit(2020,03,17,"MM/DD/YYYY");
        tryit(2020,03,18,"DD/MM/YYYY");
        tryit(2020,03,19,"YYYY-MM-DD");
    }

Y=2020 M=01 D=20 formatted with "MM/DD/YYYY" -> 01/20/2020
Y=2020 M=01 D=21 formatted with "DD/MM/YYYY" -> 21/01/2020
Y=2020 M=01 D=22 formatted with "YYYY-MM-DD" -> 2020-01-22
Y=2020 M=03 D=17 formatted with "MM/DD/YYYY" -> 03/77/2020
Y=2020 M=03 D=18 formatted with "DD/MM/YYYY" -> 78/03/2020
Y=2020 M=03 D=19 formatted with "YYYY-MM-DD" -> 2020-03-79

最后三个日期是有问题的,因为大写的DD代表的是处于这一年中那一天,不是处于这个月的那一天,但是dd就没有问题。

例子参考于:https://www.cnblogs.com/tonyY/p/12153335.html

所以建议使用yyyy和dd。

3、DateTimeFormatter.format(Instant)会报错
报错信息:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra

分析原因:

代码**DateTimeFormatter.format(Instant)**是怎么处理的呢?

    public String format(TemporalAccessor temporal) {
        StringBuilder buf = new StringBuilder(32);
        formatTo(temporal, buf);
        return buf.toString();
    }



首先new了个StringBuilder对象,用来拼接字符串;

然后调用**formatTo(temporal, buf)**方法

public void formatTo(TemporalAccessor temporal, Appendable appendable) {
    Objects.requireNonNull(temporal, "temporal");
    Objects.requireNonNull(appendable, "appendable");
    try {
        DateTimePrintContext context = new DateTimePrintContext(temporal, this);
        if (appendable instanceof StringBuilder) {
            printerParser.format(context, (StringBuilder) appendable);
        } else {
            // buffer output to avoid writing to appendable in case of error
            StringBuilder buf = new StringBuilder(32);
            printerParser.format(context, buf);
            appendable.append(buf);
        }
    } catch (IOException ex) {
        throw new DateTimeException(ex.getMessage(), ex);
    }
}

**formatTo(temporal, buf)**方法也是先判断两个入参空处理。

然后,Instant对象被封装在一个新new的DateTimePrintContext对象

运行demo有问题,进行排查

		//根据特定格式格式化日期
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
		String dateStr = DateUtil.format(new Date(),dtf);
		System.out.println(dateStr);

image-20210725195348793

到这里已经是jdk的源码了DateTimeFormatter.format

image-20210725195424950

image-20210725195522610

image-20210725195636339

从上面可知,会调用 NumberPrinterParser.format() NumberPrinterParser是在DateTimeFormatterBuilder类中的。

image-20210725195947802

到这一步会报错

image-20210725200153850

为什么会报错呢,我们来看下context.getValue(field)发生了什么:

image-20210725200349650

从上面代码可行,temporal实际上是Instant对象,Instant.getLong只支持四种字段类型。。

NANO_OF_SECOND
MICRO_OF_SECOND
MILLI_OF_SECOND
INSTANT_SECONDS

image-20210725200551164

如果不是上面这几种字段类型,则抛出异常

DateUtil.format当遇到DateTimeFormatter会将Date对象首先转换为Instant,因为缺少时区,导致报错。

解决方案:
/\*\*
 \* 根据特定格式格式化日期
 \*
 \* @param date 被格式化的日期
 \* @param format
 \* @return 格式化后的字符串
 \* @since 5.0.0
 \*/
public static String format(Date date, DateTimeFormatter format) {
   if (null == format || null == date) {
      return null;
   }
   Instant instant = date.toInstant();
   ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
   LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
   return format.format(localDateTime);
}

先把date类型转化为LocalDateTime类型,然后再进行DateTimeFormatter.format(LocalDateTime)的格式化

测试demo

//根据特定格式格式化日期
String str = "2021-07-25 20:11:25";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:dd");
Date date = DateUtil.parse(str);
String dateStr = DateUtil.format(date,dtf);
System.out.println(dateStr);
Assert.assertEquals(str, dateStr);

image-20210725201444728

DateTimeFormatterBuilder

DateTimeFormatterBuilder类说明

DateTimeFormatter 的所有格式化器都是用DateTimeFormatterBuilder 建造器类创建的。

看下面两个ofPattern 源码:

//DateTimeFormatter
public static DateTimeFormatter ofPattern(String pattern) {
        return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
    }
    
     public static DateTimeFormatter ofPattern(String pattern, Locale locale) {
        return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale);
    }

解析风格配置

官方提供了四种解析风格的配置,如下枚举 SettingsParser

 static enum SettingsParser implements DateTimePrinterParser {
     // 大小写敏感
        SENSITIVE,
     // 大小写不敏感
        INSENSITIVE,
     //严格
        STRICT,
     //宽松
        LENIENT;
        ...
        }

对应DateTimeFormatterBuilder 类中的方法:

// 大小写敏感
public DateTimeFormatterBuilder parseCaseSensitive()
// 大小写不敏感
public DateTimeFormatterBuilder parseCaseInsensitive()
// 严格
public DateTimeFormatterBuilder parseStrict()
// 宽松
public DateTimeFormatterBuilder parseLenient()

这四个方法对应的源码如下:

// 大小写敏感
public DateTimeFormatterBuilder parseCaseSensitive() {
        appendInternal(SettingsParser.SENSITIVE);
        return this;
    }
// 大小写不敏感
 public DateTimeFormatterBuilder parseCaseInsensitive() {
        appendInternal(SettingsParser.INSENSITIVE);
        return this;
    }
// 严格
 public DateTimeFormatterBuilder parseStrict() {
        appendInternal(SettingsParser.STRICT);
        return this;
    }
// 宽松
public DateTimeFormatterBuilder parseLenient() {
        appendInternal(SettingsParser.LENIENT);
        return this;
    }

可以看出,都是调用appendInternal 方法。

接着往下看 appendInternal 源码:

 private int appendInternal(DateTimePrinterParser pp) {
        Objects.requireNonNull(pp, "pp");
        if (active.padNextWidth > 0) {
            if (pp != null) {
                pp = new PadPrinterParserDecorator(pp, active.padNextWidth, active.padNextChar);
            }
            active.padNextWidth = 0;
            active.padNextChar = 0;
        }
        active.printerParsers.add(pp);
        active.valueParserIndex = -1;
        return active.printerParsers.size() - 1;
    }

其中active 是一个DateTimeFormatterBuilder 实例,且这个DateTimeFormatterBuilder 实例内部有一个列表 List< DateTimePrinterParser > ,看了源码可知,真正做解析工作的是DateTimePrinterParser 对应的实例来做的。

DateTimePrinterParser 的源码:

 interface DateTimePrinterParser {

        boolean format(DateTimePrintContext context, StringBuilder buf);

        int parse(DateTimeParseContext context, CharSequence text, int position);
    }

源码有一共有16个DateTimePrinterParser 的实例。

//1.Composite printer and parser.
static final class CompositePrinterParser implements DateTimePrinterParser {...}

//2.Pads the output to a fixed width.
static final class PadPrinterParserDecorator implements DateTimePrinterParser {...}

//3.Enumeration to apply simple parse settings.
static enum SettingsParser implements DateTimePrinterParser{...}

//4. Defaults a value into the parse if not currently present.
static class DefaultValueParser implements DateTimePrinterParser {...}

//5.Prints or parses a character literal.
static final class CharLiteralPrinterParser implements DateTimePrinterParser {...}

//6.Prints or parses a string literal.
static final class StringLiteralPrinterParser implements DateTimePrinterParser {...}

//7.Prints and parses a numeric date-time field with optional padding.
static class NumberPrinterParser implements DateTimePrinterParser {...}

//8.Prints and parses a numeric date-time field with optional padding.
static final class FractionPrinterParser implements DateTimePrinterParser {...}

//9.Prints or parses field text.
static final class TextPrinterParser implements DateTimePrinterParser  {...}

//10.Prints or parses an ISO-8601 instant.
static final class InstantPrinterParser implements DateTimePrinterParser  {...}

//11.Prints or parses an offset ID.
static final class OffsetIdPrinterParser implements DateTimePrinterParser   {...}

//12.Prints or parses an offset ID.
static final class LocalizedOffsetIdPrinterParser implements DateTimePrinterParser {...}

//13.Prints or parses a zone ID.
static class ZoneIdPrinterParser implements DateTimePrinterParser {...}

//14. Prints or parses a chronology.
 static final class ChronoPrinterParser implements DateTimePrinterParser {...}

//15.Prints or parses a localized pattern.
static final class LocalizedPrinterParser implements DateTimePrinterParser {...}

//16.Prints or parses a localized pattern from a localized field.
 static final class WeekBasedFieldPrinterParser implements DateTimePrinterParser {...}

(四)JSR-310:常用计算工具

介绍下java8 中提供了几个常用于计算的类:

  • Duration:表示秒和纳秒的时间量
  • Period:表示年月日的时间量
  • TemporalUnit:日期时间的基本单位
  • TemporalField:日期时间的属性
  • ValueRange:表示取值范围

Duration

Duration类说明

包路径:java.time.Duration

public final class Duration
        implements TemporalAmount, Comparable<Duration>, Serializable {
    private final long seconds;
   
    private final int nanos;
        ...
        }

DurationTemporalAmount 的实现类,类里包含两个变量secondsnanos ,所以Duration 是由秒和纳秒组成的时间量。

一个Duration实例是不可变的,当创建出对象后就不能改变它的值了。

Duration常用的用法
创建Duration对象

Duration 适合处理较短的时间,需要更高的精确性。我们能使用between()方法比较两个瞬间的差:

		Instant first = Instant.now();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Instant second = Instant.now();
		Duration duration = Duration.between(first, second);
		System.out.println(duration);

image-20210830213020151

可以通过LocalDateTime 类获取获取Duration对象

		LocalDateTime first = LocalDateTime.of(2021, 8, 30, 23, 14, 20);

		LocalDateTime second = LocalDateTime.of(2021, 8, 30, 23, 13, 0);

		Duration duration = Duration.between(first, second);
		System.out.println(duration);

image-20210830221908291

访问Duration的时间

Duration 对象中可以获取秒和纳秒属性。但没有毫秒属性,跟System.getCurrentTimeMillis()不同。

	Instant first = Instant.now();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Instant second = Instant.now();
		Duration duration = Duration.between(first, second);
		System.out.println(duration);
		System.out.println("秒:"+duration.getSeconds());
		System.out.println("纳秒:"+duration.getNano());

image-20210830213704477

可以转换整个时间成其他单位,如纳秒,毫秒,分钟,小时,天

		Instant first = Instant.now();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Instant second = Instant.now();
		Duration duration = Duration.between(first, second);
		System.out.println(duration);
		System.out.println("秒:"+duration.getSeconds());
		System.out.println("纳秒:"+duration.getNano());
		System.out.println("纳秒:"+duration.toNanos());
		System.out.println("毫秒:"+duration.toMillis());
		System.out.println("分:"+duration.toMinutes());
		System.out.println("小时:"+duration.toHours());
		System.out.println("天:"+duration.toDays());

image-20210830220300588

由图上可知,getNano 方法和toNanos 方法不太一样,前者是获取这段时间的小于1s的部分,后者是整个时间转化为纳秒。

Duration计算
plusNanos()
plusMillis()
plusSeconds()
plusMinutes()
plusHours()
plusDays()
minusNanos()
minusMillis()
minusSeconds()
minusMinutes()
minusHours()
minusDays()

plusSecondsminusSeconds 为例:

LocalDateTime first = LocalDateTime.of(2021, 8, 30, 23, 14, 20);
		LocalDateTime second = LocalDateTime.of(2021, 8, 30, 23, 13, 0);
		Duration duration = Duration.between(first, second);
		System.out.println(duration);

		Duration duration1 = duration.plusSeconds(10);
		System.out.println("plusSeconds 后:"+duration);
		System.out.println("plusSeconds 后新的Duration对象:"+duration1);

		Duration duration2 = duration.minusSeconds(10);
		System.out.println("minusSeconds 后:"+duration);
		System.out.println("minusSeconds 后新的Duration对象:"+duration2);

image-20210830222707761

由上面的验证可知,这些计算方法执行后,会返回一个新的Duration对象,原先的Duration对象不变。

Period

Period类说明

包路径:java.time.Period

public final class Period
        implements ChronoPeriod, Serializable {
            /\*\*
 \* The number of years.
 \*/
    private final int years;
    /\*\*
 \* The number of months.
 \*/
    private final int months;
    /\*\*
 \* The number of days.
 \*/
    private final int days;
        ...
        }

PeriodChronoPeriod 的实现类,类里包含两个变量years ,monthsdays ,所以Period 是由年,月和日组成的时间量。

Period常用的用法
创建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

访问Period的时间
		LocalDate first = LocalDate.of(2021, 8, 28);
		LocalDate second = LocalDate.of(2022, 10, 31);
		Period period = Period.between(first, second);
		System.out.println(period);
		System.out.println("年:"+period.getYears());
		System.out.println("月:"+period.getMonths());
		System.out.println("日:"+period.getDays());

image-20210830225619062

可以转换整个时间成其他单位,月

LocalDate first = LocalDate.of(2021, 8, 29);
		LocalDate second = LocalDate.of(2022, 9, 30);
		Period period = Period.between(first, second);
		System.out.println(period);
		System.out.println("月:"+period.toTotalMonths());

image-20210830225328558

由图上可知,getMonths 方法和toTotalMonths 方法不太一样,前者是获取这段时间的月的部分,后者是整个时间转化为以月为单位长度。

toTotalMonths 源码:

public long toTotalMonths() {
        return years \* 12L + months;  // no overflow
    }

Duration计算
plusDays()
plusMonths()
plusYears()

minusDays()
minusMonths()
minusYears()

plusMonthsminusMonths 为例:

		LocalDate first = LocalDate.of(2021, 8, 28);
		LocalDate second = LocalDate.of(2022, 10, 31);
		Period period = Period.between(first, second);
		System.out.println(period);
		Period period1 = period.plusMonths(1);
		System.out.println("plusMonths 后:"+period);
		System.out.println("plusMonths 后新的Period对象:"+period1);

		Period period2 = period.minusMonths(1);
		System.out.println("minusMonths 后:"+period);
		System.out.println("minusMonths 后新的Period对象:"+period2);

image-20210830230345446

由上面的验证可知,这些计算方法执行后,会返回一个新的Period对象,原先的Period对象不变。

TemporalUnit

TemporalUnit类说明

包路径:java.time.temporal.TemporalUnit

public interface TemporalUnit {
...
}

public enum ChronoUnit implements TemporalUnit {
    private final String name;
    private final Duration duration;
   ...
}

TemporalUnit 主要实现类是枚举类型ChronoUnit

一个ChronoUnit成员会维护一个字符串名字属性name和一个Duration类型的实例。

其中ChronoUnit枚举了标准的日期时间单位集合,就是常用的年、月、日、小时、分钟、秒、毫秒、微秒、纳秒,这些时间单位的时间量到底是多少,代表多长的时间,在该枚举类中都有定义。

public enum ChronoUnit implements TemporalUnit {
 
    NANOS("Nanos", Duration.ofNanos(1)),
    MICROS("Micros", Duration.ofNanos(1000)),
    MILLIS("Millis", Duration.ofNanos(1000\_000)),
    SECONDS("Seconds", Duration.ofSeconds(1)),
    MINUTES("Minutes", Duration.ofSeconds(60)),
    HOURS("Hours", Duration.ofSeconds(3600)),
    HALF\_DAYS("HalfDays", Duration.ofSeconds(43200)),
    DAYS("Days", Duration.ofSeconds(86400)),
    WEEKS("Weeks", Duration.ofSeconds(7 \* 86400L)),
    MONTHS("Months", Duration.ofSeconds(31556952L / 12)),
    YEARS("Years", Duration.ofSeconds(31556952L)),
    DECADES("Decades", Duration.ofSeconds(31556952L \* 10L)),
    CENTURIES("Centuries", Duration.ofSeconds(31556952L \* 100L)),
    MILLENNIA("Millennia", Duration.ofSeconds(31556952L \* 1000L)),
    ERAS("Eras", Duration.ofSeconds(31556952L \* 1000\_000\_000L)),
    FOREVER("Forever", Duration.ofSeconds(Long.MAX_VALUE, 999\_999\_999));     
 
    private final String name;
    private final Duration duration;
 
    private ChronoUnit(String name, Duration estimatedDuration) {
        this.name = name;
        this.duration = estimatedDuration;
    }
    ···
}

ChronoUnit常用的用法
		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

TemporalField

TemporalField类说明

包路径:java.time.temporal.TemporalField

public interface TemporalField {
    ...
}

public enum ChronoField implements TemporalField {
     private final String name;
    private final TemporalUnit baseUnit;
    private final TemporalUnit rangeUnit;
    private final ValueRange range;
    ...
}

TemporalField 主要实现类是枚举类型ChronoField

一个ChronoField成员会维护一个字符串名字属性name、一个TemporalUnit的基础单位baseUnit、一个TemporalUnit的表示范围的单位rangeUnit和一个ValueRange类型的range用于表示当前属性的范围。

public enum ChronoField implements TemporalField {
    //一秒钟的纳秒数
    NANO\_OF\_SECOND("NanoOfSecond", NANOS, SECONDS, ValueRange.of(0, 999\_999\_999))
    //一分钟的秒数
    SECOND\_OF\_MINUTE("SecondOfMinute", SECONDS, MINUTES, ValueRange.of(0, 59), "second")
    //一个小时的分钟数
    MINUTE\_OF\_HOUR("MinuteOfHour", MINUTES, HOURS, ValueRange.of(0, 59), "minute")
    //一上午或者一下午有多少个小时
    CLOCK\_HOUR\_OF\_AMPM("ClockHourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(1, 12))
    //一天的小时数
    CLOCK\_HOUR\_OF\_DAY("ClockHourOfDay", HOURS, DAYS, ValueRange.of(1, 24))
    //上午还是下午
    AMPM\_OF\_DAY("AmPmOfDay", HALF_DAYS, DAYS, ValueRange.of(0, 1), "dayperiod")
    //一周的第几天
    DAY\_OF\_WEEK("DayOfWeek", DAYS, WEEKS, ValueRange.of(1, 7), "weekday")
    //当前月的天数
    DAY\_OF\_MONTH("DayOfMonth", DAYS, MONTHS, ValueRange.of(1, 28, 31), "day")
    //当前年的天数
    DAY\_OF\_YEAR("DayOfYear", DAYS, YEARS, ValueRange.of(1, 365, 366))
    //当前月的周数
    ALIGNED\_WEEK\_OF\_MONTH("AlignedWeekOfMonth", WEEKS, MONTHS, ValueRange.of(1, 4, 5))
    //当前年的周数
    ALIGNED\_WEEK\_OF\_YEAR("AlignedWeekOfYear", WEEKS, YEARS, ValueRange.of(1, 53))
    //以每月的第一天为星期一,然后计算当天是一周的第几天
    ALIGNED\_DAY\_OF\_WEEK\_IN\_MONTH("AlignedDayOfWeekInMonth", DAYS, WEEKS, ValueRange.of(1, 7))
    //以每月的第一天为星期一,然后计算当天是一周的第几天
    ALIGNED\_DAY\_OF\_WEEK\_IN\_YEAR("AlignedDayOfWeekInYear", DAYS, WEEKS, ValueRange.of(1, 7))
    //当前年的月数
    MONTH\_OF\_YEAR("MonthOfYear", MONTHS, YEARS, ValueRange.of(1, 12), "month")
    
    private final TemporalUnit baseUnit;
    private final String name;
    private final TemporalUnit rangeUnit;
    private final ValueRange range;
    private final String displayNameKey;
...
}

ChronoField常用的用法

ALIGNED_WEEK_OF_MONTH 和 ALIGNED_DAY_OF_WEEK_IN_MONTH 使用示例

		//每七天一周,2021-08-31 是周二,对应的值是3
		int num = LocalDate.of(2021, 8, 31).get(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH);
		System.out.println(num);
		//这个月的第5周 2021-08-31
		num = LocalDate.of(2021, 8, 31).get(ChronoField.ALIGNED_WEEK_OF_MONTH);
		System.out.println(num);

image-20210831233408038

ValueRange

ValueRange类说明

ValueRange 表示取值范围。

public final class ValueRange implements Serializable {

    /\*\*
 \* The smallest minimum value.最小值
 \*/
    private final long minSmallest;
    /\*\*
 \* The largest minimum value.最大可能最小值
 \*/
    private final long minLargest;
    /\*\*
 \* The smallest maximum value.最小可能最大值
 \*/
    private final long maxSmallest;
    /\*\*
 \* The largest maximum value.最大值
 \*/
    private final long maxLargest;
...
}

ValueRange常用的用法
ValueRange valueRange = ValueRange.of(1L, 10000L);
		System.out.println(valueRange);
		valueRange = ValueRange.of(1L, 5L, 10000L, 50000L);
		System.out.println(valueRange);

image-20210831234618197

		LocalDateTime localDateTime = LocalDateTime.of(2021, 8, 30, 23, 14, 20);
		ValueRange valueRange = localDateTime.range(ChronoField.DAY_OF_MONTH);
		System.out.println(valueRange.getMinimum());
		System.out.println(valueRange.getMaximum());
		System.out.println(valueRange.getLargestMinimum());
		System.out.println(valueRange.getSmallestMaximum());

image-202109019254578

Chronology 判断是否闰年

判断是否闰年是由年表Chronology 提供的,通常情况下,我们使用ISO下的年表,是IsoChronology

看下代码实现

 @Override
    public boolean isLeapYear(long prolepticYear) {
        return ((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0);
    }

好精炼的代码,值得我们研究研究

闰年的基本判定方法:
1、非整百年:能被4整除的为闰年。(如2004年就是闰年,2001年不是闰年)
2、整百年:能被400整除的是闰年。(如2000年是闰年,1900年不是闰年)

((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0);

这段代码用了两个条件,这两个条件都符合,才是闰年。

  • (prolepticYear & 3) == 0
  • (prolepticYear % 100) != 0 || (prolepticYear % 400) == 0

(prolepticYear & 3) == 0 用了与运算符“&”,其使用规律如下:
两个操作数中位都为1,结果才为1,否则结果为0。

3 的二进制是011prolepticYear & 3 目的是保留最后2位二进制数,然后判断是否最后两位二进制数等于0。如果等于0,证明能被4整除。闰年一定要满足是4的倍数的条件;

(prolepticYear % 100) != 0 || (prolepticYear % 400) == 0 这个就比较好理解了,看是不是100的倍数或者是不是400 倍数。

而且小虚竹发现java.time.Year#isLeap() 用的实现代码逻辑是一样的

public static boolean isLeap(long year) {
    return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
}

即使是巨佬写的代码,也存在代码的复用性问题

上面IsoChronology 是对Chronology接口接口的isLeapYear实现,MinguoChronology等实现类的isLeapYear,互用了IsoChronology的isLeapYear方法。

//MinguoChronology 
public boolean isLeapYear(long prolepticYear) {
        return IsoChronology.INSTANCE.isLeapYear(prolepticYear + YEARS_DIFFERENCE);
    }

巨佬是有考虑复用的,在MinguoChronology等实现类已经有复用了。

java.time.Year#isLeap() 的优先级高,因为它是静态方法。isoChronology ** 可以引Year.isLeap**
Year ** 不可以引Chronology.isLeapYear** 。

博主发现在IsoChronology ** 的resolveYMD** 中已经存在了对Year.isLeap 的引用。

image-202109089047374

有的工具类会为了减少外部类依赖,重新写一次底层方法,避免外部类(或是不在一个包底下)的类依赖,这个已经用了,说不过去 。所以代码是存在复用性问题的。

实战
		int year = 2020;
		System.out.println(Year.isLeap(year));
		System.out.println(IsoChronology.INSTANCE.isLeapYear(year));

		LocalDate localDate = LocalDate.of(2021,9,7);
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println(localDate.isLeapYear());
		System.out.println(localDateTime.toLocalDate().isLeapYear());

image-2021090733986

比较日期时间的先后

基本上都有这四个比较方法::compareTo()、isBefore()、isAfter()、和equals()

比较-LocalDate
		LocalDate localDate1 = LocalDate.of(2021, 8, 14);
		// 比较指定日期和参数日期,返回正数,那么指定日期时间较晚(数字较大):13
		int i = localDate1.compareTo(LocalDate.of(2021, 8, 1));
		System.out.println(i);
		// 比较指定日期是否比参数日期早(true为早):true
		System.out.println(localDate1.isBefore(LocalDate.of(2021,8,31)));
		// 比较指定日期是否比参数日期晚(true为晚):false
		System.out.println(localDate1.isAfter(LocalDate.of(2021,8,31)));
		// 比较两个日期是否相等:true
		System.out.println(localDate1.isEqual(LocalDate.of(2021, 8, 14)));

image-202108149597

比较-LocalTime
		LocalTime localTime1 = LocalTime.of(23, 26, 30);
		LocalTime localTime2 = LocalTime.of(23, 26, 32);
		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(localTime1.compareTo(localTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(localTime1.isBefore(localTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(localTime1.isAfter(localTime2));
		// 比较两个时间是否相等:true
		System.out.println(localTime1.equals(LocalTime.of(23, 26, 30)));

image-2021081498214

比较-OffsetDateTime
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		OffsetDateTime offsetDateTime1 = OffsetDateTime.of(localDateTime1, ZoneOffset.ofHours(8));
		OffsetDateTime offsetDateTime3 = OffsetDateTime.of(localDateTime1, ZoneOffset.ofHours(8));

		LocalDateTime localDateTime2 = LocalDateTime.of(2021, 8, 15, 13, 14, 30);
		OffsetDateTime offsetDateTime2 = OffsetDateTime.of(localDateTime2, ZoneOffset.ofHours(8));

		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(offsetDateTime1.compareTo(offsetDateTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(offsetDateTime1.isBefore(offsetDateTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(offsetDateTime1.isAfter(offsetDateTime2));
		// 比较两个时间是否相等:true
		System.out.println(offsetDateTime1.equals(offsetDateTime3));

image-20210821944542

比较-OffsetTime
		LocalTime localTime1 = LocalTime.of( 13, 14, 20);
		OffsetTime offsetTime1 = OffsetTime.of(localTime1, ZoneOffset.ofHours(8));
		OffsetTime offsetTime3 = OffsetTime.of(localTime1, ZoneOffset.ofHours(8));

		LocalTime localTime2 = LocalTime.of(13, 14, 30);
		OffsetTime offsetTime2 = OffsetTime.of(localTime2, ZoneOffset.ofHours(8));
		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(offsetTime1.compareTo(offsetTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(offsetTime1.isBefore(offsetTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(offsetTime1.isAfter(offsetTime2));
		// 比较两个时间是否相等:true
		System.out.println(offsetTime1.equals(offsetTime3));

image-2021089109890

比较-ZonedDateTime
		LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20);
		ZonedDateTime zonedDateTime1 = ZonedDateTime.of(localDateTime1, ZoneOffset.ofHours(8));

		ZonedDateTime zonedDateTime3 = ZonedDateTime.of(localDateTime1, ZoneOffset.ofHours(8));

		LocalDateTime localDateTime2 = LocalDateTime.of(2021, 8, 15, 13, 14, 30);
		ZonedDateTime zonedDateTime2 = ZonedDateTime.of(localDateTime2, ZoneOffset.ofHours(8));

		// 两个时间进行比较 大返回1,小就返回-1,一样就返回0:-1
		System.out.println(zonedDateTime1.compareTo(zonedDateTime2));

		// 比较指定时间是否比参数时间早(true为早):true
		System.out.println(zonedDateTime1.isBefore(zonedDateTime2));
		// 比较指定时间是否比参数时间晚(true为晚):false
		System.out.println(zonedDateTime1.isAfter(zonedDateTime2));
		// 比较两个时间是否相等:true
		System.out.println(zonedDateTime1.equals(zonedDateTime3));

image-20210821907094

计算日期时间的间隔

Duration 和**Period ** 都有 **between ** 方法

这个就不在重复说了,上面DurationPeriod 的常用用法里有介绍到。

TemporalAdjuster 日期校准器

序号方法描述
1dayOfWeekInMonth返回同一个月中每周的第几天
2firstDayOfMonth返回当月的第一天
3firstDayOfNextMonth返回下月的第一天
4firstDayOfNextYear返回下一年的第一天
5firstDayOfYear返回本年的第一天
6firstInMonth返回同一个月中第一个星期几
7lastDayOfMonth返回当月的最后一天
8lastDayOfNextMonth返回下月的最后一天
9lastDayOfNextYear返回下一年的最后一天
0lastDayOfYear返回本年的最后一天
11lastInMonth返回同一个月中最后一个星期几
12next / previous返回后一个/前一个给定的星期几
13nextOrSame / previousOrSame返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回
LocalDateTime now = LocalDateTime.of(2021,9,8,0,20,13);
		System.out.println("当前时间:" + now + "======>" + now.getDayOfWeek());
		System.out.println("下一个周一:" + now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)));
		System.out.println("上一个周一:" + now.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)));
		System.out.println("下一个周五:" + now.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY)));
		System.out.println("上一个周五:" + now.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY)));
		System.out.println("本月最后一个周五:" + now.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)));
		System.out.println("本月第一个周五:" + now.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY)));
		System.out.println("本月第一天:" + now.with(TemporalAdjusters.firstDayOfMonth()));
		System.out.println("本月最后一天:" + now.with(TemporalAdjusters.lastDayOfMonth()));
		System.out.println("下月的第一天:" + now.with(TemporalAdjusters.firstDayOfNextMonth()));
		System.out.println("今年的第一天:" + now.with(TemporalAdjusters.firstDayOfYear()));
		System.out.println("今年的最后一天:" + now.with(TemporalAdjusters.lastDayOfYear()));
		System.out.println("下一年的第一天:" + now.with(TemporalAdjusters.firstDayOfNextYear()));
		System.out.println("本月的第二个周五:" + now.with(TemporalAdjusters.dayOfWeekInMonth(2,DayOfWeek.FRIDAY)));
		System.out.println("两周后:" + now.with(TemporalAdjusters.ofDateAdjuster(date -> date.plusWeeks(2))));

image-202109089785

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

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


#### 源码解析:



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](https://img-blog.csdnimg.cn/img_convert/54cc583e7f811024b9d0b20605f296ff.png)


如果不是前两者的话,就调用**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](https://img-blog.csdnimg.cn/img_convert/0c9228aeaa8649b97edd63bb8b364cc8.png)


居然没有报错,这是为什么呢



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



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)**
[外链图片转存中...(img-fAhrpmKp-1713541410106)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值