一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
一、前言
在 Java 开发过程中,肯定会有遇到时间操作的场景。Java8 之前可能还在纠结使用 java.util.Date
还是java.sql.Date
包的 Date 类,但在 Java8 后,新增了LocalTime
、LocalDate
和 LocalDateTime
,日期和时间的处理变得更加方便和容易。由于使用还不熟悉,每次使用都要百度,故将 Java8 关于日期时间的相关使用做学习总结,方便后期查阅使用,同时也会将 Java8 其他的特性进行总结,提升开发效率。
二、为什么在 Java8 中引入新的时间和日期库
- Java 的
java.util.Date
和java.util.Calendar
类易用性差,不支持时区,而且他们都不是线程安全的; - 用于格式化日期的类
DateFormat
被放在java.text
包中,它是一个抽象类,所以我们需要实例化一个SimpleDateFormat
对象来处理日期格式化,并且DateFormat
也是非线程安全,这意味着如果你在多线程程序中调用同一个DateFormat
对象,会得到意想不到的结果; - 对日期的计算方式繁琐,而且容易出错,因为月份是从0开始的,从Calendar 中获取的月份需要加一才能表示当前月份;
由于以上这些问题,出现了一些第三方的日期处理框架,例如 Joda-Time
,date4j
等开源项目。但是,Java需要一套标准的用于处理时间和日期的框架,于是Java 8中引入了新的日期API,明确了日期时间概念,例如:瞬时(instant)、期间(duration)、日期、时间、时区和周期。新的日期API是 JSR-310
规范的实现,Joda-Time
框架的作者正是 JSR-310
的规范的倡导者,所以能从Java 8 的日期API中看到很多 Joda-Time
的特性。
三、介绍
java.time类图介绍
在 rt.jar 包的 java.time 下的类结构图:
可以看到,除了一些日期、时间类之外,还有四个包:chrono
、format
、temporal
、zone
。先简略介绍下这四个包的用途。
chrono
chrono 包提供历法相关的接口与实现。Java中默认使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366天。闰年的定义是:非世纪年,能被4整除;世纪年能被400整除。为了计算的一致性,公元1年的前一年被当做公元0年,以此类推。此外 chrono 包提供了四种其他历法,每种历法有自己的纪元(Era)类、日历类和日期类,分别是:
- 泰国佛教历:ThaiBuddhistEra、ThaiBuddhistChronology 和 ThaiBuddhistDate;
- 民国历:MinguoEra、MinguoChronology 和 MinguoDate;
- 日本历:JapaneseEra、JapaneseChronology 和 JapaneseDate
- 伊斯兰历:HijrahEra、HijrahChronology 和 HijrahDate:
每个纪元类都是一个枚举类,实现Era接口。Era表示的是一个时间线的分割,比如 Java 默认的 ISO 历法中的 IsoEra,就包含两个枚举量:BCE 和 CE,前者表示“公元前”,后者表示“公元”;再比如 MinguoEra,包含了两个枚举量:BEFORE_ROC 和 ROC,ROC的意思是Republic of China,也即新中国,前者表示的就是新中国之前,也即民国,后者表示新中国;所以中国的历法用了“Minguo”这个名字。每种历法的日历系统的实现都是依赖于其纪元的。每个日历类都实现了抽象类 AbstractChronology
,其中定义了从时间、id、地域设置获取具体日历系统的接口和实现,以及获取特定日历系统下的时间的方法。定义了纪元和日历系统之后,日期类自然就确定好了,每种历法的日期类提供的接口并无大的不同,在实际开发中应用的比较少。
format
format 包提供了日期格式化的方法。format 包中定义了时区名称、日期解析和格式化的各种枚举,以及最为重要的格式化类 DateTimeFormatter
。需要注意的是,format 包类中的类都是 final 的,都提供了线程安全的访问。在DateTimeFormatter
类中提供了 ofPattern 的静态方法来获得一个 DateTimeFormatter
,但细看其实现,其实还是调用的 DateTimeFormatterBuilder
的静态方法:DateTimeFormatterBuilder.appendPattern(pattern).toFormatter();
所以我们在实际格式化日期和时间的时候,是两种方式都可以使用的。
temporal
temporal
包中定义了整个日期时间框架的基础:各种时间单位、时间调节器,以及在年月日时分秒中用到的各种属性。Java8中的日期时间类都是实现了 temporal
包中的时间单位(Temporal
)、时间调节器(TemporalAdjuster
)和各种属性的接口,所以在后面的日期的操作方法中都是以最基本的时间单位和各种属性为参数的。
zone
定义了时区转换的各种方法。
四、使用
- Instant——表示时间戳,瞬时实例;
- LocalDate——不包含具体时间的日期,比如 2020-01-14。它可以用来存储生日,周年纪念日,入职日期等。
- LocalTime——它代表的是不含日期的时间
- LocalDateTime——它包含了日期及时间,不过还是没有偏移信息或者说时区。
- ZonedDateTime——这是一个包含时区的完整的日期时间,偏移量是以 UTC / 格林威治时间为基准的。 详细看:github.com/tyronczt/ja…