LocalDate、LocalTime和LocalDateTime
# 52.LocalDate、LocalTime和LocalDateTime
由于Java旧的时间API不好用,市面上出现了不少关于时间的API,例如开源的Joda Time (opens new window),应用广泛;JDK团队邀请Joda Time的作者Stephen Colebourne共同设计了java.time
API,并在Java8引入。
# 前言
主要的类如下:
- 本地日期和时间:
LocalDate
,LocalTime
,LocalDateTime
; -
DateTimeFormatter
:格式化日期,可取代SimpleDateFormat
; - 带时区的日期和时间:
ZonedDateTime
; - 时刻:
Instant
; - 时区:
ZoneId
,ZoneOffset
; - 时间间隔:
Duration
和Period
;
其他改进:
- 严格区分了时刻、本地日期、本地时间和带时区的日期时间
- 提供了方便的日期和时间运算的API
- Month的范围用1~12表示1月到12月 ,Week的范围用1~7表示周一到周日(这才合理嘛!😄)
- 新API的类型几乎全部是不变类型(和String类似),可以放心使用不必担心被修改。
我们首先介绍这3个:
-
LocalDate
,表示本地日期 -
LocalTime
,表示本地时间 -
LocalDateTime
,表示本地日期和时间 - 实际上,看
LocalDateTime
的源码可以发现,LocalDateTime
是由LocalDate
和LocalTime
来表示的
# 入门案例
LocalDate d = LocalDate.now();
LocalTime t = LocalTime.now();
LocalDateTime dt = LocalDateTime.now();
System.out.println(d); //2022-12-22
System.out.println(t); //12:53:28.332
System.out.println(dt); //2022-12-22T12:53:28.332
2
3
4
5
6
7
说明:
- 本地日期和时间通过
now()
获取到的总是以当前默认时区返回的 - 打印的格式和旧的不同,默认严格按照ISO 8601 (opens new window)规定的日期和时间格式进行打印
获取一个某个时间的属性:
- 获得年份:
getYear()
- 获得当前的月份(枚举值):
getMonth()
- 获得月份的数值:
getMonthValue()
- 获得今天是本月的第几天:
getDayOfMonth()
- 获得当前时间的小时:
getHour()
- 获得当前时间的分钟:
getMinute()
- 获得当前时间的秒:
getSecond()
- 获得今天是本年的第几天:
getDayOfYear()
- 获得今天是本周的第几天:
getDayOfWeek()
还可以用get方法获取指定字段的int值:
LocalDateTime ldt = LocalDateTime.of(2022, 1, 1, 2, 3, 4, 56789);//2022-1-1 2:3:1
int i1 = ldt.get(ChronoField.MINUTE_OF_DAY); //123 指的是目前已经过了2*小时 + 3分钟= 123分钟了
int i2 = ldt.get(ChronoField.MINUTE_OF_HOUR); //3 指的是目前在当前这个小时内,已经过了3分钟
2
3
关于ChronoField类,我们下回分解
# 指定时间
我们可以在创建对象的时候,指定时间:
LocalDate d2 = LocalDate.of(2022, 5, 21);
LocalTime t2 = LocalTime.of(13,14, 15);
LocalDateTime dt2 = LocalDateTime.of(2022,5, 21, 13, 14, 15);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);
System.out.println("d2: " + d2); //d2: 2022-05-21
System.out.println("t2: " + t2); //t2: 13:14:15
System.out.println("dt2: " + dt2); //dt2: 2022-05-21T13:14:15
System.out.println("dt3: " + dt3); //dt3: 2022-05-21T13:14:15
2
3
4
5
6
7
8
9
第4行我们用了LocalDate和LocalTime 创建LocalDateTime ,也可以反过来:
LocalDate d3 = dt2.toLocalDate();
LocalTime t3 = dt2.toLocalTime();
System.out.println("d3: " + d3); //d3: 2022-05-21
System.out.println("t3: " + t3); //t3: 13:14:15
2
3
4
还可以通过字符串来创建:
// 通过字符串创建对象
LocalDateTime dt4 = LocalDateTime.parse("2022-5-21T21:13:14");
LocalDate d4 = LocalDate.parse("2022-5-21");
LocalTime t6 = LocalTime.parse("15:16:17");
2
3
4
注意ISO 8601规定的日期和时间分隔符是T
。标准格式如下:
- 日期:yyyy-MM-dd
- 时间:HH:mm:ss
- 带毫秒的时间:HH:mm:ss.SSS
- 日期和时间:yyyy-MM-dd'T'HH:mm:ss
- 带毫秒的日期和时间:yyyy-MM-dd'T'HH:mm:ss.SSS
其他注意事项:
构造方法得是合理的参数值,例如小时的范围是0~23,如果超过(例如设置成52),运行的时候会抛出异常:
Exception in thread "main" java.time.DateTimeException: Invalid value for HourOfDay (valid values 0 - 23): 52
at java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
at java.time.temporal.ChronoField.checkValidValue(ChronoField.java:703)
at java.time.LocalTime.of(LocalTime.java:317)
at TestLocalDateTime.main(TestLocalDateTime.java:17)
2
3
4
5
# 调整日期和时间
可以用withXXX()
调整时间和日期
- 调整年:
withYear()
- 调整月:
withMonth()
- 调整日:
withDayOfMonth()
- 调整时:
withHour()
. 例如:withHour(15)
会把10:11:12
变为15:11:12
: - 调整分:
withMinute()
- 调整秒:
withSecond()
LocalDateTime dt = LocalDateTime.of(2022,5, 21, 13, 14, 15);
dt = dt.withYear(2122);
dt = dt.withHour(05); //调整成5点
System.out.println(dt); //2122-05-21T05:14:15
2
3
4
注意:月份的加减会自动调整日期,例如从10月31日减去一个月,会得到9月30日,因为9月没有31
LocalDateTime
还有一个通用的with()
方法允许我们做更复杂的调整,我们后面会提到。
# 比较时间
由于代码执行需要一定的时间,需要注意分开创建的时间对象是不一样的(1毫秒不同也不行,除非你能保证两者运行的时间差距在1毫秒,一些性能好的计算机可能做到,但我们不能把希望寄托于未知🧐)。
LocalTime t4 = LocalTime.now();
LocalTime t5 = LocalTime.now();
System.out.println(t4.compareTo(t5)); //-1
2
3
我们还可以用isBefore()
、isAfter()
方法来判断,其返回Boolean值
# 日期和时间的加减
LocalDateTime
提供了方便的加减方法:
LocalDateTime dt = LocalDateTime.of(2022,5, 21, 13, 14, 15);
dt = dt.plusDays(5); // sub 5 days
dt = dt.plusDays(1); // add 1 day
dt = dt.plusHours(1); //sub 1 hour
dt = dt.minusHours(3); //add 3 hours
System.out.println(dt); //2022-05-27T11:14:15
2
3
4
5
6
minus
有减法的意思。可以对秒数、分钟、小时、天数、周数、月份、年份进行加减。
可以用链式调用,简化代码:
dt = LocalDateTime.of(2022,5, 21, 13, 14, 15);
dt = dt.plusDays(5).minusDays(1).plusHours(1).minusHours(3);
System.out.println(dt); //2022-05-25T11:14:15
2
3
注意:月份的加减会自动调整日期,例如从10月31日减去一个月,会得到9月30日,因为9月没有31
# 转换时区
LocalDateTime
无法与时间戳进行转换,因为LocalDateTime
没有时区,无法确定某一时刻。
后面我们会介绍ZonedDateTime
,其相当于LocalDateTime
加时区的组合,可以与long
表示的时间戳进行转换。
# 小结
LocalDate
,LocalTime
,LocalDateTime
的常用方法如下:
方法名 | 用处 | 示例 |
---|---|---|
now() | 获取当前默认时区的当前时间 | LocalDate.now(); |
getXXX() | 获取时间的某个字段 | getYear() 获取年份 |
of() | 指定时间 | LocalDate.of(2022, 5, 21); |
parse(String s) | 根据字符串来指定时间 | LocalDate.parse("2022-5-21"); |
withXXX() | 调整时间和日期 | dt.withYear(2122); |
compareTo() isBefore() isAfter() | 比较时间 | t4.compareTo(t5) |
plusXXX(), minusXXX() | 加减时间 | dt.plusDays(5) |