技术活,该赏点赞再看,养成习惯
看本篇文章前,建议先对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==dateStrUtil.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==formatnull==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==formatnull==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==formatnull==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==formatnull==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);}//FastDatePrinterOverridepublicStringformat(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(零,〇);}由源码可知,是按年月日时分秒,每个字段进行转化。
,