zoukankan      html  css  js  c++  java
  • JDK8 新特性:新时间日期API

    本文目录:

    前言

    1.旧版日期时间API存在的问题

    2.新日期时间 API 介绍

    3.用法介绍

     1.JDK8 日期和时间类

     2.JDK8 日期时间格式化与解析

     3.JDK8 Instant 类

     4.JDK8 计算日期时间差类

     5.JDK8 日期时间调整器

     6.JDK8 设置日期时间的时区


    前言

           在 JDK8 之前,我们经常使用到的时间API包括(Date、Calendar),Date 与字符串之间的转换使用 SimpleDateFormat 进行转换(parse()、format() 方法),然而 SimpleDateFormat 不是线程安全的。在设计上也是存在一些缺陷的,比如有两个 Date 类,一个在 java.util 包中,一个在 java.sql 包中。

          在JDK8 中,引入了一套全新的时间日期API,这套 API 在设计上比较合理,使用时间操作也变得更加方便。并且支持多线程安全操作。

    1.旧版日期时间API存在的问题

    1.设计很差:在 java.util 和 java.sql 的包中都有日期类。java.util.Date 同时包含日期和时间,而java.sql.Date仅包含日期,此外用于格式化和解析的类又在 java.text 包中定义;

    2.非线程安全:java.util.Date 是非线程安全的,所有的日期类都是可变的,这是 java 日期类最大的问题之一;

    3.时区处理麻烦:日期类并不提供国际化,没有时区支持。因此 java 引入了 java.util.Calendar 和 java.util.TimeZone 类,但他们同样存在上述所有的问题

    1. /**
    2. * TODO JDK8之前日期存在的问题
    3. *
    4. * @author liuzebiao
    5. * @Date 2020-1-13 11:28
    6. */
    7. public class DateDemo01 {
    8. public static void main(String[] args) {
    9. //旧版日期时间 API 存在的问题
    10. //1.设计不合理(JDK8 Date类已经 @Deprecated 注释,不推荐使用)
    11. Date now =new Date(1985,9,23);
    12. System.out.println(now);
    13. //2.时间格式化和解析是线程不安全的
    14. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    15. for (int i = 0; i < 50; i++) {
    16. new Thread(()->{
    17. try {
    18. Date date = sdf.parse("2019-09-09");
    19. System.out.println("date:"+date);
    20. } catch (ParseException e) {
    21. e.printStackTrace();
    22. }
    23. }).start();
    24. }
    25. }
    26. }

     多线程测试结果:(会有日期格式化错误的,还有直接报错的,说明线程是不安全)

    1. date:Tue Jun 09 00:00:00 CST 2026
    2. date:Tue Sep 09 00:00:00 CST 990
    3. date:Tue Sep 09 00:00:00 CST 990
    4. date:Tue Sep 09 00:00:00 CST 990
    5. date:Sun Dec 01 00:00:00 CST 2019
    6. date:Mon Sep 09 00:00:00 CST 2019(这个才是正确的)
    7. date:Mon Sep 09 00:00:00 CST 2019
    8. date:Mon Sep 09 00:00:00 CST 2019
    9. Exception in thread "Thread-35" Exception in thread "Thread-37" java.lang.NumberFormatException: For input string: "1909E.21909"
    10. at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
    11. at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    12. at java.lang.Double.parseDouble(Double.java:538)

    2.新日期时间 API 介绍

          JDK8 中增加了一套全新的日期时间 API,这套 API 设计合理,是线程安全的。新的日期及时间 API 位于 java.time 包下,如下是一些该包下的关键类:

    • LocalDate:表示日期,包含:年月日。格式为:2020-01-13
    • LocalTime:表示时间,包含:时分秒。格式为:16:39:09.307
    • LocalDateTime:表示日期时间,包含:年月日 时分秒。格式为:2020-01-13T16:40:59.138
    • DateTimeFormatter:日期时间格式化类
    • Instant:时间戳类
    • Duration:用于计算 2 个时间(LocalTime,时分秒)之间的差距
    • Period:用于计算 2 个日期(LocalDate,年月日)之间的差距
    • ZonedDateTime:包含时区的时间

           Java 中使用的历法是 ISO-8601 日历系统,他是世界民用历法,也就是我们所说的公里。平年有365天,闰年是366天。此外 Java8 还提供了 4 套其他历法,分别是:

    • ThaiBuddhistDate:泰国佛教历
    • MinguoDate:中华民国历
    • JapaneseDate:日本历
    • HijrahDate:伊斯兰历

    3.用法介绍

     1.JDK8 日期和时间类

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

            对日期时间的修改,就是对已经存在的 LocalDate对象,根据需求创建它的修改版,最简单的方式是使用 withAttribute() 方法。withAttribute()方法会创建对象的一个副本,并按照需要修改它的属性。

           以下所有方法都返回了一个修改属性的对象,它们并不会影响原来的日期对象。(即:修改后的日期与原来的日期不是一个对象,原日期不受影响

    1. /**
    2. * TODO 时间日期类
    3. * LocalDate、LocalTime、LocalDateTime
    4. *
    5. * @author liuzebiao
    6. * @Date 2020-1-13 13:42
    7. */
    8. public class DateDemo02 {
    9. /**
    10. * LocalDate 日期类(年月日)
    11. */
    12. @Test
    13. public void testLocalDate() {
    14. //获取当前日期
    15. LocalDate now = LocalDate.now();
    16. System.out.println(now);
    17. //指定日期 LocalDate.of(year,month,day)
    18. LocalDate date = LocalDate.of(2008, 8, 8);
    19. System.out.println(date);
    20. //获取年
    21. System.out.println("年:" + date.getYear());
    22. //获取月(英文)
    23. System.out.println("月(英文):" + date.getMonth());
    24. //获取月(阿拉伯数字)
    25. System.out.println("月(数字):" + date.getMonthValue());
    26. //获取日
    27. System.out.println("日:" + date.getDayOfMonth());
    28. //是否是闰年
    29. System.out.println("是否是闰年:" + date.isLeapYear());
    30. //...其他方法,自行研究
    31. }
    32. /**
    33. * LocalDate 时间类(时分秒)
    34. */
    35. @Test
    36. public void testLocalTime() {
    37. //获取当前时间
    38. LocalTime now = LocalTime.now();
    39. System.out.println(now);
    40. //指定日期 LocalTime.of(hour,minute,second)
    41. LocalTime date = LocalTime.of(13, 26, 39);
    42. System.out.println(date);
    43. //获取时
    44. System.out.println(date.getHour());
    45. //获取分
    46. System.out.println(date.getMinute());
    47. //获取秒
    48. System.out.println(date.getSecond());
    49. //获取纳秒
    50. System.out.println(now.getNano());
    51. //...其他方法,自行研究
    52. }
    53. /**
    54. * LocalDateTime 日期时间类(年月日 时分秒)
    55. */
    56. @Test
    57. public void testLocalDateTime() {
    58. //LocalDateTime: LocalDate + LocalTime,有年月日 时分秒
    59. LocalDateTime now = LocalDateTime.now();
    60. System.out.println("当前日期时间:"+now);
    61. //指定日期时间 LocalDateTime.of(year,month,day,hour,minute,second)
    62. LocalDateTime date = LocalDateTime.of(2018, 7, 23, 18, 59, 31);
    63. System.out.println(date);
    64. //获取年
    65. System.out.println(date.getYear());
    66. //获取月
    67. System.out.println(date.getMonth());
    68. //获取日
    69. System.out.println(date.getDayOfMonth());
    70. //获取时
    71. System.out.println(date.getHour());
    72. //获取分
    73. System.out.println(date.getMinute());
    74. //获取秒
    75. System.out.println(date.getSecond());
    76. //...其他方法,自行研究
    77. }
    78. /**
    79. * 修改时间
    80. */
    81. @Test
    82. public void modifyTime() {
    83. //以LocalDateTime为例(LocalDate、LocalTime与此类似)
    84. LocalDateTime now = LocalDateTime.now();
    85. //修改年[修改时间(不是JDK8之前的setXXX(),而是使用withXXX())]
    86. System.out.println("修改年后:" + now.withYear(9102));
    87. //增加年(减使用 minusYear()方法)
    88. System.out.println("+2年后:" + now.plusYears(2));
    89. //增加日(减使用 minusDays()方法)
    90. System.out.println("47天后:" + now.plusDays(47));
    91. //...其他方法,自行研究
    92. }
    93. /**
    94. * 时间比较
    95. */
    96. @Test
    97. public void compareTime() {
    98. //以LocalDateTime为例(LocalDate、LocalTime与此类似)
    99. //时间1
    100. LocalDateTime now = LocalDateTime.now();
    101. //时间2
    102. LocalDateTime dateTime = LocalDateTime.of(2018, 7, 12, 13, 28, 51);
    103. //判断前面日期是否在后面日期后
    104. System.out.println("A时间是否晚于B时间:" + now.isAfter(dateTime));
    105. //判断前面日期是否在后面日期前
    106. System.out.println("A时间是否早于B时间:" + now.isBefore(dateTime));
    107. //判断两个日期时间是否相等
    108. System.out.println("两个时间是否相等:" + now.isEqual(dateTime));
    109. //...其他方法,自行研究
    110. }
    111. }

     2.JDK8 日期时间格式化与解析

    1. /**
    2. * TODO 日期时间格式化 + 解析 + 多线程执行(安全)
    3. *
    4. * @author liuzebiao
    5. * @Date 2020-1-13 14:12
    6. */
    7. public class DateDemo03 {
    8. @Test
    9. public void dateFormat(){
    10. LocalDateTime now = LocalDateTime.now();
    11. //格式化
    12. //使用JDK自带的时间格式:ISO_DATE_TIME(默认提供了很多格式,请自行查看)
    13. DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
    14. String format = now.format(dtf);
    15. System.out.println("format="+format);
    16. //指定时间格式(ofPattern()方法)
    17. DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
    18. String format1 = now.format(dtf1);
    19. System.out.println(format1);
    20. //解析(parse()方法)
    21. LocalDateTime parse = LocalDateTime.parse(format1, dtf1);
    22. System.out.println("parse="+parse);
    23. /**
    24. * 多线程执行(验证线程安全性)
    25. * 1.返回结果正确 2.不抛异常
    26. */
    27. for (int i = 0; i < 50; i++) {
    28. new Thread(()->{
    29. LocalDateTime parse1 = LocalDateTime.parse(format1, dtf1);
    30. System.out.println("parse="+parse1);
    31. }).start();
    32. }
    33. }
    34. }

     3.JDK8 Instant 类

          Instant 类,就是时间戳,内部保存了从1970年1月1日 00:00:00以来的秒和纳秒。

    1. /**
    2. * TODO JDK8的 Instant类(时间戳)
    3. * (主要不是面向用户使用)
    4. *
    5. * @author liuzebiao
    6. * @Date 2020-1-13 14:31
    7. */
    8. public class DateDemo04 {
    9. @Test
    10. public void Instant(){
    11. //Instant
    12. // 内部保存了秒和纳秒,一般不是给用户使用的,而是方便程序做一些统计的(比如:统计方法耗时)
    13. Instant now = Instant.now();
    14. System.out.println("当前时间戳:"+now);//2020-01-13T06:48:46.267Z
    15. //Instant类 并没有修改年月日等操作.因为 Instant 本来就不是给用户使用的
    16. //Instant类:对 秒、纳秒等操作方便
    17. Instant plus = now.plusSeconds(20);
    18. System.out.println("+20秒后:"+plus);
    19. Instant minus = now.minusSeconds(20);
    20. System.out.println("-20秒后:"+minus);
    21. //获取秒、毫秒、纳秒
    22. long second = now.getEpochSecond();
    23. System.out.println("秒:"+second);
    24. int nano = now.getNano();
    25. System.out.println("纳秒:"+nano);
    26. //...其他方法,自行研究
    27. }
    28. }

     4.JDK8 计算日期时间差类

         Duration/Period 类:主要用来计算日期时间差

    1. Duration:用于计算 2 个时间(LocalTime,时分秒)的差值
    2. Period:用于计算 2 个 日期(LocalDate,年月日)的差值
    1. /**
    2. * TODO JDK8 计算日期时间差值
    3. *
    4. * @author liuzebiao
    5. * @Date 2020-1-13 14:55
    6. */
    7. public class DateDemo05 {
    8. /**
    9. * Duration类:计算时间的差值
    10. */
    11. @Test
    12. public void testTimeDiff(){
    13. //时间1
    14. LocalTime now = LocalTime.now();
    15. //时间2
    16. LocalTime dateTime = LocalTime.of(8, 15, 46);
    17. //计算两个时间的差值
    18. //计算规则:让第二个参数 减去 第一个参数(位置错误可能出现负数)
    19. Duration duration = Duration.between(dateTime,now);
    20. System.out.println("相差的天数:"+duration.toDays());
    21. System.out.println("相差的小时数:"+duration.toHours());
    22. System.out.println("相差的分钟数:"+duration.toMinutes());
    23. System.out.println("相差的秒数:"+duration.toSeconds());//JDK 9+ 出现(JDK8会报错误)
    24. System.out.println("相差的纳秒数:"+duration.toNanos());
    25. //...其他方法,自行研究
    26. }
    27. /**
    28. * Period类:计算日期的差值
    29. */
    30. @Test
    31. public void testDateDiff(){
    32. //日期1
    33. LocalDate now = LocalDate.now();
    34. //日期2
    35. LocalDate date = LocalDate.of(1999,5,29);
    36. //计算两个日期的差值
    37. //计算规则:让第二个参数 减去 第一个参数(位置错误可能出现负数)
    38. Period period = Period.between(date,now);
    39. System.out.println("相差的年:"+period.getYears());
    40. System.out.println("相差的月:"+period.getMonths());
    41. System.out.println("相差的日:"+period.getDays());
    42. //...其他方法,自行研究
    43. }
    44. }

     5.JDK8 日期时间调整器

       有时我们可能需要获取,例如:"将日期调整到下一个月的第一天"等操作,此时我们可以通过时间调整器来进行操作。

    • TemporalAdjuster:时间调整器
    • TemporalAdjusters:工具类。该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现
    1. /**
    2. * TODO JDK8 时间调整器
    3. *
    4. * @author liuzebiao
    5. * @Date 2020-1-13 15:10
    6. */
    7. public class DateDemo06 {
    8. /**
    9. * TemporalAdjuster类:自定义调整时间
    10. */
    11. @Test
    12. public void timeCorrector(){
    13. //将日期调整到"下一个月的第一天"操作
    14. LocalDateTime now = LocalDateTime.now();
    15. //参数:TemporalAdjuster adjuster。TemporalAdjuster是一个接口,里面只有 Temporal adjustInto(Temporal temporal); 这一个方法,支持接入 lambda 表达式
    16. //此处 Temporal 就是指时间(包括 LocalDate、LocalTime、LocalDateTime 都是继承自该类。继承关系:如下图所示)
    17. TemporalAdjuster adjuster = ( Temporal temporal)->{
    18. LocalDateTime dateTime = (LocalDateTime)temporal;
    19. return dateTime.plusMonths(1).withDayOfMonth(1);//下一个月第一天
    20. };
    21. LocalDateTime newDateTime = now.with(adjuster);
    22. System.out.println("下个月第一天:"+newDateTime);
    23. }
    24. /**
    25. * TemporalAdjusters工具类:使用JDK提供的时间调整器
    26. */
    27. @Test
    28. public void JDKTimeCorrector(){
    29. //JDK中自带了很多时间调整器,其他调整器请自行查看
    30. //使用 TemporalAdjusters 工具类
    31. //TemporalAdjusters.firstDayOfNextYear()--->根据内容可知:下一年第一天
    32. TemporalAdjuster temporalAdjuster = TemporalAdjusters.firstDayOfNextYear();
    33. LocalDateTime now = LocalDateTime.now();
    34. LocalDateTime newDateTime = now.with(temporalAdjuster);
    35. System.out.println("下个月第一天:"+newDateTime);
    36. }
    37. }

     附:继承关系(LocalDate、LocalTime、LocalDateTime、Temporal类)

     6.JDK8 设置日期时间的时区

           JDK8 中加入了对时区的支持。LocalDate、LocalTime、LocalDateTime 是不带时区的,带时区的日期时间类分别为:ZonedDate、ZonedTime、ZonedDateTime类。

           其中每个时区都有对应着的 ID,ID的格式为"区域/城市",例如:Asia/Shanghai 等。

           ZoneId类:该类中包含了所有的时区信息。

    1. /**
    2. * TODO JDK8设置时区(设置日期时间的时区)
    3. *
    4. * @author liuzebiao
    5. * @Date 2020-1-13 15:43
    6. */
    7. public class DateDemo07 {
    8. /**
    9. * 获取时区ID
    10. */
    11. @Test
    12. public void getZoneIds(){
    13. //1.获取所有的时区ID
    14. Set<String> zoneIds = ZoneId.getAvailableZoneIds();
    15. zoneIds.forEach(System.out::println);//返回600来个时区
    16. }
    17. /**
    18. * 不带时区 Vs 带时区的日期时间
    19. */
    20. @Test
    21. public void ZonedDemo(){
    22. //2.操作带时区的类
    23. //不带时间,获取计算机的当前时间
    24. LocalDateTime now = LocalDateTime.now();
    25. System.out.println("now:"+now);
    26. //中国使用的是东八区的时间,比标准时间早8个小时
    27. //操作带时间的类
    28. ZonedDateTime zdt = ZonedDateTime.now(Clock.systemUTC());//创建出来的时间是世界标准时间
    29. System.out.println("世界标准时间:"+zdt);
    30. }
    31. /**
    32. * 本地时间
    33. */
    34. @Test
    35. public void localTime(){
    36. //now():使用计算机的默认的时区,创建日期时间
    37. ZonedDateTime now = ZonedDateTime.now();
    38. System.out.println("本地时间:"+now);//本地时间:2020-01-13T15:52:43.633+08:00[Asia/Shanghai]
    39. }
    40. /**
    41. * 使用指定的时区来创建时间
    42. */
    43. @Test
    44. public void ZoneTime(){
    45. ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
    46. System.out.println("设置指定时间:"+now);//设置指定时间:2020-01-13T02:56:24.776-05:00[America/New_York]
    47. }
    48. /**
    49. * 修改时区
    50. */
    51. @Test
    52. public void modifyZone(){
    53. ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
    54. //withZoneSameInstant():既更改时区,也更改时间
    55. ZonedDateTime modifyTime = now.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
    56. System.out.println("修改时区后的时间:"+modifyTime);
    57. //withZoneSameLocal():只更改时区,不更改时间
    58. ZonedDateTime modifyTime2 = now.withZoneSameLocal(ZoneId.of("Asia/Shanghai"));
    59. System.out.println("修改时区后的时间:"+modifyTime2);
    60. }
    61. //...其他方法,自行研究
    62. }

    作者:Binge
    本文版权归作者和博客园共有,转载必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    页面get请求 中文参数方法乱码问题
    java版ftp简易客户端(可以获取文件的名称及文件大小)
    文件下载
    kafka:一个分布式消息系统
    Executor的线程代码
    验证码的生成
    二维码的简单实现
    rsync实现大致流程描述
    C++中模板生成时机
    gcc虚函数表生成时机
  • 原文地址:https://www.cnblogs.com/binbingg/p/15241732.html
Copyright © 2011-2022 走看看