Java8引入了新的时间格式Api:LocalDateTime
、LocalDate
和LocalTime
,分别用于处理日期-时间、日期和时间。使用他们,我门可以很方便的把日期转成String
或者把String
解析成日期。下面我们深入探究一下对于String
转LocalDateTime
,java提供的三种模式。
以下以LocalDateTime
为例:
对于String
转日期,平时用的最多的就是LocalDateTime
的parse
方法。
public static LocalDateTime parse(CharSequence text) {
return parse(text, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.parse(text, LocalDateTime::from);
}
复制代码
parse
方法提供两个重载方法,其实最后调用的都是parse(CharSequence text, DateTimeFormatter formatter)
,这里的关键就是DateTimeFormatter
,当我们只传text的时候,默认的解析规则是:DateTimeFormatter.ISO_LOCAL_DATE_TIME
,他的格式如下所示:
/**
* The ISO date-time formatter that formats or parses a date-time without
* an offset, such as '2011-12-03T10:15:30'.
* The returned formatter has a chronology of ISO set to ensure dates in
* other calendar systems are correctly converted.
* It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
*/
public static final DateTimeFormatter ISO_LOCAL_DATE_TIME;
static {
ISO_LOCAL_DATE_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(ISO_LOCAL_DATE)
.appendLiteral('T')
.append(ISO_LOCAL_TIME)
.toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
}
复制代码
也就是yyyy-MM-ddTHH:mm:ss
(这里有个关键点就是方法最后有个参数ResolverStyle.STRICT
,这就是严格模式)。这种数据格式是ECMAScript基于ISO 8601扩展格式的简化,在国外比较常见。我们平时使用最多的是yyyy-MM-dd HH:mm:ss
这个格式,比较方便前端展示。如果要转换成这种格式,那应该怎么办呢?
我们可以自定义一个DateTimeFormatter
来完成。
LocalDateTime parse = LocalDateTime.parse( "2021-02-23 08:02:02", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//输出结果
2021-02-23T08:02:02
LocalDateTime parse = LocalDateTime.parse( "2021-02-29 08:02:02", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//输出结果
2021-02-28T08:02:02
复制代码
细心的朋友可能已经发现了,我传的是02-29,为什么结果是02-28呢,这里就牵扯本文所说的解析模式了。首先我们看一下DateTimeFormatter.ofPattern
的源码:
public static DateTimeFormatter ofPattern(String pattern) {
return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
}
public DateTimeFormatter toFormatter() {
return toFormatter(Locale.getDefault(Locale.Category.FORMAT));
}
public DateTimeFormatter toFormatter(Locale locale) {
return toFormatter(locale, ResolverStyle.SMART, null);
}
复制代码
关键的地方就是 toFormatter
,依次进入方法可以看到最后传入了一个默认参数:ResolverStyle.SMART
,顾名思义,smart就是智能模式,ResolverStyle
提供:STRICT(严格),SMART(智能),LENIENT(宽松)三种模式对日期进行解析,下面我们详细讨论下三种模式的不同。
严格模式
我们首先定义一个严格的日期解析规则,然后用它解析日期。
/**
* 自定义严格日期格式:yyyy-MM-dd HH:mm:ss
**/
public static DateTimeFormatter STRICT_DATE_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(ISO_LOCAL_DATE)
.appendLiteral(" ")
.append(ISO_LOCAL_TIME)
.toFormatter(Locale.CHINESE)
.withResolverStyle(ResolverStyle.STRICT);
LocalDateTime normal = LocalDateTime.parse( "2021-04-30 08:02:02", STRICT_DATE_TIME);
// 输出结果
2021-04-30T08:02:02
LocalDateTime normal = LocalDateTime.parse( "2021-04-31 08:02:02", STRICT_DATE_TIME);
// 输出结果
Caused by: java.time.DateTimeException: Invalid date 'APRIL 31'
复制代码
由上面代码可以得知:对于不合法的日期,严格模式直接抛出异常。大家也可以改变一下其他字段,最后会发现,必须严格按照时间日期的取值,否则转换都会抛出异常。
智能模式
/**
* 自定义智能日期格式:yyyy-MM-dd HH:mm:ss
**/
public static DateTimeFormatter SMART_DATE_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(ISO_LOCAL_DATE)
.appendLiteral(" ")
.append(ISO_LOCAL_TIME)
.toFormatter(Locale.CHINESE);//默认智能模式
LocalDateTime normal = LocalDateTime.parse( "2021-02-29 18:02:02", SMART_DATE_TIME);
//输出结果
2021-02-28T18:02:02
LocalDateTime unnormal = LocalDateTime.parse( "2021-04-31 08:02:02", SMART_DATE_TIME);
//输出结果
2021-04-30T08:02:02
复制代码
对于不合法的日期,智能模式会自动把数据转换成最近的有效日期,但是要注意不合法日期也是有规则的,超出个字段的取值范围也会有问题。你搞个”2021-02-32″这个格式还是会抛出异常DateTimeException
。
宽松模式
public static DateTimeFormatter LENIENT_DATE_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(ISO_LOCAL_DATE)
.appendLiteral(" ")
.append(ISO_LOCAL_TIME)
.toFormatter(Locale.CHINESE)
.withResolverStyle(ResolverStyle.LENIENT);
LocalDateTime normal = LocalDateTime.parse( "2021-02-32 18:02:02", LENIENT_DATE_TIME);
//输出结果
2021-03-04T18:02:02
LocalDateTime unnormal = LocalDateTime.parse( "2021-13-31 08:02:02", LENIENT_DATE_TIME);
//输出结果
2022-01-31T08:02:02
复制代码
宽松模式对数据的要求就比较宽松了,只要是数字一般都会解析成功,他会自动把不合法的日期转为有效日期,比如说,13就会转成12+1,这样2021年就变成了了2022年,天和时分秒同理。
综上所述,我们可以总结出:
STRICT | 对于时间格式要求最严格,强制合法时间才能解析 |
---|---|
SMART | 对于有效期内的日期字段,可以解析为最近的有效日期,常用与处理闰年2月份,和跨月时间格式问题 |
LENIENT | 对于时间格式要求最宽松,只要是数字就能解析,而且会自动转换为有效日期,但是转换后的日期与之前相差较大,慎用!!! |