Java 中的时间处理
共 5228字,需浏览 11分钟
·
2021-02-02 02:30
最初学习 Java 时是采用学校教材和市面上的一些 Java 视频教程进行学习,到了工作中才发现有些 API 或包已经过时了。最近使用 Java 中的日期处理时发现 java.util.date 与 Calender 相关包官方已经逐渐弃用,因此总结一下目前 Java 中的日期处理方式。
首先先了解一下为什么 Java 8 之前的日期处理方式为什么难用。对大多数初学者最熟悉的 日期处理方式就是 Java 1中的 java.util.Date 类,这个类年份起始选择是 1900 年,月份起始从 0 开始。
Date date = new Date( 2020, 2, 18);
它的打印效果如下:
Tue Mar 18 00: 00: 00 CET 2020
但它并不直观,他默认返回计算机格式的日期。你需要将它转换为我们容易获取的特定格式,需要使用 java.text.SimpleDateFormat 类进行格式转化,但这个类不是线程安全的,多线程格式转化需要使用同步代码块加锁解决线程安全问题。
随后 Java 1.1 出现了 java.util.Calender 类,但 Calender 类也有涉及缺陷,比如月份也是从 0 开始。许多新手在选择日期处理方式时会不知道用哪种,新手在使用 Date 与 Calender 的过程中会出现各种难以预料的错误,调试搜索后才会发现原来是设计上的缺陷。
直到 Java 8 才出现了目前正在使用的新的日期和时间库 java.time.xxx。
Java 中的最新的日期处理方式
目前网上搜索的 Java 日期处理最多的返回结果还是 Date 、Calender 类型的问题,我也是因为踩过日期处理的许多坑才发现自己的知识已经过时,我相信跟我一样的工程师不在少数。
为什么我定义 Java 8 中的日期处理方式是最新的呢,因为 Java 11 等后续版本一直在沿用 Java 8 的日期处理方式。Java 8 中出现了新的日期创建与时间间隔的类。分别是 LocalDate 、LocalTime 、Instant 、Duration 和 Period 。
我将参考 Java 11 的 java.time 文档讲解这些日期处理方式。
使用 LocalDate 、 LocalTime 与 LocalDateTime
LocalDate 存储没有时间的日期。它存储的日期类似于 “2010-12-03” ,可以用来存储生日。
LocalDate 可以创建 ISO-8601日历系统中没有时区的日期,如 2007-12-03。LocalDate 是一个不可变的日期-时间对象,表示日期,通常格式为年-月-日。如下代码创建一个LocalDate 对象并实验其方法。
LocalDate date = LocalDate.of( 2014, 3, 18); // 2014-03-18
int year = date.getYear(); // 2014
Month month = date.getMonth();// MARCH
int day = date.getDayOfMonth();// 18
DayOfWeek dow = date.getDayOfWeek();// TUESDAY
int len = date.lengthOfMonth(); // 31 (三月的天数)
boolean leap = date.isLeapYear(); // false 不是闰年
LocalDate today = LocalDate.now();
// 从系统时钟中获取当前的日期
// 使用TemporalField 读取LocalDate 的值
int year = date.get( ChronoField.YEAR);
int month = date.get( ChronoField.MONTH_OF_YEAR);
int day = date.get( ChronoField.DAY_OF_MONTH);
一天中的时间,可以用 LocalTime 来表示,LocalTime 存储没有日期的时间。这种存储时间格式为 “11:30”,可以用来存储营业的时间。LocalTime 是一个不可变的日期-时间对象,它表示时间,通常被视为小时-分钟-秒。时间以纳秒精度表示。
LocalTime time = LocalTime.of( 13, 45, 20); // 13: 45: 20
int hour = time.getHour(); // 13
int minute = time.getMinute(); // 45
int second = time.getSecond();// 20
LocalDateTime 是 ISO-8601日历系统中不带时区的日期-时间,如2007-12-03T10:15:30。LocalDateTime是一个不可变的日期-时间对象,它表示一个日期-时间,通常被视为年-月-日-时-分-秒。还可以访问其他日期和时间字段,如 day-of-year、day-of-week 和 week-of-year。
// 2014-03-18T13: 45: 20
LocalDateTime dt1 = LocalDateTime.of( 2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of( date, time);
LocalDateTime dt3 = date.atTime( 13, 45, 20);
LocalDateTime dt4 = date.atTime( time);
LocalDateTime dt5 = time.atDate( date);
// 提取日期和时间
LocalDate date1 = dt1. toLocalDate(); // 2014-03-18
LocalTime time1 = dt1. toLocalTime(); // 13: 45: 20
ZonedDateTime 的使用
ZonedDateTime ISO-8601日历系统中带有时区的日期-时间,例如 2007-12-03T10:15:30+01:00 Europe/Paris
. 我们可以从下图看出 LocalDate、LocalTime、LocalDateTime 、ZonedDateTime 的区别。
ZoneId romeZone = ZoneId.of("Europe/ Rome");
LocalDate date = LocalDate.of( 2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay( romeZone);
LocalDateTime dateTime = LocalDateTime.of( 2014, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone( romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone( romeZone);
ZoomId 对应的Map选项可以从官方文档中查看 https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/ZoneId.html
LocalDate 与 LocalTime 与字符串格式转化
LocalDate date = LocalDate.parse("2014-03-18");
LocalTime time = LocalTime.parse("13: 45: 20");
我们可以使用指定的 fomatter 程序格式化日期, java.time.format.DateTimeFormatter 提供了两个方法:一个用于格式化,format(DateTimeFormatter formatter),另一个用于解析,parse(CharSequence text, DateTimeFormatter formatter)。
LocalDate date = LocalDate.now();
String text = date.format(formatter);
LocalDate parsedDate = LocalDate.parse(text, formatter);
LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String text = date.format(formatter);
LocalDate parsedDate = LocalDate.parse(text, formatter);
LocalDate date = LocalDate.of( 2014, 3, 18);
String s1 = date.format( DateTimeFormatter.BASIC_ISO_DATE); //20140318
String s2 = date.format( DateTimeFormatter.ISO_LOCAL_DATE); //2014-03-18
LocalDate date1 = LocalDate.parse(" 20140318", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse(" 2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE);
Duration 与 Period
Duration 一个基于时间的时间量,例如'34.5秒'。
它的 between 方法可以计算两个 LocalDateTime 或 两个 LocalTime 或两个 Instance 之间的间隔。
Duration d1 = Duration.between( time1, time2);
Duration d1 = Duration.between( dateTime1, dateTime2);
Duration d2 = Duration.between( instant1, instant2);
注意 Instance (时间线上的瞬间点)这个类记录时间线上的单个瞬时点,常在应用程序中记录事件时间戳。
// 从系统时钟中获取当前时刻。
Instant.now();
Period 是 ISO-8601日历系统中基于日期的时间量,例如“2年、3个月和4天”。
Period tenDays = Period.between( LocalDate.of( 2014, 3, 8), LocalDate.of( 2014, 3, 18));
Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2,6,1);
创建好 Period 和 Duration 对象后,我们可以使用他们定义好的各种方法查询两个日期之间的时间差,用于后续处理逻辑。
总结
Java 中的日期处理方式目前采用 LocalDate、LocalTime、LocalDateTime,应该逐渐抛弃以前的 Date 与 Calender 方式。
参考资料
《Java 8实战》 。人民邮电出版社.。[英]厄马(Raoul-Gabriel Urma)[意] 弗斯科(Mario Fusco)[英] 米克罗夫特(Alan Mycroft).
Java 11 官方文档