5hutool源码分析:DateUtil(时间工具类)-解析

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

❤️作者简介:大家好,我是小虚竹。Java领域优质创作者🏆,CSDN博客专家认证🏆,华为云享专家认证🏆

❤️技术活,该赏

❤️点赞 👍 收藏 ⭐再看,养成习惯

看本篇文章前,建议先对java源码的日期和时间有一定的了解,如果不了解的话,可以先看这篇文章:

万字博文教你搞懂java源码的日期和时间相关用法

关联文章:

hutool实战(带你掌握里面的各种工具)目录

5hutool实战:DateUtil-解析被格式化的时间


源码分析目的

知其然,知其所以然

项目引用

此博文的依据:hutool-5.6.5版本源码

1
2
3
4
5
xml复制代码        <dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.6.5</version>
</dependency>

方法名称:DateUtil.parseLocalDateTime(java.lang.CharSequence)

方法描述

构建LocalDateTime对象

格式:yyyy-MM-dd HH:mm:ss

源码分析一

1
2
3
4
5
6
7
8
9
10
java复制代码	/**
* 构建LocalDateTime对象
*
* @param dateStr 时间字符串(带格式)
* @param format 使用{@link DatePattern}定义的格式
* @return LocalDateTime对象
*/
public static LocalDateTime parseLocalDateTime(CharSequence dateStr, String format) {
return LocalDateTimeUtil.parse(dateStr, format);
}

parseLocalDateTime(CharSequence dateStr, String format)方法的format要使用DatePattern定义的格式,保证能解析出来。

来看看**LocalDateTimeUtil.parse(dateStr, format)**源码是如何写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
java复制代码//LocalDateTimeUtil
/**
* 解析日期时间字符串为{@link LocalDateTime}
*
* @param text 日期时间字符串
* @param format 日期格式,类似于yyyy-MM-dd HH:mm:ss,SSS
* @return {@link LocalDateTime}
*/
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);
}

注释里写着修复了JDK8 的一个bug bugs.openjdk.java.net/browse/JDK-…

试试:

1
2
3
4
5
6
java复制代码	@Test
public void localDateTimeTest5() {
String strDate = "20210805220359100";
DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
System.out.println(formatter.parse(strDate));
}

image-20210805911767

真的会报错。

官方有给出解决方案,但在java9版本修复。下放到8u版本里。

image-20210805224431816

所以hutool这边的写法就好理解了,这个是官方给出的解决方案:修复yyyyMMddHHmmssSSS格式不能解析的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码//LocalDateTimeUtil
/**
* 解析日期时间字符串为{@link LocalDateTime}
*
* @param text 日期时间字符串
* @param format 日期格式,类似于yyyy-MM-dd HH:mm:ss,SSS
* @return {@link LocalDateTime}
*/
public static LocalDateTime parse(CharSequence text, String format) {

...
formatter = new DateTimeFormatterBuilder()
.appendPattern(DatePattern.PURE_DATETIME_PATTERN)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
...

return parse(text, formatter);
}

最后调用parse(text, formatter);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码/**
* 解析日期时间字符串为{@link LocalDateTime},格式支持日期时间、日期、时间
*
* @param text 日期时间字符串
* @param formatter 日期格式化器,预定义的格式见:{@link DateTimeFormatter}
* @return {@link LocalDateTime}
*/
public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
if (null == text) {
return null;
}
if (null == formatter) {
return LocalDateTime.parse(text);
}

return of(formatter.parse(text));
}

首先好习惯,先判断入参是否为空处理。

1
2
java复制代码LocalDateTime.parse(text)//返回LocalDateTime对象
DateTimeFormatter.parse(text)//返回TemporalAccessor对象

都是java8 新提供的API:

万字博文教你搞懂java源码的日期和时间相关用法

最后使用**of(TemporalAccessor)**转化为LocalDateTime时间对象

首先来看下TemporalAccessor

TemporalAccessor 的实现类包含

  • Instant
  • LocalDateTime
  • ZonedDateTime
  • OffsetDateTime
  • LocalDate
  • LocalTime
  • OffsetTime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码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)
);
}

首先,由上面可知,LocalDate是temporalAccessor的实现类。((LocalDate)temporalAccessor).atStartOfDay()这个就可以变成LocalDate.atStartOfDay()

1
2
3
java复制代码public LocalDateTime atStartOfDay() {
return LocalDateTime.of(this, LocalTime.MIDNIGHT);
}

LocalTime.MIDNIGHT:

1
2
3
4
java复制代码 /**
* The time of midnight at the start of the day, '00:00'.
*/
public static final LocalTime MIDNIGHT;

LocalDateTime是由LocalDate和LocalTime组合成的。

1
2
3
4
5
java复制代码 public static LocalDateTime of(LocalDate date, LocalTime time) {
Objects.requireNonNull(date, "date");
Objects.requireNonNull(time, "time");
return new LocalDateTime(date, time);
}

然后,TemporalAccessorUtil.get(temporalAccessor, ChronoField.YEAR)这个是hutool的源码,我们来看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码	/**
* 安全获取时间的某个属性,属性不存在返回0
*
* @param temporalAccessor 需要获取的时间对象
* @param field 需要获取的属性
* @return 时间的值,如果无法获取则默认为 0
*/
public static int get(TemporalAccessor temporalAccessor, TemporalField field) {
if (temporalAccessor.isSupported(field)) {
return temporalAccessor.get(field);
}

return (int)field.range().getMinimum();
}

判断temporalAccessor是否有支持指定的字段,如果有,直接返回指定字段对应的时间值。如果没有,则执行

(int)field.range().getMinimum(),获取字段对应的最小值。

1
2
3
4
5
6
7
java复制代码		System.out.println(ChronoField.YEAR.range().getMinimum());
System.out.println(ChronoField.MONTH_OF_YEAR.range().getMinimum());
System.out.println(ChronoField.DAY_OF_MONTH.range().getMinimum());
System.out.println(ChronoField.HOUR_OF_DAY.range().getMinimum());
System.out.println(ChronoField.MINUTE_OF_HOUR.range().getMinimum());
System.out.println(ChronoField.SECOND_OF_MINUTE.range().getMinimum());
System.out.println(ChronoField.NANO_OF_SECOND.range().getMinimum());

image-20210805953564

year值初始化时设的最小值和最大值

image-20210805233338612

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%