Java 8新特性(7) 时间日期API

  1. JSR310
  2. 方法概览
  3. 与旧的 API 对应关系
  4. Clock
  5. Instant
  6. LocalDateTime、LocalDate、LocalTime
  • ZonedDateTime
  • Duration
  • Chronology
  • 其他
  • 在Java 8之前,所有关于时间和日期的API都存在各种使用方面的缺陷,主要有:

    1. Java 的 java.util.Date 和 java.util.Calendar 类易用性差,不支持时区,而且他们都不是线程安全的;
    2. 用于格式化日期的类 DateFormat 被放在 java.text 包中,它是一个抽象类,所以我们需要实例化一个 SimpleDateFormat 对象来处理日期格式化,并且 DateFormat 也是非线程安全,这意味着如果你在多线程程序中调用同一个 DateFormat 对象,会得到意想不到的结果。
    3. 对日期的计算方式繁琐,而且容易出错,因为月份是从 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 类不同的是其精确到了纳秒级别。

    第二个对应于人类自身的观念,比如 LocalDateLocalTime。他们代表了一般的时区概念,要么是日期(不包含时间),要么是时间(不包含日期),类似于 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" 转载请保留原文链接及作者。

    目录