竹笋

首页 » 问答 » 问答 » 时间工具类格式化时间万字长文
TUhjnbcbe - 2022/12/4 19:40:00

技术活,该赏点赞再看,养成习惯

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

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

关联文章:

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

hutool实战:DateUtil-格式化时间

源码分析目的

知其然,知其所以然

项目引用

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

dependency      groupIdcn.hutool/groupId      artifactIdhutool-co/artifactId      version5.6.5/version    /dependency

方法名称:DateUtil.formatLocalDateTime(java.time.LocalDateTime)

方法描述

格式化日期时间br格式yyyy-MM-ddHH:mm:ss

源码分析一

  /***格式化日期时间br*格式yyyy-MM-ddHH:mm:ss**

paramlocalDateTime被格式化的日期*

turn格式化后的字符串*/  publicstaticStringformatLocalDateTime(LocalDateTimelocalDateTime){    turnLocalDateTimeUtil.formatNormal(localDateTime);  }

首先formatLocalDateTime方法的入参是LocalDateTime(Java8支持的日期时间类,是线程安全的)

然后调用LocalDateTimeUtil.formatNormal(localDateTime)

//LocalDateTimeUtil  /***格式化日期时间为yyyy-MM-ddHH:mm:ss格式**

paramtime{

linkLocalDateTime}*

turn格式化后的字符串*

since5..*/  publicstaticStringformatNormal(LocalDateTimetime){    turnformat(time,DatePattern.NORM_DATETIME_FORMATTER);  }  /***格式化日期时间为指定格式**

paramtime{

linkLocalDateTime}*

paramformatter日期格式化器,预定义的格式见:{

linkDateTimeFormatter}*

turn格式化后的字符串*/  publicstaticStringformat(LocalDateTimetime,DateTimeFormatterformatter){    turnTemporalAccessorUtil.format(time,formatter);  }

跟代码,发现DatePattern.NORM_DATETIME_FORMATTER的日期时间格式为:

/***标准日期时间格式,精确到秒:yyyy-MM-ddHH:mm:ss*/publicstaticfinalStringNORM_DATETIME_PATTERN="yyyy-MM-ddHH:mm:ss";

然后会调用format(LocalDateTimetime,DateTimeFormatterformatter),DateTimeFormatter这个也是(Java8支持的日期格式化器类,是线程安全的)。

hutool这里做了很好的示范,使用DateTimeFormatter替换了SimpleDateFormat(线程不安全的)。

为什么SimpleDateFormat是线程不安全的,请看万字博文教你搞懂java源码的日期和时间相关用法

然后我们继续往下深挖TemporalAccessorUtil.format(time,formatter)

/***格式化日期时间为指定格式**

paramtime{

linkTemporalAccessor}*

paramformatter日期格式化器,预定义的格式见:{

linkDateTimeFormatter}*

turn格式化后的字符串*

since5..0*/publicstaticStringformat(TemporalAccessortime,DateTimeFormatterformatter){if(null==time){turnnull;}if(null==formatter){formatter=DateTimeFormatter.ISO_LOCAL_DATE_TIME;}try{turnformatter.format(time);}catch(UnsupportedTemporalTypeExceptione){if(timeinstanceofLocalDatee.getMessage().contains("HourOfDay")){//用户传入LocalDate,但是要求格式化带有时间部分,转换为LocalDateTime重试turnformatter.format(((LocalDate)time).atStartOfDay());}elseif(timeinstanceofLocalTimee.getMessage().contains("YearOfEra")){//用户传入LocalTime,但是要求格式化带有日期部分,转换为LocalDateTime重试turnformatter.format(((LocalTime)time).atDate(LocalDate.now()));}throwe;}}

最前面加了两个入参判空处理,time为null时,返回null;formatter为null时,给格式默认值,eg:0--0T0:5:0

然后执行formatter.format(time)相当于是DateTimeFormatter.format(LocalDateTime)。这样就格式化成功了。

值得一说的是**TemporalAccessorUtil.format(TemporalAccessortime,DateTimeFormatterformatter)**里面有加异常处理机制

try{turnformatter.format(time);}catch(UnsupportedTemporalTypeExceptione){if(timeinstanceofLocalDatee.getMessage().contains("HourOfDay")){//用户传入LocalDate,但是要求格式化带有时间部分,转换为LocalDateTime重试turnformatter.format(((LocalDate)time).atStartOfDay());}elseif(timeinstanceofLocalTimee.getMessage().contains("YearOfEra")){//用户传入LocalTime,但是要求格式化带有日期部分,转换为LocalDateTime重试turnformatter.format(((LocalTime)time).atDate(LocalDate.now()));}throwe;}

因为入参TemporalAccessortime的实现类常用的有如下几个(java8提供的):

LocalDateTimeLocalDateLocalTime

在进行日期时间转化时,日期时间和要转化的格式化字符串要对应上,不然会抛出异常,所以做了如上的补救措施。

方法名称:DateUtil.format(java.time.LocalDateTime,java.lang.String)

方法描述

根据特定格式格式化日期

源码分析一

/***根据特定格式格式化日期**

paramlocalDateTime被格式化的日期*

paramformat日期格式,常用格式见:{

linkDatePattern}*

turn格式化后的字符串*/publicstaticStringformat(LocalDateTimelocalDateTime,Stringformat){turnLocalDateTimeUtil.format(localDateTime,format);}

首先:hutool提供了常用的日期时间格式

/***日期格式化类,提供常用的日期格式化对象**/publicclassDatePattern{...}

然后:调用LocalDateTimeUtil.format(localDateTime,format)

/***格式化日期时间为指定格式**

paramtime{

linkLocalDateTime}*

paramformat日期格式,类似于yyyy-MM-ddHH:mm:ss,SSS*

turn格式化后的字符串*/publicstaticStringformat(LocalDateTimetime,Stringformat){if(null==time){turnnull;}turnformat(time,DateTimeFormatter.ofPattern(format));}

源码**format(time,DateTimeFormatter.ofPattern(format))**可以拆解成两部分:

DateTimeFormatter.ofPattern(format)

format(LocalDateTimetime,DateTimeFormatterformatter)

第一部分:**DateTimeFormatter.ofPattern(format)**是把字符串日期时间格式转化为日期时间格式化对象DateTimeFormatter;

注意DateTimeFormatter.ofPattern(format)的用法是有坑的(代码详解–万字博文教你搞懂java源码的日期和时间相关用法):

在正常配置按照标准格式的字符串日期,是能够正常转换的。如果月,日,时,分,秒在不足两位的情况需要补0,否则的话会转换失败,抛出异常。YYYY和DD谨慎使用

第二部分,format(LocalDateTimetime,DateTimeFormatterformatter)上面有介绍了,这里就不水字了。

方法名称:DateUtil.format(java.util.Date,java.text.DateFormat)

方法描述

根据特定格式格式化日期

源码分析一

/***根据特定格式格式化日期**

paramdate被格式化的日期*

paramformat日期格式,常用格式见:{

linkDatePattern}*

turn格式化后的字符串*/publicstaticStringformat(Datedate,Stringformat){if(null==date

StrUtil.isBlank(format)){turnnull;}TimeZonetimeZone=null;if(dateinstanceofDateTime){timeZone=((DateTime)date).getTimeZone();}turnformat(date,newSimpleFormat(format,null,timeZone));}

从代码中**format(Datedate,Stringformat)**方法提供了两个入参,一个是Date类型的被格式化的日期和要日期格式的字符串。这是为了兼容java8之前的旧日期时间API提供的方法。

方法内首先对两个参数加了判空处理。

然后判断时间是否是hutool的DateTime对象,如果是,则获取时区TimeZone

接着调用format(date,newSimpleFormat(format,null,timeZone)),可拆解成两部分:

newSimpleFormat(format,null,timeZone),获取SimpleDateFormat对象(注:此方法是非线程安全的)

format(Datedate,DateFormatformat)根据特定格式格式化日期

首先:**newSimpleFormat(format,null,timeZone)**代码详解:

/***创建{

linkSimpleDateFormat},注意此对象非线程安全!br*此对象默认为严格格式模式,即parse时如果格式不正确会报错。**

parampattern表达式*

paramlocale{

linkLocale},{

codenull}表示默认*

paramtimeZone{

linkTimeZone},{

codenull}表示默认*

turn{

linkSimpleDateFormat}*

since5.5.5*/publicstaticSimpleDateFormatnewSimpleFormat(Stringpattern,Localelocale,TimeZonetimeZone){if(null==locale){locale=Locale.getDefault(Locale.Category.FORMAT);}finalSimpleDateFormatformat=newSimpleDateFormat(pattern,locale);if(null!=timeZone){format.setTimeZone(timeZone);}format.setLenient(false);turnformat;}

如果**format(Datedate,Stringformat)输入的是Date对象的时间,那format(date,newSimpleFormat(format,null,timeZone))**具象化后,是这样的:format(date,newSimpleFormat(format,null,null))。

//获取当前的语言环境locale=Locale.getDefault(Locale.Category.FORMAT);

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

然后**format(Datedate,DateFormatformat)**代码分析:

/***根据特定格式格式化日期**

paramdate被格式化的日期*

paramformat{

linkSimpleDateFormat}*

turn格式化后的字符串*/publicstaticStringformat(Datedate,DateFormatformat){if(null==format

null==date){turnnull;}turnformat.format(date);}

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

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

方法描述

根据特定格式格式化日期

源码分析一

/***根据特定格式格式化日期**

paramdate被格式化的日期*

paramformat{

linkDateTimeFormatter}*

turn格式化后的字符串*

since5.0.0*/publicstaticStringformat(Datedate,DateTimeFormatterformat){if(null==format

null==date){turnnull;}turnformat.format(date.toInstant());}

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

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

date.toInstant():返回Instant对象DateTimeFormatter.format(Instant):java8提供的格式化日期时间的方法

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

publicStringformat(TemporalAccessortemporal){StringBuilderbuf=newStringBuilder();formatTo(temporal,buf);turnbuf.toString();}

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

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

publicvoidformatTo(TemporalAccessortemporal,Appendableappendable){Objects.quiNonNull(temporal,"temporal");Objects.quiNonNull(appendable,"appendable");try{DateTimePrintContextcontext=newDateTimePrintContext(temporal,this);if(appendableinstanceofStringBuilder){printerParser.format(context,(StringBuilder)appendable);}else{//bufferoutputtoavoidwritingtoappendableincaseoferrorStringBuilderbuf=newStringBuilder();printerParser.format(context,buf);appendable.append(buf);}}catch(IOExceptionex){thrownewDateTimeException(ex.getMessage(),ex);}}

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

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

运行demo有问题,进行排查

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

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

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

到这一步会报错

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

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

NANO_OF_SECONDMICRO_OF_SECONDMILLI_OF_SECONDINSTANT_SECONDS

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

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

建议改法

/***根据特定格式格式化日期**

paramdate被格式化的日期*

paramformat{

linkSimpleDateFormat}todo-zhwDateTimeFormatter*

turn格式化后的字符串*

since5.0.0*/publicstaticStringformat(Datedate,DateTimeFormatterformat){if(null==format

null==date){turnnull;}Instantinstant=date.toInstant();ZoneIdzoneId=ZoneId.systemDefault();ZonedDateTimezonedDateTime=instant.atZone(zoneId);LocalDateTimelocalDateTime=zonedDateTime.toLocalDateTime();turnformat.format(localDateTime);}

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

测试demo

//根据特定格式格式化日期Stringstr="0-07-50::5";DateTimeFormatterdtf=DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:dd");Datedate=DateUtil.parse(str);StringdateStr=DateUtil.format(date,dtf);System.out.println(dateStr);Assert.assertEquals(str,dateStr);

官方改法

修订版本#5.7.5

/***根据特定格式格式化日期**

paramdate被格式化的日期*

paramformat{

linkSimpleDateFormat}{

linkDatePattern#NORM_DATETIME_FORMATTER}*

turn格式化后的字符串*

since5.0.0*/publicstaticStringformat(Datedate,DateTimeFormatterformat){if(null==format

null==date){turnnull;}//java.time.temporal.UnsupportedTemporalTypeException:Unsupportedfield:YearOfEra//出现以上报错时,表示Instant时间戳没有时区信息,赋予默认时区turnTemporalAccessorUtil.format(date.toInstant(),format);}

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

//TemporalAccessorUtil  /***格式化日期时间为指定格式**

paramtime{

linkTemporalAccessor}*

paramformatter日期格式化器,预定义的格式见:{

linkDateTimeFormatter}*

turn格式化后的字符串*

since5..0*/  publicstaticStringformat(TemporalAccessortime,DateTimeFormatterformatter){    if(null==time){      turnnull;    }    if(null==formatter){      formatter=DateTimeFormatter.ISO_LOCAL_DATE_TIME;    }    try{      turnformatter.format(time);    }catch(UnsupportedTemporalTypeExceptione){      if(timeinstanceofLocalDatee.getMessage().contains("HourOfDay")){        //用户传入LocalDate,但是要求格式化带有时间部分,转换为LocalDateTime重试        turnformatter.format(((LocalDate)time).atStartOfDay());      }elseif(timeinstanceofLocalTimee.getMessage().contains("YearOfEra")){        //用户传入LocalTime,但是要求格式化带有日期部分,转换为LocalDateTime重试        turnformatter.format(((LocalTime)time).atDate(LocalDate.now()));      }elseif(timeinstanceofInstant){        //时间戳没有时区信息,赋予默认时区        turnformatter.format(((Instant)time).atZone(ZoneId.systemDefault()));      }      throwe;    }  }

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

elseif(timeinstanceofInstant){//时间戳没有时区信息,赋予默认时区turnformatter.format(((Instant)time).atZone(ZoneId.systemDefault()));}

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

方法描述

格式化日期时间br格式yyyy-MM-ddHH:mm:ss

源码分析一

/***格式化日期时间br*格式yyyy-MM-ddHH:mm:ss**

paramdate被格式化的日期*

turn格式化后的日期*/publicstaticStringformatDateTime(Datedate){if(null==date){turnnull;}turnDatePattern.NORM_DATETIME_FORMAT.format(date);}

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

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

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

FastDateFormat是一个线程安全的实现来源ApacheCommonsLang.5

DatePattern.NORM_DATETIME_FORMAT--  /***标准日期时间格式,精确到秒{

linkFastDateFormat}:yyyy-MM-ddHH:mm:ss*/  publicstaticfinalFastDateFormatNORM_DATETIME_FORMAT=FastDateFormat.getInstance(NORM_DATETIME_PATTERN);

则转化为了FastDateFormat.format(date)

//FastDateFormat

OverridepublicStringformat(finalDatedate){turnprinter.format(date);}//FastDatePrinter

OverridepublicStringformat(finalDatedate){finalCalendarc=Calendar.getInstance(timeZone,locale);c.setTime(date);turnapplyRulesToString(c);}

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

privateStringapplyRulesToString(finalCalendarc){turnapplyRules(c,newStringBuilder(mMaxLengthEstimate)).toString();}  privateBextendsAppendableBapplyRules(finalCalendarcalendar,finalBbuf){    try{      for(finalRulerule:this.rules){        rule.appendTo(buf,calendar);      }    }catch(finalIOExceptione){      thrownewDateException(e);    }    turnbuf;  }

核心的代码是这块

for(finalRulerule:this.rules){        rule.appendTo(buf,calendar);      }

测试demo

StringdateStr="07-0-0";Datedate=DateUtil.parse(dateStr);StringformatDateTime=DateUtil.formatDateTime(date);Assert.assertEquals("07-0-:00:00",formatDateTime);

断点跟进代码:

往下跟代码

//FastDatePrinterprivatestaticclassPaddedNumberFieldimplementsNumberRule{...

Override    publicvoidappendTo(finalAppendablebuffer,finalCalendarcalendar)throwsIOException{      appendTo(buffer,calendar.get(mField));    }    /***{

inheritDoc}*/    

Override    publicfinalvoidappendTo(finalAppendablebuffer,finalintvalue)throwsIOException{      appendFullDigits(buffer,value,mSize);    }...}

已经取到年份了:07

往下跟代码:

//FastDatePrinter  privatestaticclassCharacterLiteralimplementsRule{  ...  

Override    publicvoidappendTo(finalAppendablebuffer,finalCalendarcalendar)throwsIOException{      buffer.append(mValue);    }  ...  }  

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

下一个获取月份:

//FastDatePrinterprivatestaticclassTwoDigitMonthFieldimplementsNumberRule{.../***{

inheritDoc}*/    

Override    publicvoidappendTo(finalAppendablebuffer,finalCalendarcalendar)throwsIOException{      appendTo(buffer,calendar.get(Calendar.MONTH)+);    }    /***{

inheritDoc}*/    

Override    publicfinalvoidappendTo(finalAppendablebuffer,finalintvalue)throwsIOException{      appendDigits(buffer,value);    }...}

获取了月份:

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

继续往下跟

//FastDatePrinterprivatestaticclassTwoDigitNumberFieldimplementsNumberRule{...  

Override    publicvoidappendTo(finalAppendablebuffer,finalCalendarcalendar)throwsIOException{      appendTo(buffer,calendar.get(mField));    }    /***{

inheritDoc}*/    

Override    publicfinalvoidappendTo(finalAppendablebuffer,finalintvalue)throwsIOException{      if(value00){        appendDigits(buffer,value);      }else{        appendFullDigits(buffer,value,);      }    }...}

这样获取到的日期:07-0-0

然后加了一个空格字符串

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

//FastDatePrinterprivatestaticclassTwoDigitNumberFieldimplementsNumberRule{...  

Override    publicvoidappendTo(finalAppendablebuffer,finalCalendarcalendar)throwsIOException{      appendTo(buffer,calendar.get(mField));    }    /***{

inheritDoc}*/    

Override    publicfinalvoidappendTo(finalAppendablebuffer,finalintvalue)throwsIOException{      if(value00){        appendDigits(buffer,value);      }else{        appendFullDigits(buffer,value,);      }    }...}

这样就得到了"07-0-:00:00"

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

方法描述

格式化日期部分(不包括时间)br格式yyyy-MM-dd

源码分析一

/***格式化日期部分(不包括时间)br*格式yyyy-MM-dd**

paramdate被格式化的日期*

turn格式化后的字符串*/publicstaticStringformatDate(Datedate){if(null==date){turnnull;}turnDatePattern.NORM_DATE_FORMAT.format(date);}

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

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

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

FastDateFormat是一个线程安全的实现来源ApacheCommonsLang.5

  /***标准日期格式{

linkFastDateFormat}:yyyy-MM-dd*/  publicstaticfinalFastDateFormatNORM_DATE_FORMAT=FastDateFormat.getInstance(NORM_DATE_PATTERN);

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

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

方法描述

格式化时间br格式HH:mm:ss

源码分析一

/***格式化时间br*格式HH:mm:ss**

paramdate被格式化的日期*

turn格式化后的字符串*

since.0.*/publicstaticStringformatTime(Datedate){if(null==date){turnnull;}turnDatePattern.NORM_TIME_FORMAT.format(date);}

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

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

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

FastDateFormat是一个线程安全的实现来源ApacheCommonsLang.5

  /***标准时间格式{

linkFastDateFormat}:HH:mm:ss*/  publicstaticfinalFastDateFormatNORM_TIME_FORMAT=FastDateFormat.getInstance(NORM_TIME_PATTERN);

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

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

方法描述

格式化为Http的标准日期格式br标准日期格式遵循RFC规范,格式类似于:Fri,Dec:59:59GMT

源码分析一

/***格式化为Http的标准日期格式br*标准日期格式遵循RFC规范,格式类似于:Fri,Dec:59:59GMT**

paramdate被格式化的日期*

turnHTTP标准形式日期字符串*/publicstaticStringformatHttpDate(Datedate){if(null==date){turnnull;}turnDatePattern.HTTP_DATETIME_FORMAT.format(date);}

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

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

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

FastDateFormat是一个线程安全的实现来源ApacheCommonsLang.5

  /***HTTP头中日期时间格式{

linkFastDateFormat}:EEE,ddMMMyyyyHH:mm:ssz*/  publicstaticfinalFastDateFormatHTTP_DATETIME_FORMAT=FastDateFormat.getInstance(HTTP_DATETIME_PATTERN,TimeZone.getTimeZone("GMT"),Locale.US);

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

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

方法描述

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

源码分析一

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

paramdate被格式化的日期*

paramisUppercase是否采用大写形式*

paramwithTime是否包含时间部分*

turn中文日期字符串*

since5..9*/publicstaticStringformatChineseDate(Datedate,booleanisUppercase,booleanwithTime){if(null==date){turnnull;}if(false==isUppercase){turn(withTime?DatePattern.CHINESE_DATE_TIME_FORMAT:DatePattern.CHINESE_DATE_FORMAT).format(date);}turnCalendarUtil.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)

/***标准日期格式{

linkFastDateFormat}:yyyy年MM月dd日HH时mm分ss秒*/publicstaticfinalFastDateFormatCHINESE_DATE_TIME_FORMAT=FastDateFormat.getInstance(CHINESE_DATE_TIME_PATTERN);  /***标准日期格式{

linkFastDateFormat}:yyyy年MM月dd日*/  publicstaticfinalFastDateFormatCHINESE_DATE_FORMAT=FastDateFormat.getInstance(CHINESE_DATE_PATTERN);

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

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

FastDateFormat是一个线程安全的实现来源ApacheCommonsLang.5

  /***HTTP头中日期时间格式{

linkFastDateFormat}:EEE,ddMMMyyyyHH:mm:ssz*/  publicstaticfinalFastDateFormatHTTP_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(Calendarcalendar,booleanwithTime):将指定Calendar时间格式化为纯中文形式,例如

08-0-::转换为二〇一八年二月二十四日(withTime为false)08-0-::转换为二〇一八年二月二十四日一十二时一十三分一十四秒(withTime为true)

CalendarUtil.calendar(date)源码分析:

/***转换为Calendar对象**

paramdate日期对象*

turnCalendar对象*/publicstaticCalendarcalendar(Datedate){if(dateinstanceofDateTime){turn((DateTime)date).toCalendar();}else{turncalendar(date.getTime());}}

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

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

/***转换为Calendar对象**

parammillis时间戳*

turnCalendar对象*/publicstaticCalendarcalendar(longmillis){finalCalendarcal=Calendar.getInstance();cal.setTimeInMillis(millis);turncal;}

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

//CalendarpublicfinalvoidsetTime(Datedate){setTimeInMillis(date.getTime());}

本质上是一样的。。setTime方法里也是会调用setTimeInMillis方法。

CalendarUtil.formatChineseDate(Calendarcalendar,booleanwithTime)源码分析:

/***将指定Calendar时间格式化为纯中文形式,比如:**p*08-0-::转换为二〇一八年二月二十四日(withTime为false)*08-0-::转换为二〇一八年二月二十四日一十二时一十三分一十四秒(withTime为true)*/p**

paramcalendar{

linkCalendar}*

paramwithTime是否包含时间部分*

turn格式化后的字符串*

since5..9*/publicstaticStringformatChineseDate(Calendarcalendar,booleanwithTime){finalStringBuildersult=StrUtil.builder();//年Stringyear=String.valueOf(calendar.get(Calendar.YEAR));finalintlength=year.length();for(inti=0;ilength;i++){sult.append(NumberChineseFormatter.numberCharToChinese(year.charAt(i),false));}sult.append(年);//月intmonth=calendar.get(Calendar.MONTH)+;sult.append(NumberChineseFormatter.format(month,false));sult.append(月);//日intday=calendar.get(Calendar.DAY_OF_MONTH);sult.append(NumberChineseFormatter.format(day,false));sult.append(日);if(withTime){//时inthour=calendar.get(Calendar.HOUR_OF_DAY);sult.append(NumberChineseFormatter.format(hour,false));sult.append(时);//分intminute=calendar.get(Calendar.MINUTE);sult.append(NumberChineseFormatter.format(minute,false));sult.append(分);//秒intsecond=calendar.get(Calendar.SECOND);sult.append(NumberChineseFormatter.format(second,false));sult.append(秒);}turnsult.toString().place(零,〇);}

由源码可知,是按年月日时分秒,每个字段进行转化。

,

1
查看完整版本: 时间工具类格式化时间万字长文