Java8 新特性 —— LocalDateTime,Instant(时间戳),DateTimeFormatter(格式化),Duration和Period(时间对比),时区详解
前言
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
为了解决以上问题,Java 8引入了新的日期时间API。新API具有以下特点:
Java 8日期时间API中主要涉及到以下类和接口:
java.time.LocalDate
:表示一个ISO-8601格式的日期,不包含时间信息java.time.LocalTime
:表示一个ISO-8601格式的时间,不包含日期信息java.time.LocalDateTime
:表示一个ISO-8601格式的日期时间java.time.Instant
:表示从‘1970-01-01T00:00:00Z’开始的秒数(时间戳)java.time.Duration
:表示两个时刻之间的持续时间或时间段java.time.Period
:表示两个日期之间的天数、月数或年数java.time.ZonedDateTime
:表示带时区的日期时间信息java.time.temporal
:用于校正时间java.time.DateTimeFormatter
:用于格式化和解析日期时间信息一、LocalDate、LocalTime以及LocalDateTime
这三者的使用方法其实是如出一辙的,下面我们以LocalDateTime为例,了解一下他们的作用及用法:
@Test
public void test3(){
LocalDateTime now = LocalDateTime.now();//当前时间 yyyy-MM-ddTHH:mm:ss
LocalDateTime localDateTime = LocalDateTime.of(2024, 11, 6, 12, 30, 30);//2024-11-06T12:30:30
LocalDateTime plusHours = localDateTime.plusHours(1);//加上一个小时 -> 2024-11-06T13:30:30
LocalDateTime minusMinutes = localDateTime.minusMinutes(10);//减去10分钟 -> 2024-11-06T12:20:30
int dayOfWeek = localDateTime.getDayOfWeek().getValue();//获取当前时间是一周中的第几天 -> 3
int year = localDateTime.getYear();//获取年 -> 2024
int month = localDateTime.getMonthValue();//获取月份 -> 11
int hour = localDateTime.getHour();//获取小时 -> 12
int minute = localDateTime.getMinute();//获取分钟 -> 30
int second = localDateTime.getSecond();//获取秒 -> 30
}
二、Instant 时间戳
/**
* Instant : 时间戳, 存储了一个从1970-01-01 00:00:00 以来的 秒 和 纳秒 数据
* getEpochSecond() : 获取的是秒
* toEpochMilli() : 获取的是毫秒
* getNano() : 获取的是纳秒
* ofEpochSecond() : 通过秒级时间戳获取Instant
* ofEpochMilli() : 通过毫秒级时间戳获取Instant
*/
@Test
public void test4(){
Instant inst1 = Instant.now();//默认获取 UTC 时区
System.out.println("UTC 时区: " + inst1);//2024-11-06T12:44:24.803617100Z
System.out.println("Instant 秒: " + inst1.getEpochSecond());//1730897064
System.out.println("Instant 毫秒: " + inst1.toEpochMilli());//1730897064803
System.out.println("当前时间戳(毫秒): " + System.currentTimeMillis());//1730897064808
System.out.println("Instant 纳秒: " + inst1.getNano());//803617100
//根据指定时间戳获取 Instant 对象, 默认UTC时区
Instant ods1 = Instant.ofEpochSecond(1730897056);//2024-11-06T12:44:16Z
System.out.println("指定时间戳获取Instant: " + ods1);
//转换成指定时区,或者当前时区
OffsetDateTime ods2 = ods1.atOffset(ZoneOffset.ofHours(8));//2024-11-06T20:44:16+08:00
System.out.println("时区加8:" + ods2);
ZonedDateTime ods3 = ods1.atZone(ZoneId.systemDefault());//2024-11-06T20:44:16+08:00[Asia/Shanghai]
System.out.println("时区加8:" + ods3);
}
效果图如下:
需要注意的是Instant 不管是通过Now()获取当前时间,还是通过ofEpochSecond()和ofEpochMilli()都是UTC时区的时间,后续需要结合时区使用。
三、ZonedDateTime 带时区的时间
1、ZoneId
返回带时区的时间,如:2024-11-06T21:12:53.794258+08:00[Asia/Shanghai]
2、ZoneOffset
指定时间偏移量,如:2024-11-06T19:12:53.794258+06:00
@Test
public void test8(){
System.out.println(ZonedDateTime.now());//返回系统时区的带时区的时间
System.out.println(ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()));//返回系统时区的带时区的时间
System.out.println(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));//返回Asia/Shanghai 时区当前时间
System.out.println(LocalDateTime.now(ZoneId.of("Africa/Nairobi")));//返回Africa/Nairobi 时区当前时间
System.out.println(LocalDateTime.now(ZoneId.systemDefault()));//返回系统时区的时间
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));//返回Asia/Shanghai 时区当前时间
ZonedDateTime zonedDateTime = now.atZone(ZoneId.of("Asia/Shanghai"));//返回Asia/Shanghai 时区当前带时区时间(与UTC相比)
System.out.println(now);
System.out.println(zonedDateTime);
LocalDateTime now1 = LocalDateTime.now(ZoneOffset.ofHours(6));//返回偏移后的时间
ZonedDateTime zonedDateTime1 = now1.atZone(ZoneOffset.ofHours(6));//返回带偏移量的时间(与UTC相比)
System.out.println(now1);
System.out.println(zonedDateTime1);
}
四、DateTimeFormatter 格式化
同样在Java8中也提供了专业的时间转换类,其中也内置了许多特定的格式,也可以自定义格式
– ofPattern():指定格式
//DateTimeFormatter: 格式化
@Test
public void test7(){
LocalDateTime localDateTime = LocalDateTime.of(2024, 12, 24,20, 40, 40);//2024-12-24T20:40:40
System.out.println(localDateTime);
// 官方API
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ISO_DATE;
String sdt1 = localDateTime.format(dateTimeFormatter1);
System.out.println(sdt1);//2024-12-24
// 自定义
DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String sdt2 = localDateTime.format(dateTimeFormatter2);
System.out.println(sdt2);//2024年12月24日 20:40:40
//字符串转时间
LocalDateTime dt = LocalDateTime.parse(sdt2, dateTimeFormatter2);
System.out.println(dt);//2024-12-24T20:40:40
}
五、TemporalAdjuster 时间校正器
相对于上文时区的大范围校正时间,以及LocalDateTime中加减时间实现时间校正,TemporalAdjuster 就显得较为专业,可以返回指定日子的时间,当然也可以指定模式返回校正后的时间:
//时间校正器 TemporalAdjuster
@Test
public void test6(){
LocalDateTime dt1 = LocalDateTime.of(2024, 12, 24,20, 40, 40);//2024-12-24T20:40:40
System.out.println("当前时间: " + dt1);
//将时间调整到下个周日
LocalDateTime dt2 = dt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("获取下个周日: " + dt2);
//自定义获取下一个工作日
LocalDateTime dt3 = dt1.with(temporal -> {
LocalDateTime localDateTime = (LocalDateTime) temporal;
DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
return localDateTime.plusDays(3);
} else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
return localDateTime.plusDays(2);
} else {
return localDateTime.plusDays(1);
}
});
System.out.println("获取下一个工作日: " + dt3);
}
六、Duration和Period 计算时间差
Duration:时间层间计算时间差
Period:日期层面计算时间差
//计算时间间隔或者日期间隔
// Duration 计算时间间隔
// Period 计算日期间隔
@Test
public void test5() throws InterruptedException {
LocalDateTime dt1 = LocalDateTime.of(2024, 11, 1,12, 30, 30);
LocalDateTime dt2 = LocalDateTime.of(2025, 12, 24,20, 40, 40);
Duration duration = Duration.between(dt1, dt2);
System.out.println("Duration 展示毫秒秒间隔:" + duration.toMillis());//36144610000
System.out.println("Duration 展示秒间隔:" + duration.toSeconds());//36144610
System.out.println("Duration 展示分钟间隔:" + duration.toMinutes());//602410
System.out.println("Duration 展示小时间隔:" + duration.toHours());//10040
System.out.println("Duration 展示天数间隔:" + duration.toDays());//418
LocalDate localDate1 = LocalDate.of(2024, 11, 1);
LocalDate localDate2 = LocalDate.of(2025, 12, 24);
Period period = Period.between(localDate1, localDate2);
System.out.println("Period 展示天数间隔: " + period.getDays());//23
System.out.println("Period 展示月份间隔: " + period.getMonths());//1
System.out.println("Period 展示年间隔: " + period.getYears());//1
}
需要注意的是:
Duration 是精确的时间段的比较,而Period 只是相同字段的数值相比较
作者:重生之绝世牛码