快捷搜索:  汽车  科技

java日期操作技巧(Java日期时间类中老三样)

java日期操作技巧(Java日期时间类中老三样)public abstract class DateFormat extends Format { protected Calendar calendar; } public class SimpleDateFormat extends DateFormat { @Override public Date parse(String text ParsePosition pos) { CalendarBuilder calb = new CalendarBuilder(); parsedDate = calb.establish(calendar).getTime(); return parsedDate; } } class CalendarBuilder { Calendar establish(Ca

背景

在 Java8之前,我们处理日期时间需求时,使用 Date、Calender 和 SimpleDateFormat,来声明时间戳、使用日历处理日期和格式化解析日期时间。但是,这些类的 API 的缺点比较明显,比如可读性差、易用性差、使用起来冗余繁琐,还有线程安全问题。

坑点

1、时间格式化的坑,明明是一个 2020 年的日期,怎么使用 SimpleDateFormat 格式化后就提前跨年了?(我昨天的一篇文档也解释过了,也因为这个我的年终奖没了,大家可以翻看下那个YYYY和yyyy的那篇)https://www.toutiao.com/a7047306921803629063/

原因:混淆了 SimpleDateFormat 的各种格式化模式。JDK文档中有说明:小写 y 是年,而大写 Y 是 week year,也就是所在的周属于哪一年。所以务必要看文档,文档是如此的重要,都是细小的知识点。

java日期操作技巧(Java日期时间类中老三样)(1)

https://www.toutiao.com/a7047306921803629063/

2、定义的 static 的 SimpleDateFormat 可能会出现线程安全问题

SimpleDateFormat 的作用是定义解析和格式化日期时间的模式。这,看起来这是一次性的工作,应该复用,但它的解析和格式化操作是非线程安全的。

源码分析:

SimpleDateFormat 继承了 DateFormat,DateFormat 有一个字段 Calendar;SimpleDateFormat 的 parse 方法调用 CalendarBuilder 的 establish 方法,来构建 Calendar;establish 方法内部先清空 Calendar 再构建 Calendar,整个操作没有加锁。显然,如果多线程池调用 parse 方法,也就意味着多线程在并发操作一个 Calendar,可能会产生一个线程还没来得及处理 Calendar 就被另一个线程清空了的情况。

public abstract class DateFormat extends Format { protected Calendar calendar; } public class SimpleDateFormat extends DateFormat { @Override public Date parse(String text ParsePosition pos) { CalendarBuilder calb = new CalendarBuilder(); parsedDate = calb.establish(calendar).getTime(); return parsedDate; } } class CalendarBuilder { Calendar establish(Calendar cal) { ... cal.clear();//清空 for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp ) { for (int index = 0; index <= maxFieldIndex; index ) { if (field[index] == stamp) { cal.set(index field[MAX_FIELD index]);//构建 break; } } } return cal; } }

3、当需要解析的字符串和格式不匹配的时候居然还有结果

比如,我们期望使用 yyyyMM 来解析 20201231 字符串,居然输出了 2022 年 1 月 1 日,原因是把 1231 当成了月份:

String dateString = "20201231"; SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM"); System.out.println("result:" dateFormat.parse(dateString));

java日期操作技巧(Java日期时间类中老三样)(2)

解决方法

对于 SimpleDateFormat 的这三个坑,我们使用 Java 8 中的 DateTimeFormatter 就可以避过去。

  • 坑1的解决方法:

DateTimeFormatterBuilder 来定义格式化字符串,不用去记忆使用大写的 Y 还是小写的 Y,大写的 M 还是小写的 m

  • 坑2的解决方法:

DateTimeFormatter 是线程安全的,可以定义为 static 使用

  • 坑3的解决方法:

DateTimeFormatter 的解析比较严格,需要解析的字符串和格式不匹配时,会直接报错,而不会把1231解析为月份。

java日期操作技巧(Java日期时间类中老三样)(3)

猜您喜欢: