时间规范每个国家或地区都不一致,实现起来困难较大,另外老版的JDK时间API没有很好的设计和规范,使用起来很复杂而且多线程时容易出现问题,JDK8提供一套更友好,有便捷安全的API。

LocalDate 、LocalTime 、LocalDateTime

LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
常用方法:

  • now() 静态方法,根据当前时间创建对象
    • LocalDate localDate = LocalDate.now();
    • LocalTime localTime = LocalTime.now();
    • LocalDateTime localDateTime = LocalDateTime.now();
  • of() 静态方法,根据指定日期/时间创建对象
    • LocalDate localDate = LocalDate.of(2016, 10, 26);
    • LocalTime localTime = LocalTime.of(02, 22, 56);
    • LocalDateTime localDateTime = LocalDateTime.of(2016, 10,26, 12, 10, 55);
  • plusDays, plusWeeks,plusMonths, plusYears 向当前 LocalDate 对象添加几天、几周、几个月、几年
  • minusDays, minusWeeks,minusMonths, minusYears 从当前 LocalDate 对象减去几天、几周、几个月、几年
  • plus, minus 添加或减少一个 Duration 或 Period
  • withDayOfMonth,withDayOfYear,withMonth,withYear将月份天数、年份天数、月份、年份修改为指定 的值 并返回新的LocalDate 对象
  • getDayOfMonth 获得月份天数(1-31)
  • getDayOfYear 获得年份天数(1-366)
  • getDayOfWeek 获得星期几(返回一个 DayOfWeek枚举值)
  • getMonth 获得月份, 返回一个 Month 枚举值
  • getMonthValue 获得月份(1-12)
  • getYear 获得年份
  • until 获得两个日期之间的 Period 对象,或者指定 ChronoUnits 的数字
  • isBefore, isAfter 比较两个 LocalDate
  • isLeapYear 判断是否是闰年

ISO 8601 时间标准

国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》,是一套全世界共认的时间表示标准。

  • 年份:年由4位数组成,以公历公元1年为0001年,以公元前1年为0000年,公元前2年为-0001年,其他以此类推
  • 月份:月为2位数,月中的日为2位数,例如2004年5月3日可写成2004-05-03或20040503。
  • 星期或日期:可以用2位数表示年内第几个日历星期,再加上一位数表示日历星期内第几天,但日历星期前要加上一个大写字母W,如2004年5月3日可写成2004-W19-1或2004W191。但2005-W011是从2005年1月3日开始的,前几天属于上年的第53个日历星期。每个日历星期从星期一开始,星期日为第7天。
  • 时间:小时、分和秒都用2位数表示,对UTC时间最后加一个大写字母Z,其他时区用实际时间加时差表示。如UTC时间下午2点30分5秒表示为14:30:05Z或143005Z,当时的北京时间表示为22:30:05+08:00或223005+0800,也可以简化成223005+08。
    • 协调世界时,又称世界统一时间、世界标准时间、国际协调时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
    • 以本初子午线的平子夜起算的平太阳时。又称格林尼治平时格林尼治时间。各地的地方平时与世界时之差等于该地的地理经度
  • 日期和时间组合:合并表示时,要在时间前面加一大写字母T,如要表示北京时间2004年5月3日下午5点30分8秒,可以写成2004-05-03T17:30:08+08:00或20040503T173008+08。
  • 时间段:如果要表示某一作为一段时间的时间期间,前面加一大写字母P,但时间段后都要加上相应的代表时间的大写字母。如在一年三个月五天六小时七分三十秒内,可以写成P1Y3M5DT6H7M30S。
  • 重复时间:前面加上一大写字母R,如要从2004年5月6日北京时间下午1点起重复半年零5天3小时,要重复3次,可以表示为R3/20040506T130000+08/P0Y6M5DT3H0M0S。对应的各地标准

来个示例: 2014-03-25T06:26:01.927Z

T和Z是不是看着很别扭?官方说明是某些极端情况下T和Z可以防止数据丢失(比空格要好很多),你不用纠结,人家就是规范,更重要的是,jdk8采用了这套规范,所以特此说明。

  1. System.out.println("当前日期: " + LocalDate.now());
  2. System.out.println("当前时间: " + LocalTime.now());
  3. System.out.println("当前日期和时间: " + LocalDateTime.now());
  4. //当前日期: 2019-12-07
  5. //当前时间: 23:38:24.743
  6. //当前日期和时间: 2019-12-07T23:38:24.743
  7. //自定义时间
  8. LocalDateTime dt = LocalDateTime.of(2019, 12, 7, 21, 21, 21, 11);
  9. System.out.println(dt);//2019-12-07T21:21:21.000000011
  10. System.out.println("年: " + dt.getYear());
  11. System.out.println("月份 " + dt.getMonth());
  12. System.out.println("月的数字 " + dt.getMonthValue());
  13. System.out.println("本年第几天 " + dt.getDayOfYear());
  14. System.out.println("本月几号 " + dt.getDayOfMonth());
  15. System.out.println("星期几 " + dt.getDayOfWeek());
  16. System.out.println("小时 " + dt.getHour());
  17. System.out.println("分钟 " + dt.getMinute());
  18. System.out.println("秒 " + dt.getSecond());
  19. System.out.println("纳秒 " + dt.getNano());
  20. //年: 2019
  21. //月份 DECEMBER
  22. //月的数字 12
  23. //本年第几天 341
  24. //本月几号 7
  25. //星期几 SATURDAY
  26. //小时 21
  27. //分钟 21
  28. //秒 21
  29. //纳秒 11
  30. //判断日期大小
  31. boolean before = dt.isBefore(LocalDateTime.now());//之前
  32. boolean after = dt.isAfter(LocalDateTime.now());//之后
  33. System.out.println(before);//true
  34. System.out.println(after);//false
  35. //日期加减
  36. LocalDateTime localDateTime = dt.plusYears(1); //加一年
  37. LocalDateTime localDateTime1 = dt.plusDays(2); //加2天
  38. LocalDateTime localDateTime2 = dt.minusHours(2);//减2小时
  39. //……等等

Instant 时间戳

用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算

  1. //时间戳 Instant (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
  2. //默认使用 UTC 时区
  3. Instant ins = Instant.now(); //默认使用 UTC 时区
  4. System.out.println(ins);
  5. OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
  6. System.out.println(odt);
  7. System.out.println(ins.getNano());
  8. Instant ins2 = Instant.ofEpochSecond(5);
  9. System.out.println(ins2);
  10. //2019-12-07T16:03:25.310Z
  11. //2019-12-08T00:03:25.310+08:00
  12. //310000000
  13. //1970-01-01T00:00:05Z

Duration 和 Period

  • Duration:用于计算两个“时间”间隔
  • Period:用于计算两个“日期”间隔
  1. Instant ins1 = Instant.now();
  2. System.out.println("--------------------");
  3. try {
  4. Thread.sleep(1000);
  5. } catch (InterruptedException e) {
  6. }
  7. Instant ins2 = Instant.now();
  8. System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
  9. System.out.println("----------------------------------");
  10. LocalDate ld1 = LocalDate.now();
  11. LocalDate ld2 = LocalDate.of(2011, 1, 1);
  12. Period pe = Period.between(ld2, ld1);
  13. System.out.println(pe.getYears());
  14. System.out.println(pe.getMonths());
  15. System.out.println(pe.getDays());
  16. //--------------------
  17. //所耗费时间为:PT1.001S
  18. //----------------------------------
  19. //8
  20. //11
  21. //7

日期的 操纵

  • TemporalAdjuster : 时间校正器。有时我们可能需要获取 ,例如:将日期调整到“下个周日”等操作。
  • TemporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
  1. LocalDateTime ldt = LocalDateTime.now();
  2. System.out.println(ldt);
  3. LocalDateTime ldt2 = ldt.withDayOfMonth(10);
  4. System.out.println(ldt2);
  5. LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
  6. System.out.println(ldt3);
  7. //自定义:下一个工作日
  8. LocalDateTime ldt5 = ldt.with((l) -> {
  9. LocalDateTime ldt4 = (LocalDateTime) l;
  10. DayOfWeek dow = ldt4.getDayOfWeek();
  11. if (dow.equals(DayOfWeek.FRIDAY)) {
  12. return ldt4.plusDays(3);
  13. } else if (dow.equals(DayOfWeek.SATURDAY)) {
  14. return ldt4.plusDays(2);
  15. } else {
  16. return ldt4.plusDays(1);
  17. }
  18. });
  19. System.out.println(ldt5);
  20. //2019-12-08T00:02:51.110
  21. //2019-12-10T00:02:51.110
  22. //2019-12-15T00:02:51.110
  23. //2019-12-09T00:02:51.110

解析 与格式化

java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

  • 预定义的标准格式
  • 语言环境相关的格式
  • 自定义的格式
  1. // DateTimeFormatter : 解析和格式化日期或时间
  2. // DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
  3. DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
  4. LocalDateTime ldt = LocalDateTime.now();
  5. String strDate = ldt.format(dtf);
  6. System.out.println(strDate);
  7. LocalDateTime newLdt = LocalDateTime.parse(strDate, dtf);
  8. System.out.println(newLdt);
  9. //2019年12月08日 00:02:33 星期日
  10. //2019-12-08T00:02:33

时区 的处理

  • Java8 中加入了对时区的支持,带时区的时间为分别为:onedDate、ZonedTime、ZonedDateTime
  • 其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式例如 :Asia/Shanghai 等
  • ZoneId:该类中包含了所有的时区信息
    • getAvailableZoneIds() : 可以获取所有时区时区信息
    • of(id) : 用指定的时区信息获取 ZoneId 对象
  1. //ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
  2. Set<String> set = ZoneId.getAvailableZoneIds();
  3. set.forEach(System.out::println);
  4. LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
  5. System.out.println(ldt);
  6. ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
  7. System.out.println(zdt);

与传统日期类的转换

image.png