目录
背景
在1.8以前, 我们通常使用 Date / Calendar 等util类来进行时间的描述与操作, 到目前为止Date还是被广泛使用中, 但是Date类有几点不足:
- 粒度不够细: 最小维度为毫秒, 不支持纳秒级别
- 无时区: Date固定采用TimeZone中给定的默认时区
- 不够便捷: 必须通过Calendar类进行时间操作
- 变量歧义: Calendar中, 描述月份不是从112而是从011; 描述星期时第1天是周日而不是周一;
- 不够抽象: 一个Date为特定的时刻, 无法单纯的描述某一年或某一天
- ...
因此, jdk1.8版本在java.time包下新增了一系列的类, 试图解决以上不足
包结构
java.time包
常用时间处理类包
时间点
类 | 说明 |
Clock | 抽象时钟类 |
├ SystemClock | 系统时钟类,类似System.currentTimeMillis() |
├ FixedClock | 固定时间时钟类,包含时区信息 |
├ OffsetClock | 时差时钟类 |
└ TickClock | 自定义步长时钟类,不足步长时按步长舍入 |
Instant | 时刻类,用于描述某一个纳秒级别精度瞬间,不包含时区信息 |
时间段
类 | 说明 |
Duration | 秒级别精度持续时间 |
Period | 日级别精度持续时间 |
日期
类 | 说明 |
LocalDate | 本地日期类,描述一个固定的日期,不可变类,包含年/月/日 |
LocalTime | 本地时间类,描述一个时间点, 包含时/分/秒/纳秒 |
LocalDateTime | 本地日期时间类, LocalDate与LocalTime的组合 |
OffsetTime | 本地时区时间, 包含时区信息(ZoneOffset)的LocalTime |
OffsetDateTime | 本地时区日期时间, 包含时区信息(ZoneOffset)的LocalDateTime |
Year | 年分类, 专门用来描述某一年 |
YearMonth | 年月类, 专门用来描述某一年的某一个月 |
MonthDay | 月日类, 专门用来描述某一个月的某一天 |
Month | 1到12月的枚举类, 映射的数值分别是1~12 |
DayOfWeek | 周一到周日的枚举值, 映射的数值分别是1~7 |
时区
类 | 说明 |
ZonedDateTime | 区域时间类, 它用来精准的描述某一个Zone区域的时间 |
ZoneId | 抽象时区ID类, 用于标识用于在Instant和LocalDateTime之间转换的规则 |
├ ZoneOffset | 时区偏移量, 用于描述某一个时区相对UTC时间的偏移量, 比如东八区的id为: +8 |
└ ZoneRegion | 地理区域类, 配合ZoneRulesProvider类获取该地区的时间规则(ZoneRules) |
java.time.format包
时间格式化/解析工具包
类 | 说明 |
DateTimeFormatter | 用于打印和解析日期时间对象的格式化程序 |
DateTimeFormatterBuilder | DateTimeFormatter构造器 |
DecimalStyle | 日期和时间格式中使用的数字样式,包括正符号/负符号/零/小数点的样式 |
FormatStyle | 枚举类, 描述以何种模式来格式化日期或时间 |
常用操作
1. 获取当前时间
北京时间:
/*
* 底层实现 Clock.systemUTC().instant()
* 返回自1970/1/1 UTC以来的秒数,结尾Z表示UTC
*/
System.out.println(Instant.now());
System.out.println(LocalDateTime.now());
System.out.println(OffsetDateTime.now());
System.out.println(ZonedDateTime.now());
System.out.println(LocalDate.now());
System.out.println(LocalTime.now());
结果:
2023-05-22T10:47:30.842Z
2023-05-22T18:47:30.913
2023-05-22T18:47:30.913+08:00
2023-05-22T18:47:30.914+08:00[Asia/Shanghai]
2023-05-22
18:47:30.914
2. 字符串转时间
String dateTime = "2023-05-22 13:20:12";
LocalDateTime localDateTime = LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
3. 格式化时间
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.toFormatter();
System.out.println(dateTime.format(dtf));
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
结果:
2023-05-22T12:16:45.725
2023-05-22 12:16:45
4. 获取某个时间域
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
int monthValue = now.getMonthValue();
int dayOfMonth = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nano = now.getNano();
System.out.println(String.format("%d-%d-%d %d:%d:%d.%d", year, monthValue, dayOfMonth, hour, minute, second, nano));
结果:
2023-5-22 17:43:10.730000000
Month month = now.getMonth();
String monthName1 = month.getDisplayName(TextStyle.SHORT, Locale.CHINA);
String monthName2 = month.getDisplayName(TextStyle.SHORT, Locale.PRC);
String monthName3 = month.getDisplayName(TextStyle.SHORT, Locale.CHINESE);
String monthName4 = month.getDisplayName(TextStyle.SHORT, Locale.SIMPLIFIED_CHINESE);
System.out.println(String.format("%s %s %s %s", monthName1, monthName2, monthName3, monthName4));
结果:
五月 五月 五月 五月
Month month = Month.JUNE;
String monthName1 = month.getDisplayName(TextStyle.SHORT, Locale.ENGLISH);
String monthName2 = month.getDisplayName(TextStyle.SHORT_STANDALONE, Locale.ENGLISH);
String monthName3 = month.getDisplayName(TextStyle.NARROW, Locale.ENGLISH);
String monthName4 = month.getDisplayName(TextStyle.NARROW_STANDALONE, Locale.ENGLISH);
String monthName5 = month.getDisplayName(TextStyle.FULL, Locale.ENGLISH);
String monthName6 = month.getDisplayName(TextStyle.FULL_STANDALONE, Locale.ENGLISH);
System.out.println(String.format("%s %s %s %s %s %s", monthName1, monthName2, monthName3, monthName4, monthName5, monthName6));
DayOfWeek dayOfWeek = now.getDayOfWeek();
String dayOfWeekName = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.CHINA);
System.out.println(dayOfWeekName);
结果:
Jun Jun J J June June
星期一
5. 时间比较
LocalDateTime dateTime1 = LocalDateTime.parse("2023-05-01 15:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime dateTime2 = LocalDateTime.now();
System.out.println(dateTime1.isBefore(dateTime2));
结果:
true
6. 时间计算
增减时间
// 增减分秒
LocalDateTime dateTime = LocalDateTime.parse("2023-05-01 15:30:00.100", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.n"));
System.out.println(dateTime);
dateTime = dateTime.plusYears(1);
dateTime = dateTime.minusMonths(1);
dateTime = dateTime.plusDays(1);
dateTime = dateTime.plusHours(1);
dateTime = dateTime.plusMinutes(1);
dateTime = dateTime.plusSeconds(1);
dateTime = dateTime.plusNanos(300);
System.out.println(dateTime);
结果:
2023-05-01T15:30:00.000000100
2024-04-02T16:31:01.000000400
计算时间差
LocalDateTime dateTime1 = LocalDateTime.now();
// 设置时间
dateTime1 = dateTime1
.withYear(2023)
.withMonth(5)
.withDayOfMonth(1)
.withHour(0)
.withMinute(0)
.withSecond(0);
LocalDateTime dateTime2 = LocalDateTime.parse("2023-05-02 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
/*
* 计算 dateTime1 和 dateTime2 时间差
* 2023-05-01 00:00:00.000000400
* 2023-05-02 00:00:00
* 相差不足一天
*/
long days = dateTime1.until(dateTime2, ChronoUnit.DAYS);
System.out.println(String.format("%s - %s = %d days", dateTime1, dateTime2, days));
dateTime1 = dateTime1.withNano(0);
days = dateTime1.until(dateTime2, ChronoUnit.DAYS);
System.out.println(String.format("%s - %s = %d days", dateTime1, dateTime2, days));
结果:
2023-05-01T00:00:00.000000400 - 2023-05-02T00:00 = 0 days
2023-05-01T00:00 - 2023-05-02T00:00 = 1 days
LocalDate date1 = LocalDate.of(2022, 5, 20);
LocalDate date2 = LocalDate.of(2023, 6, 1);
Period between = Period.between(date1, date2);
System.out.println(String.format("diff: %d years, %d months, %d days", between.getYears(), between.getMonths(), between.getDays()));
LocalDateTime time1 = LocalDateTime.of(2023, 5, 20, 8, 30, 20, 100);
LocalDateTime time2 = LocalDateTime.of(2023, 5, 20, 8, 30, 25);
Duration duration = Duration.between(time1, time2);
System.out.println(String.format("diff: %d seconds, %d nanos", duration.getSeconds(), duration.getNano()));
结果:
diff: 1 years, 0 months, 12 days
diff: 4 seconds, 999999900 nanos
时区转换
private final static DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
/**
* 把fromZone时区的时间转换成toZone时区的时间
*/
public static String zoneChange(String time, String fromZone, String toZone) {
return LocalDateTime.parse(time, DATETIME_FORMATTER)
.atZone(ZoneId.of(fromZone))
.withZoneSameInstant(ZoneId.of(toZone))
.format(DATETIME_FORMATTER);
}