一、简介
Java 1.8 引入了全新的日期时间库 java.time
,在介绍新的日期时间库之前,先了解下原来的日期时间工具的诟病。
在 Java SE 8 前,日期时间工具库在 java.util
包中,包括:
java.util.Date
:表示日期和时间java.util.Calendar
以及其实现子类:表示各种日历系统,常用的是格林威治日历java.util.GregorianCalendar
java.util.TimeZone
以及其实现子类:表示时区偏移量和夏令时
以及辅助其进行格式化和解析的工具库在 java.text
包中,包括:
java.text.DateFormat
:格式化日期时间和解析日期时间的工具抽象类java.text.SimpleDateFormat
:DateDateFormat
的实现
下面简要总结一下原有的日期时间在设计上的瑕疵和被开发者无限吐槽的诟病:
- 从以上的api上看,Java 8 之前的日期时间工具库概念较为集中,缺乏年、月、日、时间、星期的单独抽象;
- Date日期时间类既描述日期又描述时间,且Date不仅在
java.util
包中存在,在java.sql
中也存在,名称重复,很容易导致bug发生; - api在设计上比较晦涩,难用,难以用人们自然的思维去理解日期时间。年月日需要从Calendar中获取;
- 最被开发者抱怨的是类型不安全,
Calendar
类中全局属性是可变的,在多线程访问时,会存在线程安全问题。SimpleDateFormat
格式化和解析日期,需要使用年月日时分秒,所以持有了Calendar
属性,导致其也是非线程安全;
二、入门
我们使用新的日期时间库 java.time
首先要接触到的就是 LocalDate
,LocalTime
和 LocalDateTime
,顾名思义,其意思就是本地日期、本地时间 和 本地日期时间。
LocalDate
只包含日期,例如:“2007-12-03”,而 LocalTime
则只包含时间其精确到纳秒值,例如:“12:14:23.267”。相对的 LocalDateTime
其实就是LocalDate
和 LocalTime
的结合体,其包含了日期和时间。
java.time
库利用【?】工厂模式为我们提供了创建这些日期时间的工厂方法,主要分为四类:
- now:根据当前的日期时间来生成,同时我们也可以指定相应的时钟【Clock】或时区ID【ZoneId】,否则就按照本地的时钟或时区生成
- parse:通过指定的日期时间的字符串生成,同时我们也可以指定字符串的格式
- of:通过指定生成时间日期的详细信息生成
- from:相对较为高级了,我们若了解了架构层次其实也不复杂,简而言之就是通过其它日期时间对象来生成当前类型的时间对象
TemporalAccessor
是一个较为顶级的一个接口,如果想提前了解相关信息可以直接访问深入学习 Java 8 全新日期时间库 java.time(三)。
我们以 LocalDate
为例简单的演示一下:
LocalDate date01 = LocalDate.now(); // 2019-08-09
LocalDate date02 = LocalDate.parse("2022-05-02"); // 2022-05-02
LocalDate date03 = LocalDate.of(1995, Month.JULY, 15); // 1995-07-15
LocalDateTime dateTime = LocalDateTime.of(1989, 12, 12, 5, 28);
LocalDate date04 = LocalDate.from(dateTime); // 1989-12-12
LocalDate
,LocalTime
和 LocalDateTime
这三者也都提供了相互转换的方法:
- LocalDate → LocalDateTime
atStartOfDay
:将此日期和午夜时间结合起来创建LocalDateTime
实例atTime
:通过指定详细的时间参数来生成LocalDateTime
实例
- LocalTime → LocalDateTime
atDate
:将时间与指定的日期日期结合起来成LocalDateTime
实例
- LocalDateTime → LocalDate 或 LocalTime
toLocalDate
:将LocalDateTime
转变成LocalDate
toLocalTime
:将LocalDateTime
转变成LocalTime
我们可以利用对应的**get*()
**方法拿到更加详细的信息:
LocalDateTime dateTime = LocalDateTime.now();
int year = dateTime.getYear();
Month month = dateTime.getMonth();
int monthValue = dateTime.getMonthValue();
int dayOfYear = dateTime.getDayOfYear();
int dayOfMoth = dateTime.getDayOfMonth();
DayOfWeek dayOfWeek = dateTime.getDayOfWeek();
int hour = dateTime.getHour();
int minute = dateTime.getMinute();
int second = dateTime.getSecond();
int nano = dateTime.getNano();// 纳秒
相对更加高级点的获取方法:
- int get(TemporalField field):从此日期时间中获取指定字段的int值。
- long getLong(TemporalField field):从此日期时间中获取指定字段的long值。
TemporalField
是一个接口,定义了如何访问 temporal
对象某个字段的值,此处不详细解释 temporal
,其大概意思就是所有时间日期的顶头上司。只有一个枚举 java.time.temporal.ChronoField
实现了此TemporalField
接口。使用起来更加的整洁易懂,但是若获取当前对象不支持的某个字段则会抛出 UnsupportedTemporalTypeException
异常。
dateTime.get(ChronoField.MONTH_OF_YEAR);
dateTime.get(ChronoField.DAY_OF_YEAR);
dateTime.get(ChronoField.DAY_OF_MONTH);
dateTime.get(ChronoField.DAY_OF_WEEK);
同样的类库也为我们提供了**minus*
** 、plus*
和 with*
方法来对应进行 增、减 和 修改 相关参数信息的操作:
LocalDate date = LocalDate.of(2025, 6, 8);
LocalTime time = LocalTime.of(7, 25, 38);
LocalDateTime dateTime = LocalDateTime.of(date, time); // 2025-06-08T07:25:38
LocalDateTime temp1 = dateTime.minusYears(3); // 2022-06-08T07:25:38
LocalDateTime temp2 = dateTime.minusMonths(16); // 2024-02-08T07:25:38
LocalDateTime temp3 = dateTime.minusDays(25); // 2025-05-14T07:25:38
LocalDateTime temp4 = dateTime.plusHours(12); // 2025-06-08T19:25:38
LocalDateTime temp5 = dateTime.plusMinutes(12); // 2025-06-08T07:37:38
LocalDateTime temp6 = dateTime.withSecond(3); // 2025-06-08T07:25:03
相对更加高级点的方法:
minus
(long amountToSubtract, TemporalUnit unit)minus
(TemporalAmount amountToSubtract)plus
(long amountToAdd, TemporalUnit unit)plus
(TemporalAmount amountToAdd)with
(TemporalAdjuster adjuster)with
(TemporalField field, long newValue)- LocalDateTime
- LocalDateTime truncatedTo(TemporalUnit unit)
- LocalTime
- LocalTime truncatedTo(TemporalUnit unit)
由于这些方法设计的面相对广了点,后面我们在对其进行详细的解释!如果想提前了解相关信息可以直接访问 深入学习 Java 8 全新日期时间库 java.time(三)。
类库也为我们提供了比较的方法:【字面意思很好理解】
- boolean isAfter(ChronoLocalDateTime<?> other)
- boolean isBefore(ChronoLocalDateTime<?> other)
- boolean isEqual(ChronoLocalDateTime<?> other)
其它常用方法还有:
- Temporal adjustInto(Temporal temporal):调整指定的temporal【时态】对象,使其具有与此对象相同的日期。【
TemporalAdjuster
接口的实现】 - String format(DateTimeFormatter formatter):使用指定的格式化程序格式化此日期。
- boolean isLeapYear():根据ISO标准的日历系统规则,检查该年是否是闰年。
- boolean isSupported(TemporalField field):检查指定的 field 是否支持。
- boolean isSupported(TemporalUnit unit):检查指定的 unit 是否支持。