共计 5336 个字符,预计需要花费 14 分钟才能阅读完成。
Java8 新特性的功能已经更新了不少篇幅了,今天重点讲解时间日期库中 DateTime 相关处理。同样的,如果你现在依旧在项目中使用传统 Date、Calendar 和 SimpleDateFormat 等 API 来处理日期相关操作,这篇文章你一定不要错过。来刷新你的知识库吧!
背景
Java 对日期、日历及时间的处理一直以来都饱受诟病,比如 java.util.Date 和 java.util.Calendar 类易用性差,不支持时区,非线程安全;还有用于格式化日期的类 DateFormat 也是非线程安全的等问题。
Java8 引入的新的一系列 API,对时间日期的处理提供了更好的支持,清楚的定义了时间日期的一些概念,比如说,瞬时时间(Instant), 持续时间(duration),日期(date), 时间(time),时区(time-zone)以及时间段(Period)。
同时,借鉴了 Joda 库的一些优点,比如将人和机器对时间日期的理解区分开的。
简介
新的时间日期 API 核心位于 java.time 内,另外也在 java.time.chrono,java.time.format,java.time.temporal 和 java.time.zone 有相关的 API,但使用频次较少。
Java8 常用的日期和时间类包含 LocalDate、LocalTime、Instant、Duration、Period、LocalDateTime 以及 ZonedDateTime 等。
- LocalDate:不包含时间的日期,比如 2019-10-14。可以用来存储生日,周年纪念日,入职日期等。
- LocalTime:与 LocalDate 想对照,它是不包含日期的时间。
- LocalDateTime:包含了日期及时间,没有偏移信息(时区)。
- ZonedDateTime:包含时区的完整的日期时间,偏移量是以 UTC/ 格林威治时间为基准的。
- Instant:时间戳,与 System.currentTimeMillis() 类似。
- Duration:表示一个时间段。
- Period:用来表示以年月日来衡量一个时间段。
另外,还有新的日期解析格式化类 DateTimeFormatter。
学习最佳的途径就是去实践它,现在我们示例的形式来讲每个知识点进行讲解。
LocalDate- 如何获得日期
LocalDate 类内只包含日期,不包含具体时间。只需要表示日期而不包含时间,就可以使用它。
// 只获取日期
LocalDate today = LocalDate.now();
System.out.println(today);
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
System.out.printf("Year : %d Month : %d day : %d \t %n", year, month, day);
同时,还可以通过 LocalDate 获取日期是月份的第几天、周的第几天,月份的天数,是否为闰年等。看下面的代码是不是非常方便。
LocalDate today = LocalDate.now();
// 月份中的第几天
int dayOfMonth = today.getDayOfMonth();
// 一周的第几天
DayOfWeek dayOfWeek = today.getDayOfWeek();
// 月份的天数
int length = today.lengthOfMonth();
// 是否为闰年
boolean leapYear = today.isLeapYear();
上面通过 now 获取 LocalDate 对象,也可以通过静态方法 of() 或 parse 创建任意一个日期。再也不用像之前一样年只能从 1900 年开始,月必须从 0 开始等。
LocalDate oneDay = LocalDate.of(2019,10,1);
System.out.println(oneDay);
LocalDate parseDay = LocalDate.parse("2019-10-01");
System.out.println(parseDay);
打印结果:2019-10-01。
LocalDate 重写了 equals 方法,让日期的比较也变得简单了。
// 定义任意日期
LocalDate oneDay = LocalDate.of(2019, 10, 1);
System.out.println(oneDay);
// 定义任意比较
LocalDate anyDay = LocalDate.of(2019, 10, 1);
System.out.println(oneDay.equals(anyDay));
同时,针对日期还可延伸出 MonthDay 或 YearMonth 类,顾名思义,只包含月天或年月。同样适用于 equals 方法来比较。
另外使用 before 和 after 可以比较两个日期前后时间。
boolean notBefore = LocalDate.parse("2019-10-01").isBefore(LocalDate.parse("2019-10-02"));
boolean isAfter = LocalDate.parse("2019-10-01").isAfter(LocalDate.parse("2019-10-02"));
对日期进行前一天后一天或前一个月的加减也变得十分方便。
LocalDate tomorrowDay = LocalDate.now().plusDays(1);
LocalDate nextMonth = LocalDate.now().plusMonths(1);
还有,我们在实战的时候往往要获取某一天的开始时间和当天所在月的第一天等。
LocalDate.now().atStartOfDay();
LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
LocalTime- 如何获取时间
LocalTime 和 LocalDate 类似,区别在于 LocalDate 不包含具体时间,而 LocalTime 包含具体时间。同样可以使用 now 或 of 方法来获得对象。
LocalTime localTime = LocalTime.now();
LocalTime oneTime = LocalTime.of(10,10,10);
LocalDate 类似它也拥有 parse、isBefore、获取时间单元等方法,就不再赘述。
需要注意的是,LocalTime 获得的时间格式为:11:41:58.904。也就是,HH:mm:ss.nnn,这里 nnn 是纳秒。
还有一个在实战中查询日期区间时我们经常定义的“23:59:59.99”常量再也不用自己定义了。
// 23:59:59.999999999
LocalTime maxTime = LocalTime.MAX;
// 00:00
LocalTime minTime = LocalTime.MIN;
LocalDateTime- 日期和时间的组合
LocalDateTime 表示日期和时间组合。可以通过 of() 方法直接创建,也可以调用 LocalDate 的 atTime() 方法或 LocalTime 的 atDate() 方法将 LocalDate 或 LocalTime 合并成一个 LocalDateTime。
创建时间示例:
LocalDateTime now = LocalDateTime.now();
LocalDateTime oneTime = LocalDateTime.of(2019,10,14,10,12,12);
// 拼接日期
LocalTime.now().atDate(LocalDate.now());
LocalDateTime 与 LocalDate 和 LocalTime 之间可以相互转化。其他日期增减等操作与上面的类似。
Instant- 获取时间戳
Instant 用于一个时间戳,与 System.currentTimeMillis() 类似,但 Instant 可以精确到纳秒(Nano-Second)。
Instant 除了可以使用 now() 方法创建,还可以通过 ofEpochSecond 方法创建。
Instant now = Instant.now();
Instant.ofEpochSecond(365 * 24 * 60, 100);
其中 ofEpochSecond 第一个参数表示秒,第二个参数表示纳秒。整体表示:从 1970-01-01 00:00:00 开始后的 365 天 100 纳秒的时间点。
Duration- 获取时间段
Duration 的内部实现与 Instant 类似,但 Duration 表示时间段,通过 between 方法创建。
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration = Duration.between(from, to);
// 区间统计换算
// 总天数
long days = duration.toDays();
// 小时数
long hours = duration.toHours();
// 分钟数
long minutes = duration.toMinutes();
// 秒数
long seconds = duration.getSeconds();
// 毫秒数
long milliSeconds = duration.toMillis();
// 纳秒数
long nanoSeconds = duration.toNanos();
Duration 对象还可以通过 of() 方法创建,该方法参数为时间段长度和时间单位。
// 7 天
Duration duration1 = Duration.of(7, ChronoUnit.DAYS);
// 60 秒
Duration duration2 = Duration.of(60, ChronoUnit.SECONDS);
Period- 获取日期段
Period 与 Duration 类似,获取一个时间段,只不过单位为年月日,也可以通过 of 方法和 between 方法创建,between 方法接收的参数为 LocalDate。
Period period = Period.of(1, 10, 25);
Period period1 = Period.between(LocalDate.now(), LocalDate.now().plusYears(1));
ZonedDateTime- 创建时区时间
ZonedDateTime 类,用于处理带时区的日期和时间。ZoneId 表示不同的时区。大约有 40 不同的时区。
获取所有时区集合:
Set allZoneIds = ZoneId.getAvailableZoneIds();
创建时区:
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
把 LocalDateTime 转换成特定的时区:
ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), zoneId);
另外和时区一起使用的类是 OffsetDateTime 类,OffsetDateTime 是不变的,表示 date-time 偏移,存储所有日期和时间字段,精确至纳秒,从 UTC/Greenwich 计算偏移。
时间日期格式化
Java8 对日期的格式化操作非常简单,首先看到上面的类大多都提供了 parse 方法,可以直接通过解析字符串得到对应的对象。
而日期和时间的格式化可通过 LocalDateTime 的 format 方法进行格式化。
LocalDateTime dateTime = LocalDateTime.now();
String str = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(str);
str = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println(str);
可以使用 DateTimeFormatter 预置的格式,也可以通过 DateTimeFormatter.ofPattern 方法来指定格式。
关键点回顾
Java8 中关于时间日期的 API 有以下关键点:
- 提供了 javax.time.ZoneId 用来处理时区。
- 提供了 LocalDate 与 LocalTime 类。
- 时间与日期 API 中的所有类都是线程安全的。
- 明确定义了基本的时间与日期概念。
- 核心 API:Instant、LocalDate、LocalTime、LocalDateTime、ZonedDateTime。
- DateTimeFormatter 类用于在 Java 中进行日期的格式化与解析。
好了,关于 Java8 新特性的时间日期功能就将到这里,用起来是不是简单明快多了,赶紧在项目中练练手吧。
原文链接:《Java8 新特性时间日期库 DateTime API 及示例》
<center> 程序新视界 :精彩和成长都不容错过 </center>