这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」
❤️作者简介:大家好,我是小虚竹。Java领域优质创作者🏆,CSDN博客专家认证🏆,华为云享专家认证🏆
❤️技术活,该赏
❤️点赞 👍 收藏 ⭐再看,养成习惯
看本篇文章前,建议先对java源码的日期和时间有一定的了解,如果不了解的话,可以先看这篇文章:
关联文章:
源码分析目的
知其然,知其所以然
项目引用
此博文的依据:hutool-5.6.5版本源码
<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
源码分析一
/**
* 构建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)**源码是如何写的:
//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-…
试试:
@Test
public void localDateTimeTest5() {
String strDate = "20210805220359100";
DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
System.out.println(formatter.parse(strDate));
}
真的会报错。
官方有给出解决方案,但在java9版本修复。下放到8u版本里。
所以hutool这边的写法就好理解了,这个是官方给出的解决方案:修复yyyyMMddHHmmssSSS格式不能解析的问题
//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);
/**
* 解析日期时间字符串为{@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));
}
首先好习惯,先判断入参是否为空处理。
LocalDateTime.parse(text)//返回LocalDateTime对象
DateTimeFormatter.parse(text)//返回TemporalAccessor对象
都是java8 新提供的API:
最后使用**of(TemporalAccessor)**转化为LocalDateTime时间对象
首先来看下TemporalAccessor:
TemporalAccessor 的实现类包含
- Instant
- LocalDateTime
- ZonedDateTime
- OffsetDateTime
- LocalDate
- LocalTime
- OffsetTime
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()
public LocalDateTime atStartOfDay() {
return LocalDateTime.of(this, LocalTime.MIDNIGHT);
}
LocalTime.MIDNIGHT:
/**
* The time of midnight at the start of the day, '00:00'.
*/
public static final LocalTime MIDNIGHT;
LocalDateTime是由LocalDate和LocalTime组合成的。
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的源码,我们来看看
/**
* 安全获取时间的某个属性,属性不存在返回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(),获取字段对应的最小值。
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());
year值初始化时设的最小值和最大值