当前位置 博文首页 > 龚厂长的博客:java8 详解时间日期API

    龚厂长的博客:java8 详解时间日期API

    作者:[db:作者] 时间:2021-07-26 17:40

    在使用java8以前的时间日期类的时候,总觉得这一块有点蹩脚:

    • java.util.Date和java.sql.Date,不知道为啥定义两个同名的类,而且后者还没有默认的构造方法;
    • 日期类格式化类SimpleDateFormat居然是在java.text包下;
    • 推荐实用Calendar获取Date对象的年月日时分秒,不过从名字上很难感觉到两者之间有什么关联。

    还有一些其他的问题我不再一一说明了,下面是从网上找到的关于时间日期类的问题总结:

    • 非线程安全:java.util.Date、java.util.Calendar、java.util.GregoiranCalendar和 java.text.SimpleDateFormat四大类都不是线程安全的;
    • 设计不佳 :一方面日期和日期格式化分布在多个包中;另一方面,java.util.Date 的默认日期为1970年1月1日,没有统一性。而且Date 类也缺少直接操作日期的相关方法。
    • 时区处理困难:因为设计不佳,开发人员不得不编写大量代码来处理时区问题。
    • 还有其它一些问题,如Calendar类月份从零开始计算等。

    java8对时间日期类进行了重新设计,将这些类都放在了 java.time包和其子包下,并做出了一下改进:

    • 新的日期时间 API 是线程安全的。不仅没有 setter 方法,而且任何对实例的变更都会返回一个新的实例,保证原来的实例不变。
    • 新的日期时间 API 提供了大量的方法,用于修改日期时间的各个部分,并返回一个新的实例。
    • 借鉴了第三方日期时间库joda很多的优点。
    • 在时区方面,新的日期时间 API 引入了 域 ( domain ) 这个概念。
    • Java 8 还针对原来复杂的 API 进行重新组合和拆分,分成了好多个类。

    下表是java8中主要的日期时间类:

    作用
    LocalDate本地日期,没有时区信息
    LocalTime本地时间,没有时区信息
    LocalDateTime本地日期时间,是上面两个类的集合,没有时区信息
    TemporalAdjuster调整日期工具类,比如获取下一日
    Duration计算两个时间的时间间隔
    Period计算两个日期的时间间隔
    Instant记录瞬时时间,没有时区信息
    Clock时钟类,包含有时区信息
    ZonedDate日期类,包含有时区信息
    ZonedTime时间类,包含有时区信息
    ZonedDateTime日期时间类,包含有时区信息
    ZoneId时区类
    DateTimeFormatter日期时间格式化类,功能类似于SimpleDateFormat

    接下来按照上表的顺序一一介绍每个类。

    一、LocalDate 、LocalTime、LocalDateTime

    这三个类没有时区信息,表示的是本地时间或者日期。
    这三个类将构造方法都设为private,因此都不能通过new关键字创建对象。为了获取对象,它们都提供了相似的静态方法。

    1. now():不带参数的now()可以获取当前系统时间,内部是借助Clock.systemDefaultZone()完成的,也可以指定参数来获取指定时区或者指定时间日期的对象;
    2. of():通过分别设置年月日时分秒来获得对象,LocalDateTime还提供了入参为LocalDate 和LocalTime的of()方法
    3. parse():可以根据字符串和指定的格式获得时间日期对象,如果没有传入格式,默认使用ISO格式:yyyy-MM-ddThh:mm:ss;
    4. ofXXX():这三个类还提供了一些以of开头的方法来获取对象,有的方法默认起点时间是1970-01-01 00:00:00,大家使用时注意一下,各个类提供的方法不太一致,这里不再一一介绍了;
    5. from():入参为时间日期类对象(TemporalAccessor对象),可以将入参对象转换为LocalDate 、LocalTime或者LocalDateTime。

    注意:这三个类都是不可变对象,修改时间日期的任何一个部分都会新建对象。
    除了静态方法之外,还有以下实例方法:

    1. with()/withMonth()/withDay()/…:以当前日期时间为基准,修改其中某一部分的值获得一个新对象;
    2. plus()/plusYears()/plusMonths()/…/minus()/minusMonths()/…:在当前时间上加上/减去某个值获得一个新对象;
    3. format():获得一个格式化后的时间日期字符串;
    4. isAfter()/isBefore()/isEqual():比较两个对象;
    5. get()/getXXX():可以获得日期时间的某一个部分值,比如获取年、月。

    下面以LocalDateTime为例,介绍这些方法的使用:

        public static void main(String argv[]){
            //获取当前时间
            LocalDateTime now= LocalDateTime.now();
            System.out.println(now);
            LocalDateTime parseDate= LocalDateTime.parse("2021-01-01T00:00:01");
            System.out.println(parseDate);
            //获取2021-01-01T00:01:01
            LocalDateTime ofDate= LocalDateTime.of(2021,1,1,0,1,1);
            System.out.println(ofDate);
            LocalDateTime ofDate1= LocalDateTime.of(LocalDate.now(), LocalTime.now());
            System.out.println(ofDate1);
    
            LocalDateTime firstDay=now.with(TemporalAdjusters.firstDayOfMonth());
            System.out.println(firstDay==now);
            LocalDateTime addOneDay=now.plusDays(1);
            System.out.println(addOneDay);
            System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy_MM_dd hh:mm:ss")));
        }
    

    运行结果为:

    2021-01-13T21:15:37.980
    2021-01-01T00:00:01
    2021-01-01T00:01:01
    2021-01-13T21:15:38.091
    false
    2021-01-14T21:15:37.980
    2021_01_13 09:15:37
    

    二、TemporalAdjuster

    该类用于调整时间日期,可以在当前对象的基础上调整到本月的第一个天或者下个星期。
    该类可以让我们更加灵活方便的调整时间日期。注意该类是一个接口,不过java为我们提供了TemporalAdjusters工具类可以直接获取TemporalAdjuster对象。

    	public static void main(String argv[]){
            //获取当前时间
            LocalDateTime now= LocalDateTime.now();
            System.out.println(now);
            //调整为本月的第一天
            System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));
            //调整为下个月的第一天
            System.out.println(now.with(TemporalAdjusters.firstDayOfNextMonth()));
            //调整到下个星期五
            System.out.println(now.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)));
            //如果TemporalAdjusters不能满足功能,可以创建TemporalAdjuster对象
            //下面实现一个对象,用于调整当前时间到5个小时之后
            System.out.println(now.with(new TemporalAdjuster(){
                //TemporalAdjuster接口只有这一个方法
                public Temporal adjustInto(Temporal temporal){
                    if(temporal instanceof LocalDate) {
                        return temporal;//日期对象无法调整
                    }
                    else if(temporal instanceof LocalTime)   {
                        LocalTime time=(LocalTime)temporal;
                        return time.plusHours(5);
                    }else if(temporal instanceof LocalDateTime){
                        LocalDateTime time=(LocalDateTime)temporal;
                        return time.plusHours(5);
                    }
                    return temporal;
                }
            }));
        }
    

    运行结果为:

    2021-01-13T21:37:00.536
    2021-01-01T21:37:00.536
    2021-02-01T21:37:00.536
    2021-01-15T21:37:00.536
    2021-01-14T02:37:00.536
    

    三、Duration

    该类表示一个时间间隔,时间间隔会转换为秒和纳秒存储在Duration对象中。
    常用方法有:

    1. ofDays()/ofHours()/ofXXX():设置一个时间间隔,单位为天/小时/XXX;
    2. parse():入参为字符串,根据字符串解析出时间间隔,字符串的格式为+/-PnDTnHnMn.nS,n表示数字,D表示天,H表示小时,M表示分钟,S表示秒,这是 ISO-8601的表示格式;
    3. between():计算两个日期时间之间的间隔,该方法要求两个入参必须支持时间,比如LocalDate只支持日期,使用该方法会报错。

    以上三个方法是静态方法,下面的方法是实例方法;

    1. withSeconds()/withNanos():调整秒或者纳秒;
    2. plus()/minus():在当前间隔的基础上增加或者减去一个时间;
    3. toHours()/toMillis()/toDays()/…:将当前间隔转换为要求的时间日期单位。
        public static void main(String argv[]){
            Duration dayDuration=Duration.ofDays(1);
            System.out.println(dayDuration);
            Duration minutesDuration=Duration.ofMinutes(1);
            System.out.println(minutesDuration);
            //字符串的格式为PnDTnHnMn.nS
            Duration parseDuration=Duration.parse("P1DT1H1M1.1S");
            System.out.println(parseDuration);
            //获取当前时间
            LocalDateTime now= LocalDateTime.now();
            System.out.println(now);
            //一个小时后的时间
            LocalDateTime oneHour= now.plusHours(1);
            System.out.println(oneHour);
            Duration betweenDuration=Duration.between(now,oneHour);
            System.out.println(betweenDuration);
            //一分钟加上13秒
            System.out.println(minutesDuration.plusSeconds(13));
            //一分钟加上13秒转换成毫秒
            System.out.println(minutesDuration.plusSeconds(13).toMillis());
        }
    

    运行结果为:

    PT24H
    PT1M
    PT25H1M1.1S
    2021-01-13T22:09:02.874
    2021-01-13T23:09:02.874
    PT1H
    PT1M13S
    73000
    

    如果将Duration.between()方法的入参对象换成LocalDate:

        public static void main(String argv[]){
            //获取当前时间
            LocalDate now= LocalDate.now();
            System.out.println(now);
            //一个小时后的时间
            LocalDate oneDay= now.plusDays(1);
            System.out.println(oneDay);
            Duration betweenDuration=Duration.between(now,oneDay)