Java8新特性9--新的日期和时间API

新的日期时间处理API

从Java8开始,原生的Java API中已经能提供高质量的日期和时间支持,java.time包中整合了很多Joda-Time的特性。

LocalDate和LocalTime

开始使用新的日期和时间API时,你最先碰到的可能是LocalDate类。该类的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息。另外,它也不附带任何与时区相关的信息。

你可以通过静态工厂方法of创建一个LocalDate实例。LocalDate实例提供了多种方法来 读取常用的值,比如年份、月份、星期几等,如下所示:

package java8;

import java.time.LocalDate;

public class DateTimeTest {

    public static void main(String[] a) {
        //获取当前日期
        LocalDate now = LocalDate.now();
        
        //获取指定年月日的localdate
        LocalDate date = LocalDate.of(2017, 5, 29);
        
        System.out.println("年:" + date.getYear());
        
        System.out.println("月:" + date.getMonthValue());
        
        System.out.println("几号:" + date.getDayOfMonth());
        
        System.out.println("星期几:" + date.getDayOfWeek().getValue());
        
        System.out.println("当年中的第几天:" + date.getDayOfYear());
        
        System.out.println("当月的天数:"+date.lengthOfMonth());
        
        System.out.println("当年的天数:"+date.lengthOfYear());
        
    }
}

类似地,一天中的时间,比如13:45:20,可以使用LocalTime类表示。你可以使用of重载的两个工厂方法创建LocalTime的实例。第一个重载函数接收小时和分钟,第二个重载函数同时还接收秒。同LocalDate一样,LocalTime类也提供了一些getter方法访问这些变量的值,如下所示。

        //表示11:30分
        LocalTime time=LocalTime.of(11,30);

        //11:30:50
        LocalTime time2=LocalTime.of(11,30,50);

        System.out.println("时:"+time2.getHour());
        
        System.out.println("分:"+time2.getMinute());
        
        System.out.println("秒:"+time2.getSecond());
        
     

LocalDate和LocalTime都可以通过解析代表它们的字符串创建。使用静态方法parse,你可以实现这一目的:

    LocalDate date = LocalDate.parse("2014-03-18");
    
    LocalTime time = LocalTime.parse("13:45:20");

你可以向parse方法传递一个DateTimeFormatter。该类的实例定义了如何格式化一个日期或者时间对象。

合并日期和时间

这个复合类名叫LocalDateTime,是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息,你可以直接创建,也可以通过合并日期和时间对象构造。

        //2017-05-29 11:30:50
        LocalDateTime dateTime=LocalDateTime.of(2017,5,29,11,30,50);

        LocalDateTime dateTime2=LocalDateTime.of(date,time2);

可以通过它们各自的atTime或者atDate方法,向LocalDate传递一个时间对象,或者向LocalTime传递一个日期对象的方式,你可以创建一个LocalDateTime对象。你也可以使用toLocalDate或者toLocalTime方法,从LocalDateTime中提取LocalDate或LocalTime对象。

LocalDateTime ldt=time2.atDate(date);

LocalDateTime dateTime3=date.atTime(time2);

LocalDate localDate = dateTime3.toLocalDate();

LocalTime localTime=dateTime3.toLocalTime();
        

Instant

作为人,我们习惯于以星期几、几号、几点、几分这样的方式理解日期和时间。毫无疑问, 这种方式对于计算机而言并不容易理解。新的java.time.Instant类是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数来表示时间。

你可以通过向静态工厂方法ofEpochSecond传递一个代表秒数的值创建一个该类的实例。静态工厂方法ofEpochSecond还有一个增强的重载版本,它接收第二个以纳秒为单位的参数值,对传入作为秒数的参数进行调整。一旦得到Instant对象,你就可以像之前的LocalDate和LocalTime一样获取该时间的各个信息.

        //获取当前时间的instant
        Instant now_instant=Instant.now();
        
        //传入秒得到instant
        Instant instant=Instant.ofEpochSecond(100);
        
        //传入毫秒
        Instant instant2=Instant.ofEpochMilli(100000);

        now.getDayOfMonth();
        
        now.getDayOfYear();
        
        now.getMonthValue();
        
        now.getYear();
        
        now.lengthOfYear();

Duration

Duration类主要用于以秒和纳秒衡量时长。Duration类的静态工厂方法between用于创建两个时间之间的时长。

你可以创建两个LocalTime对象、两个LocalDateTime或者两个Instant对象之间的duration。

        //创建两个localTime之间的时长对象
        Duration duration = Duration.between(LocalTime.of(19, 20), LocalTime.of(19, 30));
        
        //获取相差多少秒      
      System.out.println(duration.getSeconds());

        //创建两个LocalDateTime之间的时长对象
        Duration duration2 = Duration.between(LocalDateTime.of(2017, 5, 29, 19, 20), LocalDateTime.of(2017, 5, 29, 19, 30));
      
        //获取相差多少秒
        System.out.println(duration2.getSeconds());
     
        //获取相差多少秒的绝对值
        System.out.println(duration2.abs().getSeconds());
       
        //相差多少天
        System.out.println(duration2.toDays());
        
        //相差多少时
        System.out.println(duration2.toHours());
       
        //相差多少分钟
        System.out.println(duration2.toMinutes());
       
        //相差多少毫秒
        System.out.println(duration2.toMillis());

由于LocalDateTime和Instant是为不同的目的而设计的,一个是为了便于人阅读使用, 另一个是为了便于机器处理,所以你不能将二者混用。如果你试图在这两类对象之间创建 duration,会触发一个DateTimeException异常。此外,由于Duration类主要用于以秒和纳秒衡量时间的长短,你不能仅向between方法传递一个LocalDate对象做参数。

如果你需要以年、月或者日的方式对多个时间单位建模,可以使用Period类。使用该类的 工厂方法between,你可以使用得到两个LocalDate之间的时长,如下所示:

Period tenDays = Period.between(LocalDate.of(2017, 3, 8),                                LocalDate.of(2017, 3, 18));

Duration和Period类都提供了很多非常方便的工厂类,用于直接创建对应的实例。

       //1天的时长转成小时
        System.out.println(Duration.ofDays(1).toHours());

        //1小时的时长转成分钟
        System.out.println(Duration.ofHours(1).toMinutes());

        //10天的时长
        System.out.println(Period.ofDays(10).getDays());

操纵和格式化日期

上述这些日期和时间对象都是不可修改的,这是为了更好地支持函数式编程,确保线程安全,保持领域模式一致性而做出的重大设计决定。当然,新的日期和时间API也 提供了一些便利的方法来创建这些对象的可变版本。

如果你已经有一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使 用withXXX方法。withXXX方法会创建对象的一个副本,并按照需要修改它的属性。注意,下面的这段代码中所有的方法都返回一个修改了属性的新对象。它们都不会修改原来的对象!

//2017-5-18
 LocalDate date1 = LocalDate.of(2017, 5, 18);
 
 //2018-5-18
 LocalDate date2 = date1.withYear(2018);
 
 //2018-5-25
 LocalDate date3 = date2.withDayOfMonth(25);

也可以以声明的方式操纵LocalDate对象。比如,你可以加上或者减去一段时间。

        //加3天
        System.out.println(date1.plusDays(5));

        //减2天减2小时减1周
        System.out.println(date1.minusDays(2).minusMonths(2).plusWeeks(1));

使用TemporalAdjuster

有的时候,你需要进行一些更加复杂的日期操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,你可以使用重载版本的withXXX方法,向其传递一个提供了更多定制化选择的TemporalAdjuster对象, 更加灵活地处理日期。对于最常见的用例,日期和时间API已经提供了大量预定义的 TemporalAdjuster。你可以通过TemporalAdjuster类的静态工厂方法访问它们。

import static java.time.temporal.TemporalAdjusters.*;

LocalDate date1 = LocalDate.of(2014, 3, 18);

//2014-3-24
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); 

//2014-3-
LocalDate date3 = date2.with(lastDayOfMonth());

TemporalAdjuster类中的常用工厂方法

dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天

firstDayOfMonth 创建一个新的日期,它的值为当月的第一天

firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天

firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天

firstDayOfYear 创建一个新的日期,它的值为当年的第一天

firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值

lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天

lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天

lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天

lastDayOfYear 创建一个新的日期,它的值为今年的最后一天

lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值

next/previous 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期

nextOrSame/previousOrSame 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期,如果该日期已经符合要求,直接返回该对象

日期格式化

处理日期和时间对象时,格式化以及解析日期时间对象是另一个非常重要的功能。新的 java.time.format包就是特别为这个目的而设计的。这个包中,最重要的类是DateTime- Formatter。创建格式器最简单的方法是通过它的静态工厂方法以及常量。像BASIC_ISO_DATE 和ISO_LOCAL_DATE这 样 的 常 量 是DateTimeFormatter类 的 预 定 义 实 例 。 所 有 的 DateTimeFormatter实例都能用于以一定的格式创建代表特定日期或时间的字符串。

LocalDate date = LocalDate.of(2014, 3, 18);

String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); 

String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);

LocalDate date1 = LocalDate.parse("20140318",
                                 DateTimeFormatter.BASIC_ISO_DATE);

LocalDate date2 = LocalDate.parse("2014-03-18",
                                 DateTimeFormatter.ISO_LOCAL_DATE);

你也可以通过解析代表日期或时间的字符串重新创建该日期对象。所有的日期和时间API 都提供了表示时间点或者时间段的工厂方法,你可以使用工厂方法parse达到重创该日期对象 的目的:

LocalDate date1 = LocalDate.parse("20140318",
                                 DateTimeFormatter.BASIC_ISO_DATE);

LocalDate date2 = LocalDate.parse("2014-03-18",
                                 DateTimeFormatter.ISO_LOCAL_DATE);

和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的。所以,你能够以单例模式创建格式器实例,就像DateTimeFormatter所定义的那些常量,并能在多个线程间共享这些实例。DateTimeFormatter类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器.

DateTimeFormatter formatter = 

DateTimeFormatter.ofPattern("dd/MM/yyyy");

LocalDate date1 = LocalDate.of(2014, 3, 18);

String formattedDate = date1.format(formatter);

LocalDate date2 = LocalDate.parse(formattedDate, formatter);

处理不同的时区

上述的日期和时间的种类都不包含时区信息。时区的处理是新版日期和时间API新增加的重要功能,使用新版日期和时间API时区的处理被极大地简化了。新的java.time.ZoneId 类是老版java.util.TimeZone的替代品。它的设计目标就是要让你无需为时区处理的复杂和繁琐而操心,比如处理日光时这种问题。跟其他日期和时间类一样,ZoneId类也是无法修改的。

时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules这个类中包含了40个这样的实例。你可以简单地通过调用ZoneId的getRules()得到指定时区的规则。每个特定的ZoneId对象都由一个地区ID标识,比如:

ZoneId romeZone = ZoneId.of("Europe/Rome");

地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构(IANA) 的时区数据库提供。

也可以利用当前时区和UTC/格林尼治的固定偏差创建时区。

        //东九区
        ZoneId zoneId= ZoneId.of("+09:00");

你可以通过Java 8的新方法toZoneId将一个老的时区对象转换为ZoneId:

ZoneId zoneId = TimeZone.getDefault().toZoneId();

一旦得到一个ZoneId对象整合起来,构造为一个ZonedDateTime实例,它代表了相对于指定时区的时间点.

        //不包含任何时区的时间
        LocalDateTime localDateTime=LocalDateTime.now();
        
        //东九区
        ZoneId zoneId= ZoneId.of("+09:00");
        
        //东八区
        ZoneId bj=ZoneId.of("+08:00");
        
        //将东八区的当期时间转化成东九区的时间
        System.out.println(LocalDateTime.ofInstant(localDateTime.atZone(bj).toInstant(),zoneId).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    

@不迷失|知识改善生活

微信公众号:java技术

《Java8新特性9--新的日期和时间API》 1492957339

专注技术研究与视频教学,分享有价值的技术与经验,关注程序员的发展!

技术博客:http://bumishi.cn

技术交流群:245130488

@不迷失教学视频

《Java8新特性9--新的日期和时间API》 xiaoetong_qrcode

《Java8新特性9--新的日期和时间API》 qqketang_qrcode

腾讯课堂:
http://bumishi.ke.qq.com

《Java8新特性9--新的日期和时间API》 baiduketang_qrcode –

百度课堂:
http://chuanke.com/s3377987.html

    原文作者:不迷失
    原文地址: https://www.jianshu.com/p/23651c604fa3
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞