Java 8新特性(7) 时间日期API
在Java 8之前,所有关于时间和日期的API都存在各种使用方面的缺陷,主要有:
- Java 的 java.util.Date 和 java.util.Calendar 类易用性差,不支持时区,而且他们都不是线程安全的;
- 用于格式化日期的类 DateFormat 被放在 java.text 包中,它是一个抽象类,所以我们需要实例化一个 SimpleDateFormat 对象来处理日期格式化,并且 DateFormat 也是非线程安全,这意味着如果你在多线程程序中调用同一个 DateFormat 对象,会得到意想不到的结果。
- 对日期的计算方式繁琐,而且容易出错,因为月份是从 0 开始的,从 Calendar 中获取的月份需要加一才能表示当前月份。
JSR310
以上种种,导致目前有些第三方的 java 日期库诞生,比如广泛使用的 JODA-TIME,还有 Date4j 等,虽然第三方库已经足够强大,好用,但还是有兼容问题的,比如标准的 JSF 日期转换器与 joda-time API 就不兼容,你需要编写自己的转换器,所以标准的 API 还是必须的,于是就有了 JSR310。
JSR 310 实际上有两个日期概念。第一个是 Instant
,它大致对应于 java.util.Date 类,因为它代表了一个确定的时间点,即相对于标准 Java 纪元(1970年1月1日)的偏移量;但与 java.util.Date 类不同的是其精确到了纳秒级别。
第二个对应于人类自身的观念,比如 LocalDate
和 LocalTime
。他们代表了一般的时区概念,要么是日期(不包含时间),要么是时间(不包含日期),类似于 java.sql 的表示方式。此外,还有一个 MonthDay
,它可以存储某人的生日(不包含年份)。每个类都在内部存储正确的数据而不是像 java.util.Date 那样利用午夜 12 点来区分日期,利用 1970-01-01 来表示时间。
目前 Java8 已经实现了 JSR310 的全部内容。新增了 java.time 包定义的类表示了日期-时间概念的规则,包括 instants, durations, dates, times, time-zones and periods。这些都是基于 ISO 日历系统,它又是遵循 Gregorian 规则的。最重要的一点是值不可变,且线程安全,通过下面一张图,我们快速看下 java.time 包下的一些主要的类的值的格式,方便理解。
方法概览
该包的 API 提供了大量相关的方法,这些方法一般有一致的方法前缀:
of
:静态工厂方法。parse
:静态工厂方法,关注于解析。get
:获取某些东西的值。is
:检查某些东西的是否是 true。with
:不可变的 setter 等价物。plus
:加一些量到某个对象。minus
:从某个对象减去一些量。to
:转换到另一个类型。at
:把这个对象与另一个对象组合起来,例如: date.atTime(time)。
与旧的 API 对应关系
Clock
时钟,类似于钟表的概念,提供了如系统时钟、固定时钟、特定时区的时钟
//时钟提供给我们用于访问某个特定 时区的 瞬时时间、日期 和 时间的。
// systemUTC,系统默认UTC时钟(当前瞬时时间 System.currentTimeMillis())
Clock c1 = Clock.systemUTC();
System.out.println(c1.millis()); // 每次调用将返回当前瞬时时间(UTC),1551083737596
// systemDefaultZone,系统默认时区时钟(当前瞬时时间)
Clock c2 = Clock.systemDefaultZone(); // 1551083737682
// system
Clock c31 = Clock.system(ZoneId.of("Europe/Paris")); // 巴黎时区
System.out.println(c31.millis()); // 1551083737684
Clock c32 = Clock.system(ZoneId.of("Asia/Shanghai")); // 上海时区
System.out.println(c32.millis()); // 1551083737684
// fixed,固定时钟
Clock c4 = Clock.fixed(Instant.now(), ZoneId.of("Asia/Shanghai"));//固定上海时区时钟
System.out.println(c4.millis()); // 1551083737684
Thread.sleep(1000);
System.out.println(c4.millis()); // 1551083737684,不变 即时钟时钟在那一个点不动
// offset
Clock c5 = Clock.offset(c1, Duration.ofSeconds(2)); // 相对于系统默认时钟两秒的时钟
System.out.println(c1.millis()); // 1551083738685
System.out.println(c5.millis()); // 1551083740685
Instant
瞬时时间,等价于以前的 System.currentTimeMillis()
Instant instant1 = Instant.now();
System.out.println(instant1.getEpochSecond()); // 1551085125,精确到秒 得到相对于1970-01-01 00:00:00 UTC的一个时间
System.out.println(instant1.toEpochMilli()); // 1551085125192,精确到毫秒
Clock clock1 = Clock.systemUTC();
Instant instant2 = Instant.now(clock1); // 得到时钟的瞬时时间
System.out.println(instant2.toEpochMilli()); // 1551085125193
Clock clock2 = Clock.fixed(instant1, ZoneId.systemDefault()); // 固定瞬时时间时钟
Instant instant3 = Instant.now(clock2); // 得到时钟的瞬时时间
System.out.println(instant3.toEpochMilli()); // 1551085125192,equals instant1
LocalDateTime、LocalDate、LocalTime
// 使用默认时区时钟瞬时时间创建 Clock.systemDefaultZone() -->即相对于 ZoneId.systemDefault()默认时区
LocalDateTime now = LocalDateTime.now(); // 2019-03-10T16:01:04.890
// 自定义时区
LocalDateTime now2 = LocalDateTime.now(ZoneId.of("Europe/Paris")); // 2019-03-10T09:01:04.892
// 自定义时钟
Clock clock = Clock.system(ZoneId.of("Asia/Dhaka"));
LocalDateTime now3 = LocalDateTime.now(clock); // 2019-03-10T14:01:04.906
// 不需要写什么相对时间 如 java.util.Date 年是相对于1900 月是从 0 开始
LocalDateTime d1 = LocalDateTime.of(2013, 12, 31, 23, 59); // 2013-12-31 23:59
// 年月日 时分秒 纳秒
LocalDateTime d2 = LocalDateTime.of(2013, 12, 31, 23, 59, 59, 11);
// 使用瞬时时间 + 时区
Instant instant = Instant.now();
LocalDateTime d3 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); // 2019-03-10T16:01:04.906
// 解析 String --> LocalDateTime
LocalDateTime d4 = LocalDateTime.parse("2013-12-31T23:59"); // 2013-12-31T23:59
// 999毫秒 等价于999000000纳秒
LocalDateTime d5 = LocalDateTime.parse("2013-12-31T23:59:59.999"); // 2013-12-31T23:59:59.999
// 使用 DateTimeFormatter API 解析 和 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime d6 = LocalDateTime.parse("2013/12/31 23:59:59", formatter);
System.out.println(formatter.format(d6)); // 2013/12/31 23:59:59
// 时间获取
System.out.println(d6.getYear()); // 2013
System.out.println(d6.getMonth()); // DECEMBER
System.out.println(d6.getMonthValue()); // 12
System.out.println(d6.getDayOfYear()); // 365
System.out.println(d6.getDayOfMonth()); // 31
System.out.println(d6.getDayOfWeek()); // TUESDAY
System.out.println(d6.getHour()); // 23
System.out.println(d6.getMinute()); // 59
System.out.println(d6.getSecond()); // 59
System.out.println(d6.getNano()); // 0
// 时间增减
LocalDateTime d7 = d6.minusDays(1); // 2013-12-30T23:59:59
// IsoFields.QUARTER_YEARS: 四分之一年
LocalDateTime d8 = d7.plus(1, IsoFields.QUARTER_YEARS); // 2014-03-30T23:59:59
LocalDate
即年月日 无时分秒LocalTime
即时分秒 无年月日
和 LocalDateTime
类似就不演示了
ZonedDateTime
即带有时区的 date-time 存储纳秒、时区和时差(避免与本地date-time歧义)。
API 和 LocalDateTime 类似,只是多了时差(如 2013-12-20T10:35:50.711+08:00[Asia/Shanghai])
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now); // 2019-03-12T14:07:02.143+08:00[Asia/Shanghai]
ZonedDateTime now2= ZonedDateTime.now(ZoneId.of("Europe/Paris"));
System.out.println(now2); // 2019-03-12T07:07:02.146+01:00[Europe/Paris]
// 其他的用法也是类似的 就不介绍了
ZonedDateTime z1 = ZonedDateTime.parse("2013-12-31T23:59:59Z[Europe/Paris]");
System.out.println(z1); // 2013-12-31T23:59:59+01:00[Europe/Paris]
Duration
表示两个瞬时时间的时间段
Duration d1 = Duration.between(Instant.ofEpochMilli(System.currentTimeMillis() - 12323123), Instant.now());
//得到相应的时差
System.out.println(d1.toDays()); // 0
System.out.println(d1.toHours()); // 3
System.out.println(d1.toMinutes()); // 205
System.out.println(d1.toMillis()); // 12323125
System.out.println(d1.toNanos()); // 12323125000000
//1天时差 类似的还有如ofHours()
Duration d2 = Duration.ofDays(1);
System.out.println(d2.toDays()); // 1
Chronology
用于对年历系统的支持,是 java.util.Calendar 的替代者
Chronology c = HijrahChronology.INSTANCE;
ChronoLocalDateTime d = c.localDateTime(LocalDateTime.now());
System.out.println(d); // Hijrah-umalqura AH 1440-07-05T14:15:37.388
其他
如果提供了年、年月、月日、周期的 API 支持
Year year = Year.now();
YearMonth yearMonth = YearMonth.now();
MonthDay monthDay = MonthDay.now();
System.out.println(year); // 2019
System.out.println(yearMonth); // 2019-03
System.out.println(monthDay); // --03-12
// 周期,如表示 10 天前 3 年 5 个月前
Period period1 = Period.ofDays(10);
System.out.println(period1); // P10D
Period period2 = Period.of(3, 5, 0);
System.out.println(period2); // P3Y5M
https://my.oschina.net/benhaile/blog/193956
https://jinnianshilongnian.iteye.com/blog/1994164
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 bin07280@qq.com
文章标题:Java 8新特性(7) 时间日期API
文章字数:1.9k
本文作者:Bin
发布时间:2019-03-13, 21:17:22
最后更新:2019-08-06, 00:07:35
原始链接:http://coolview.github.io/2019/03/13/Java8/Java%208%E6%96%B0%E7%89%B9%E6%80%A7(7)%20%E6%97%B6%E9%97%B4%E6%97%A5%E6%9C%9FAPI/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。