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

然后new了一个SimpleDateFormat对象。并设置了时区和设置了setLenient,这个方法的含义是是否严格解析日期。setLenient设置为false时,就是严格解析日期:会严格按照日期时间格式,java不会帮忙计算,直接抛出异常。

image-20210725122714337

然后**format(Date date, DateFormat format) **代码分析:

/**

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

  • @param date 被格式化的日期

  • @param format {@link SimpleDateFormat}

  • @return 格式化后的字符串

*/

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

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

return null;

}

return format.format(date);

}

对两个入参进行了判空处理。然后调用SimpleDateFormat.format(date),这是java8之前就有提供的方法。

方法名称:DateUtil.format(java.util.Date, java.time.format.DateTimeFormatter)(方法有问题,已反馈,官方已修正)

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

方法描述


根据特定格式格式化日期

源码分析一


/**

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

  • @param date 被格式化的日期

  • @param format {@link DateTimeFormatter}

  • @return 格式化后的字符串

  • @since 5.0.0

*/

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

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

return null;

}

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

}

首先对两个入参做了判空处理。

然后,执行了format.format(date.toInstant()),代码可拆解成两部分:

  • date.toInstant():返回Instant对象

  • DateTimeFormatter.format(Instant):java8提供的格式化日期时间的方法

代码**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 {@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)**

Java面试核心知识点笔记

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

Java中高级面试高频考点整理

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

最后分享Java进阶学习及面试必备的视频教学

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

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

方法描述


格式化为中文日期格式,如果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)**

Java面试核心知识点笔记

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

[外链图片转存中…(img-TbqfogC7-1714253256477)]

Java中高级面试高频考点整理

[外链图片转存中…(img-QFgh9CqT-1714253256477)]

[外链图片转存中…(img-O4taG0DC-1714253256478)]

最后分享Java进阶学习及面试必备的视频教学

[外链图片转存中…(img-o2F6W355-1714253256478)]

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

  • 30
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python图像处理是一项非常有趣的技能,这项技能可以使您能够更好地理解计算机视觉、数字信号处理、深度习和其他各种应用程序。Python图像处理的第一步是掌握基本的Python语言和编程知识,例如变量、循环、条件语句和函数等。 Python图像处理工具包也是非常重要的,这些工具包包括 Pillow、OpenCV、SciPy、scikit-image等。这些工具包提供了一系列函数和方法,使您可以方便地操作图像、从图像中提取特征和执行模式识别。Pillow是Python Imaging Library的升级版,支持在Python 3.x版本的平台上使用。OpenCV是一个强大的计算机视觉库,提供各种算法和函数来处理图像和视频件。SciPy提供了广泛的科计算功能,包括图像处理。Scikit-image是一个基于Python的用于图像处理和计算机视觉的库。 Python图像处理的习过程需要不断地进行实践,应该尝试不同的图像处理技术、算法和工具包来理解每个技术的优势和劣势。还应该尽量积累有关图像处理的知识(例如神经网络、模式识别和计算机视觉的数原理)。可以使用在线编程环境或者自己安装Python和相关工具包来进行实践。实践的过程中,一定要保持耐心、自信和专注,因为Python图像处理涉及到很多细节和不同的实现方式。 另外,可以通过阅读相关的书籍、参加培训班和参与Python图像处理社区等方式,来了解更多Python图像处理的知识和技术。阅读书籍可以从入门到进阶的习,培训班可以有专业人士教授,Python图像处理社区可以互相交流习。 Python图像处理需要不断地实践和努力,但是通过掌握基本的Python语言和编程知识、了解Python图像处理工具包和积累相关的知识,您一定可以成为Python图像处理的专家。加油!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值