Java 中的时间处理

全栈港

共 5228字,需浏览 11分钟

 · 2021-02-02

最初学习 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 官方文档


浏览 28
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报