java8學習:新的日期使用

內容來自《 java8實戰 》,本篇文章內容均爲非盈利,旨爲方便自己查詢、總結備份、開源分享。如有侵權請告知,馬上刪除。
書籍購買地址:java8實戰

  • 最開始java 1.0時代,開始使用Date時間類,相當難用,他是以1900年起始,月份是從0開始的,如果你要表示2018年11月25日,那麼就要這樣

    Date date = new Date(118,10,25);
    System.out.println(date.toLocaleString()); //2018-11-25 0:00:00
  • 之後Date大多數方法已經廢棄,卻而代之的是Calendar類,但是這個類的月份依舊是從0開始的,但是年份不是從1900開始的,但是解析日期的DateFormat方法只在Date中有,並且他不是線程安全的
  • Date和Calendar都是可變的,這將帶來很多麻煩,java8中在java.time中整合了很多Joda-Time的特性,Joda-Time之前是第三方類庫

使用LocalDate和LocalTime

  • 從類名就可以看出,LocalDate是表示年月日,而LocalTime是表示時分秒
  • LocalDate使用

    LocalDate date = LocalDate.of(2018,11,25);
    int year = date.getYear();  //2018
    Month month = date.getMonth(); // NOVEMBER
    int dayOfMonth = date.getDayOfMonth(); //25
    DayOfWeek dayOfWeek = date.getDayOfWeek();  //SUNDAY
    int i = date.lengthOfMonth();   //30
    boolean leapYear = date.isLeapYear();  //false
    //獲取現在時間
    LocalDate now = LocalDate.now();  //2018-11-25
  • 除了getxxx方法,還可以使用TemporalField參數給get方法獲取相同的信息

    LocalDate localDate = LocalDate.of(2018, 11, 25);
    int year = localDate.get(ChronoField.YEAR);
    int month = localDate.get(ChronoField.MONTH_OF_YEAR);
    int dayofweek = localDate.get(ChronoField.DAY_OF_WEEK);
  • 如上的ChronoFieldTemporalField的實現
  • LocalTime的使用

    LocalTime time = LocalTime.of(8,34,00);
    int hour = time.getHour();  //8
    int minute = time.getMinute();  //34
    int second = time.getSecond();  //0
    int nano = time.getNano();  //0
  • LocalDate和LocalTime的解析

    LocalTime time = LocalTime.parse("08:24");
    LocalDate date = LocalDate.parse("2018-11-25");
  • 解析不了會出DateTimeParseException

合併日期和事件

  • 就是LocalDateTime類,結合了上面兩個類,可以同時表示年月日和時分秒

    //LocalDateTime基本使用
    LocalDateTime localDateTime = LocalDateTime.of(2018,11,25,8,37);
    //創建LocalDate對象
    LocalDate localDate = LocalDate.now();
    //調用atTime可以傳入具體時分秒,構成LocalDateTime
    LocalDateTime time1 = localDate.atTime(8, 37);
    //創建LocalTime對象
    LocalTime localTime = LocalTime.now();
    //調用atDate,傳入具體年月日對象,構成LocalDateTime
    LocalDateTime localDateTime1 = localTime.atDate(localDate);
    //從LocalDateTime中可以獲取LocalDate和LocalTime
    LocalDate date = localDateTime.toLocalDate();  
    LocalTime time = localDateTime.toLocalTime();

Instant

  • 以1970年1月1日0時0分0秒開始計算的

    Instant instant = Instant.ofEpochSecond(20); //1970-01-01T00:00:20Z
    Instant.ofEpochSecond(20, 1000000000);//1970-01-01T00:00:21Z
  • 如上的ofEpochSecond方法,第一個參數是以秒爲單位的,然後增強版本,是以納秒爲單位的,其實第二個方法就是在1970-1-1-00:00:00時間上加上了21秒而已
  • 也有now方法獲取當前時間戳

Duration和Period

  • 取兩個時間之間的差值

    LocalTime time1 = LocalTime.of(8,00);
    LocalTime time2 = LocalTime.of(9,00);
    Duration between = Duration.between(time1, time2);  //PT1H
  • LocalTime,LocalDateTime,Instant可以用於Duration,Duration適用於以秒和納秒,所以就不能向其傳入LocalDate

    LocalDate date1 = LocalDate.of(2018,7,7);
    LocalDate date2 = LocalDate.of(2018,8,10);
    Period between = Period.between(date1, date2);  //P1M3D
  • 當然他們不僅限於計算差值,還可以創建對象以代表具體的時間值

    Duration duration = Duration.ofMillis(3);
    System.out.println("duration = " + duration);//duration = PT0.003S
    Period period = Period.ofDays(10);
    System.out.println("period = " + period);//period = P10D
  • 對於其他的方法,大家可以查詢文檔即可:java8 api文檔
  • 對於以上的介紹目前爲止日期對象都是不可變的,這是考慮函數式編程以及線程安全而做的決定

操縱解析和格式化日期

  • 創建LocalDate的修改版

    LocalDate date = LocalDate.of(2018, 4, 4); //2018-4-4
    LocalDate date1 = date.withYear(2014);//2014-4-4
    LocalDate date2 = date1.withMonth(6);//2014-6-4
    LocalDate date3 = date2.withDayOfMonth(6);//2014-6-6
  • 也可以這樣

    LocalDate date = LocalDate.of(2018, 4, 4); //2018-4-4
    LocalDate date1 = date.minusYears(4); //2014-4-4
    LocalDate date2 = date1.plusMonths(8);//2014-12-4
    LocalDate date3 = date2.plusDays(10);//2014-12-14
  • 需要注意的是,即使對一個時間對象調用方法,但是該對象是不會發生改變的,只是返回一個新對象,如下

    LocalDate date = LocalDate.of(2018, 4, 4); //2018-4-4
    LocalDate date1 = date.plusYears(2).with(ChronoField.MONTH_OF_YEAR, 12).plusDays(10);
    date.plusYears(12);
    System.out.println(date);//2018-4-4
    System.out.println(date1);//2020-12-14

使用TemporalAdjuster

  • 當我們遇到複雜情況的時候,比如調整時間到下個週日之類的要求,我們就需要自定義一個TemporalAdjuster對象,更加靈活的處理日期
  • java8已經預定義好一些TemporalAdjuster了,可以通過工廠類訪問他們

    LocalDate date = LocalDate.of(2018, 11, 26); //2018-11-26   周1
    LocalDate date1 = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); //下個週日是2018-12-2
    LocalDate date2 = date1.with(TemporalAdjusters.lastDayOfMonth()); //2018-12-31   十二月的最後一天
  • 其他工廠方法請自己查看java api獲取
  • TemporalAdjuster是一個函數式接口,定義如下

    @FunctionalInterface
    public interface TemporalAdjuster {
        Temporal adjustInto(Temporal temporal);
    }
  • 如上傳入一個Temporal,返回一個Temporal,也就是說,實現的邏輯在於怎樣把傳入的Temporal轉換爲另一個需要的Temporal

定製TemporalAdjuster

  • 創建類實現TemporalAdjuster接口

    
    /**
     * 需求:返回下一個工作日,如果日期在週一到週五之間,就返回下一天就好,如果是週五六日就需要返回下週一的日期
     */
    public class NextWorkingDay implements TemporalAdjuster {
        @Override
        public Temporal adjustInto(Temporal temporal) {
            //得到參數中是這周的第幾天
            int day = temporal.get(ChronoField.DAY_OF_WEEK);
            //根據第幾天構造出DayOfWeek枚舉類,容易觀察,當然也可以不用構造
            //如果不夠再下面的判斷相等直接改成上面的day就行了
            DayOfWeek dayOfWeek = DayOfWeek.of(day);
            //需要在參數的基礎上增加幾天
            int dayNeedAdd = 1;
            //如果是週五,就需要推後三天才是週一
            if (dayOfWeek == DayOfWeek.FRIDAY){
                dayNeedAdd = 3;
            }
            //如果是週六,就需要推後兩天才是週一
            if (dayOfWeek == DayOfWeek.SATURDAY){
                dayNeedAdd = 2;
            }
            //如果是週日或者當天就是在週一到週五之間的,直接加一就可以,即返回下一天
            return temporal.plus(dayNeedAdd, ChronoUnit.DAYS);
        }
    }
  • 上面說此接口是函數式接口,當然就可以使用Lmabda傳入了,使用如下

    LocalDate date = LocalDate.of(2018, 11, 25);//週日
    LocalDate nextWorkDay = date.with(new NextWorkingDay());  //2018-11-26
  • 使用lambda

    LocalDate date = LocalDate.of(2018, 11, 25);//週日
    LocalDate nextWorkDay = date.with(temporal -> {
        //具體實現是把上面的實現類NextWorkingDay內實現的方法體貼進來就行了
    });
  • 也可以這樣

    LocalDate date = LocalDate.of(2018, 11, 25);//週日
    TemporalAdjuster nextWorkDay = TemporalAdjusters.ofDateAdjuster(temporal -> {
        //具體實現是把上面的實現類NextWorkingDay內實現的方法體貼進來就行了
    });
    LocalDate date1 = date.with(nextWorkDay);
  • 如上是一個靜態方法,傳入一個UnaryOperator函數式接口的實現即可,就能夠返回一個TemporalAdjuster對象,然後傳入with方法即可

日期解析

  • java.time.format包就是解析日期格式化日期的,最重要的是DateTimeFormatter類,他有一些預定義常量

    LocalDate date = LocalDate.of(2018, 8, 8);
    String format = date.format(DateTimeFormatter.BASIC_ISO_DATE);//20180808
    String format1 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);//2018-08-08
  • 也可以通過指定解析模式來解析Sring時間字符串

    LocalDate parse = LocalDate.parse("20180808", DateTimeFormatter.BASIC_ISO_DATE); //2018-08-08
    LocalDate parse1 = LocalDate.parse("2018-08-08", DateTimeFormatter.ISO_LOCAL_DATE); //2018-08-08
  • 與之前的DateFormat相比,DateTimeFormatter是線程安全的
  • 按照指定格式創建解析器

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM-dd");
    LocalDate date = LocalDate.of(2018, 12, 6);
    //按照指定格式返回時間字符串
    String format = date.format(formatter);
    System.out.println("format = " + format);  //2018/12-06
    //根據指定的解析格式解析指定時間字符串
    LocalDate parse = LocalDate.parse(format, formatter);
    System.out.println("parse = " + parse);  //2018-12-06
  • 創建一個更加複雜的解析器

    DateTimeFormatter builder = new DateTimeFormatterBuilder()
            .appendText(ChronoField.DAY_OF_MONTH)   //首先解析的是一個月的第幾天
            .appendLiteral("(")   //分隔符
            .appendText(ChronoField.MONTH_OF_YEAR)  //一年的第幾個月
            .appendLiteral("+")   //分隔符
            .appendText(ChronoField.YEAR)  //年份
            .parseCaseInsensitive()  //開始解析,不區分大小寫
            .toFormatter(Locale.CHINA);  //按照哪個國家的方言開始解析
    LocalDate date = LocalDate.of(2018, 2, 25);
    String format = date.format(builder);
    System.out.println("format = " + format); //25(二月+2018
  • 到這裏,我們介紹了時間類的使用和解析,以及自定義如何實現自己的日期轉換邏輯,至於本書剩下的時區和計算時區的偏差,本文不再記錄
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章