4hutool源码分析:DateUtil(时间工具类)-格式化时间(万字长文源码分析,学大佬如何写代码)

最后

手绘了下图所示的kafka知识大纲流程图(xmind文件不能上传,导出图片展现),但都可提供源文件给每位爱学习的朋友

image.png

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

}

} 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 {@link SimpleDateFormat} todo-zhw DateTimeFormatter

  • @return 格式化后的字符串

  • @since 5.0.0

*/

public static String format(Date date, DateTimeFormatter format) {

if (null == format || null == date) {

return null;

}

Instant instant = date.toInstant();

ZoneId zoneId = ZoneId.systemDefault();

ZonedDateTime zonedDateTime = instant.atZone(zoneId);

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

官方改法


修订版本 #5.7.5

/**

  • 根据特定格式格式化日期

  • @param date 被格式化的日期

  • @param format {@link SimpleDateFormat} {@link DatePattern#NORM_DATETIME_FORMATTER}

  • @return 格式化后的字符串

  • @since 5.0.0

*/

public static String format(Date date, DateTimeFormatter format) {

if (null == format || null == date) {

return null;

}

// java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra

// 出现以上报错时,表示Instant时间戳没有时区信息,赋予默认时区

return TemporalAccessorUtil.format(date.toInstant(), format);

}

更换了新的调用方法TemporalAccessorUtil.format(date.toInstant(), format),date.toInstant()返回Instant对象,则变成了TemporalAccessorUtil.format(Instant, format)

//TemporalAccessorUtil

/**

  • 格式化日期时间为指定格式

  • @param time {@link TemporalAccessor}

  • @param formatter 日期格式化器,预定义的格式见:{@link DateTimeFormatter}

  • @return 格式化后的字符串

  • @since 5.3.10

*/

public static String format(TemporalAccessor time, DateTimeFormatter formatter) {

if (null == time) {

return null;

}

if(null == formatter){

formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

}

try {

return formatter.format(time);

} catch (UnsupportedTemporalTypeException e){

if(time instanceof LocalDate && e.getMessage().contains(“HourOfDay”)){

// 用户传入LocalDate,但是要求格式化带有时间部分,转换为LocalDateTime重试

return formatter.format(((LocalDate) time).atStartOfDay());

}else if(time instanceof LocalTime && e.getMessage().contains(“YearOfEra”)){

// 用户传入LocalTime,但是要求格式化带有日期部分,转换为LocalDateTime重试

return formatter.format(((LocalTime) time).atDate(LocalDate.now()));

} else if(time instanceof Instant){

// 时间戳没有时区信息,赋予默认时区

return formatter.format(((Instant) time).atZone(ZoneId.systemDefault()));

}

throw e;

}

}

对比了下跟5.6.5版本的差异,新增了当time是Instant时,给一个默认的时区

else if(time instanceof Instant){

// 时间戳没有时区信息,赋予默认时区

return formatter.format(((Instant) time).atZone(ZoneId.systemDefault()));

}

image-2021072651290

方法名称:DateUtil.formatDateTime(java.util.Date)

================================================================================================================

方法描述


格式化日期时间

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

源码分析一


/**

  • 格式化日期时间

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

  • @param date 被格式化的日期

  • @return 格式化后的日期

*/

public static String formatDateTime(Date date) {

if (null == date) {

return null;

}

return DatePattern.NORM_DATETIME_FORMAT.format(date);

}

首先好习惯,先对入参进行判空处理

然后调用DatePattern.NORM_DATETIME_FORMAT.format(date)返回FastDateFormat对象

针对只支持java8之前的程序,可以使用FastDateFormat线程安全替换SimpleDateFormat线程不安全–》源码分析

FastDateFormat 是一个线程安全的实现

来源 Apache Commons Lang 3.5

DatePattern.NORM_DATETIME_FORMAT–>

/**

  • 标准日期时间格式,精确到秒 {@link FastDateFormat}:yyyy-MM-dd HH:mm:ss

*/

public static final FastDateFormat NORM_DATETIME_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_PATTERN);

则转化为了FastDateFormat.format(date)

//FastDateFormat

@Override

public String format(final Date date) {

return printer.format(date);

}

//FastDatePrinter

@Override

public String format(final Date date) {

final Calendar c = Calendar.getInstance(timeZone, locale);

c.setTime(date);

return applyRulesToString©;

}

先把date转化为Calendar,方便获取日期和时间

private String applyRulesToString(final Calendar c) {

return applyRules(c, new StringBuilder(mMaxLengthEstimate)).toString();

}

private B applyRules(final Calendar calendar, final B buf) {

try {

for (final Rule rule : this.rules) {

rule.appendTo(buf, calendar);

}

} catch (final IOException e) {

throw new DateException(e);

}

return buf;

}

核心的代码是这块

for (final Rule rule : this.rules) {

rule.appendTo(buf, calendar);

}

测试demo

String dateStr = “2017-03-01”;

Date date = DateUtil.parse(dateStr);

String formatDateTime = DateUtil.formatDateTime(date);

Assert.assertEquals(“2017-03-01 00:00:00”, formatDateTime);

断点跟进代码:

image-20210725211322291

往下跟代码

image-20210725203801875

//FastDatePrinter

private static class PaddedNumberField implements NumberRule {

@Override

public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {

appendTo(buffer, calendar.get(mField));

}

/**

  • {@inheritDoc}

*/

@Override

public final void appendTo(final Appendable buffer, final int value) throws IOException {

appendFullDigits(buffer, value, mSize);

}

…}

已经取到年份了:2017

image-20210725204215108

往下跟代码:

image-20210725211459199

//FastDatePrinter

private static class CharacterLiteral implements Rule {

@Override

public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {

buffer.append(mValue);

}

}

就是把’-'字符串直接拼接上去。

下一个获取月份:

image-20210725211534912

//FastDatePrinter

private static class TwoDigitMonthField implements NumberRule {

/**

  • {@inheritDoc}

*/

@Override

public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {

appendTo(buffer, calendar.get(Calendar.MONTH) + 1);

}

/**

  • {@inheritDoc}

*/

@Override

public final void appendTo(final Appendable buffer, final int value) throws IOException {

appendDigits(buffer, value);

}

}

获取了月份:3

image-20210725205900990

然后下一个:把’-'字符串直接拼接上去。

image-20210725211609400

继续往下跟

image-20210725211704501

//FastDatePrinter

private static class TwoDigitNumberField implements NumberRule {

@Override

public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {

appendTo(buffer, calendar.get(mField));

}

/**

  • {@inheritDoc}

*/

@Override

public final void appendTo(final Appendable buffer, final int value) throws IOException {

if (value < 100) {

appendDigits(buffer, value);

} else {

appendFullDigits(buffer, value, 2);

}

}

}

image-20210725210353207

这样获取到的日期:2017-03-01

然后加了一个空格字符串

image-20210725211811625

时分秒的调用,都是调用一样的FastDatePrinter#TwoDigitNumberField。

//FastDatePrinter

private static class TwoDigitNumberField implements NumberRule {

@Override

public void appendTo(final Appendable buffer, final Calendar calendar) throws IOException {

appendTo(buffer, calendar.get(mField));

}

/**

  • {@inheritDoc}

*/

@Override

public final void appendTo(final Appendable buffer, final int value) throws IOException {

if (value < 100) {

appendDigits(buffer, value);

} else {

appendFullDigits(buffer, value, 2);

}

}

}

image-20210725212005035

这样就得到了"2017-03-01 00:00:00"

方法名称:DateUtil.formatDate(java.util.Date)

============================================================================================================

方法描述


格式化日期部分(不包括时间)

格式 yyyy-MM-dd

源码分析一


/**

  • 格式化日期部分(不包括时间)

  • 格式 yyyy-MM-dd

  • @param date 被格式化的日期

  • @return 格式化后的字符串

*/

public static String formatDate(Date date) {

if (null == date) {

return null;

}

return DatePattern.NORM_DATE_FORMAT.format(date);

}

首先好习惯,先对入参进行判空处理

然后调用DatePattern.NORM_DATE_FORMAT.format(date)返回FastDateFormat对象

针对只支持java8之前的程序,可以使用FastDateFormat线程安全替换SimpleDateFormat线程不安全–》源码分析

FastDateFormat 是一个线程安全的实现

来源 Apache Commons Lang 3.5

/**

  • 标准日期格式 {@link FastDateFormat}:yyyy-MM-dd

*/

public static final FastDateFormat NORM_DATE_FORMAT = FastDateFormat.getInstance(NORM_DATE_PATTERN);

则转化为了FastDateFormat.format(date),源码分析看上面

方法名称:DateUtil.formatTime(java.util.Date)

============================================================================================================

方法描述


格式化时间

格式 HH:mm:ss

源码分析一


/**

  • 格式化时间

  • 格式 HH:mm:ss

  • @param date 被格式化的日期

  • @return 格式化后的字符串

  • @since 3.0.1

*/

public static String formatTime(Date date) {

if (null == date) {

return null;

}

return DatePattern.NORM_TIME_FORMAT.format(date);

}

首先好习惯,先对入参进行判空处理

然后调用DatePattern.NORM_TIME_FORMAT.format(date)返回FastDateFormat对象

针对只支持java8之前的程序,可以使用FastDateFormat线程安全替换SimpleDateFormat线程不安全–》源码分析

FastDateFormat 是一个线程安全的实现

来源 Apache Commons Lang 3.5

/**

  • 标准时间格式 {@link FastDateFormat}:HH:mm:ss

*/

public static final FastDateFormat NORM_TIME_FORMAT = FastDateFormat.getInstance(NORM_TIME_PATTERN);

则转化为了FastDateFormat.format(date),源码分析看上面

方法名称:DateUtil.formatHttpDate(java.util.Date)

================================================================================================================

方法描述


格式化为Http的标准日期格式

标准日期格式遵循RFC 1123规范,格式类似于:Fri, 31 Dec 1999 23:59:59 GMT

源码分析一


/**

  • 格式化为Http的标准日期格式

  • 标准日期格式遵循RFC 1123规范,格式类似于:Fri, 31 Dec 1999 23:59:59 GMT

  • @param date 被格式化的日期

  • @return HTTP标准形式日期字符串

*/

public static String formatHttpDate(Date date) {

if (null == date) {

return null;

}

return DatePattern.HTTP_DATETIME_FORMAT.format(date);

}

首先好习惯,先对入参进行判空处理

然后调用DatePattern.HTTP_DATETIME_FORMAT.format(date)返回FastDateFormat对象

针对只支持java8之前的程序,可以使用FastDateFormat线程安全替换SimpleDateFormat线程不安全–》源码分析

FastDateFormat 是一个线程安全的实现

来源 Apache Commons Lang 3.5

/**

  • HTTP头中日期时间格式 {@link FastDateFormat}:EEE, dd MMM yyyy HH:mm:ss z

*/

public static final FastDateFormat HTTP_DATETIME_FORMAT = FastDateFormat.getInstance(HTTP_DATETIME_PATTERN, TimeZone.getTimeZone(“GMT”), Locale.US);

则转化为了FastDateFormat.format(date),源码分析看上面

方法名称:DateUtil.formatChineseDate(java.util.Date, boolean, boolean)

=====================================================================================================================================

方法描述


格式化为中文日期格式,如果isUppercase为false,则返回类似:2018年10月24日,否则返回二〇一八年十月二十四日

源码分析一


/**

  • 格式化为中文日期格式,如果isUppercase为false,则返回类似:2018年10月24日,否则返回二〇一八年十月二十四日

  • @param date 被格式化的日期

  • @param isUppercase 是否采用大写形式

  • @param withTime 是否包含时间部分

  • @return 中文日期字符串

  • @since 5.3.9

*/

public static String formatChineseDate(Date date, boolean isUppercase, boolean withTime) {

if (null == date) {

return null;

}

if (false == isUppercase) {

return (withTime ? DatePattern.CHINESE_DATE_TIME_FORMAT : DatePattern.CHINESE_DATE_FORMAT).format(date);

}

return CalendarUtil.formatChineseDate(CalendarUtil.calendar(date), withTime);

}

首先好习惯,先对入参进行判空处理

当不采用大写形式时

执行**(withTime ? DatePattern.CHINESE_DATE_TIME_FORMAT : DatePattern.CHINESE_DATE_FORMAT).format(date)**

这里用了一个多目运算,包含时间部分时,调用DatePattern.CHINESE_DATE_TIME_FORMAT .format(date), 不包含时间部分时,调用DatePattern.CHINESE_DATE_FORMAT.format(date)

/**

  • 标准日期格式 {@link FastDateFormat}:yyyy年MM月dd日HH时mm分ss秒

*/

public static final FastDateFormat CHINESE_DATE_TIME_FORMAT = FastDateFormat.getInstance(CHINESE_DATE_TIME_PATTERN);

/**

  • 标准日期格式 {@link FastDateFormat}:yyyy年MM月dd日

*/

public static final FastDateFormat CHINESE_DATE_FORMAT = FastDateFormat.getInstance(CHINESE_DATE_PATTERN);

由上面源码可知,两个都是返回FastDateFormat对象。

针对只支持java8之前的程序,可以使用FastDateFormat线程安全替换SimpleDateFormat线程不安全–》源码分析

FastDateFormat 是一个线程安全的实现

来源 Apache Commons Lang 3.5

/**

  • HTTP头中日期时间格式 {@link FastDateFormat}:EEE, dd MMM yyyy HH:mm:ss z

*/

public static final FastDateFormat HTTP_DATETIME_FORMAT = FastDateFormat.getInstance(HTTP_DATETIME_PATTERN, TimeZone.getTimeZone(“GMT”), Locale.US);

则转化为了FastDateFormat.format(date),源码分析看上面

当采用大写形式时

执行CalendarUtil.formatChineseDate(CalendarUtil.calendar(date), withTime);

可以拆解成两部分:

  • CalendarUtil.calendar(date):把date对象转换为Calendar对象

  • CalendarUtil.formatChineseDate(Calendar calendar, boolean withTime):将指定Calendar时间格式化为纯中文形式,例如

2018-02-24 12:13:14转换为 二〇一八年二月二十四日(withTime为false)

2018-02-24 12:13:14 转换为 二〇一八年二月二十四日一十二时一十三分一十四秒(withTime为true)

CalendarUtil.calendar(date)源码分析:

/**

  • 转换为Calendar对象

  • @param date 日期对象

  • @return Calendar对象

*/

public static Calendar calendar(Date date) {

if (date instanceof DateTime) {

return ((DateTime) date).toCalendar();

} else {

return calendar(date.getTime());

}

}

如果是hutool提供的DateTime类型,可以直接转化获取。

如果是date类型,则通过*cal.setTimeInMillis转化为Calendar对象。

/**

  • 转换为Calendar对象

  • @param millis 时间戳

  • @return Calendar对象

*/

public static Calendar calendar(long millis) {

final Calendar cal = Calendar.getInstance();

cal.setTimeInMillis(millis);

return cal;

}

有同学会发现,这个跟我们常用的Calendar.setTime(Date date),不一样,看了下源码

//Calendar

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

拿到字节跳动offer后,简历被阿里捞了起来,二面迎来了P9"盘问"

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

例如

2018-02-24 12:13:14转换为 二〇一八年二月二十四日(withTime为false)

2018-02-24 12:13:14 转换为 二〇一八年二月二十四日一十二时一十三分一十四秒(withTime为true)

CalendarUtil.calendar(date)源码分析:

/**

  • 转换为Calendar对象

  • @param date 日期对象

  • @return Calendar对象

*/

public static Calendar calendar(Date date) {

if (date instanceof DateTime) {

return ((DateTime) date).toCalendar();

} else {

return calendar(date.getTime());

}

}

如果是hutool提供的DateTime类型,可以直接转化获取。

如果是date类型,则通过*cal.setTimeInMillis转化为Calendar对象。

/**

  • 转换为Calendar对象

  • @param millis 时间戳

  • @return Calendar对象

*/

public static Calendar calendar(long millis) {

final Calendar cal = Calendar.getInstance();

cal.setTimeInMillis(millis);

return cal;

}

有同学会发现,这个跟我们常用的Calendar.setTime(Date date),不一样,看了下源码

//Calendar

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

[外链图片转存中…(img-JDPlukdz-1715278256991)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值