Joda Time專案和java8時間api

攻城獅07發表於2019-10-10

## 一、Joda Time專案

1. Joda Time出現的背景

    在java1.0中,對日期和時間的支援只能依賴java.util.Date類。正如類名所表達的,這個類無法表示日期,只能以毫秒的精度表示時間。更糟糕的是它的易用性,由於某些未知的設計決策,這個類的易用性被深深的損害了,比如:年份的起始日期選擇是1990年,月份的起始從0開始。

    在java1.1中,Date類中的很多方法被廢棄了,取而代之的是java.util.Calendar類。Calendar類也有類似的問題和設計缺陷,導致使用這些方法寫出的程式碼非常容易出錯。比如月份依舊是從0開始計算(拿掉了由1990年開始計算年份這一設計)。更糟的是,有的特性只在某一個類有提供,比如用於語言無關方式格式化和解析日期或時間的DateFormat方法就只在Date類有。DateFormat不是執行緒安全的,二個執行緒同時使用formatter解析日期,你可能會得到無法預期的結果。

    在jdk1.8之前,這些問題使得使用者們使用了第三方日期和時間庫,比如Joda Time。jdk1.8大量借鑑了Joda Time特任。

2. Joda Time專案

    Java SE 8之前的標準日期和時間類很差。 通過解決這個問題,Joda-Time在Java SE 8之前成為Java的實際標準日期和時間庫。請注意,從Java SE 8起,使用者被要求遷移到java.time(JSR-310) - JDK的核心部分,取代了這個專案。如果我們工作中的jdk版本是1.8版本之前可以使用Joda Time專案,Joda專案中其實包括的不止Joda Time,還包括Joda-Money ,Joda-Beans,Joda-Convert ,Joda-Collect,Joda Primitives專案,有興趣可以在Joda官網地址中瞭解一下。

  1. pom依賴:
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.9</version>
</dependency>
複製程式碼
  1. 第一個demo
package com.zhihao.joda;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;

public class JodaTest2 {
    public static void main(String[] args) {
        DateTime today = new DateTime();
        DateTime datetorrow = today.plusDays(1);

        System.out.println(today.toString("yyyy-MM-dd"));//2017-06-26
        System.out.println(today.toString("yyyy-MM-dd HH:mm:ss"));//2017-06-26 22:04:03
        System.out.println(datetorrow.toString("yyyy-MM-dd"));//2017-06-27
        System.out.println("......................");

        //獲得一個時間的副本,將day設定成自己制定的時間,不改變月份,只改變日期
        DateTime d1 = today.withDayOfMonth(1);
        System.out.println(d1.toString("yyyy-MM-dd"));//2017-06-01
        System.out.println("......................");

        LocalDate localDate = new LocalDate();
        System.out.println(localDate);//2017-06-26
        System.out.println("........................");

        //獲取當前時間三個月後的月份的最後一天
        localDate = localDate.plusMonths(3).dayOfMonth().withMaximumValue();
        System.out.println(localDate);//2017-09-30
        System.out.println("........................");


        //計算二年前第三個月最後一天的日期
        DateTime dateTime = new DateTime();
        DateTime dateTime2 = dateTime.minusYears(2).monthOfYear().setCopy(3).
                dayOfMonth().withMinimumValue();
        System.out.println(dateTime2.toString("yyyy-MM-dd"));//2017-09-30

    }
}
================================================================================
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;

import java.util.Date;

public class JodaTest3 {

    //將utc時間轉換成java中的Date格式
    public static Date convertUTC2Date(String utcDate){
        try{
            DateTime dateTime =DateTime.parse(utcDate, DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
            return dateTime.toDate();
        }catch (Exception ex){
            return null;
        }
    }

    //將java中的date格式的時間轉換成utc時間標準
    public static String ConvertDate2UTC(Date javaDate){
        DateTime dateTime = new DateTime(javaDate, DateTimeZone.UTC);
        return dateTime.toString();
    }

    //將Date型別轉換成字串
    public static String convertDate2LocalByDateformat(Date javaDate,String dateFormat){
        DateTime dateTime = new DateTime(javaDate);
        return dateTime.toString(dateFormat);
    }


    public static void main(String[] args) {
        System.out.println(JodaTest3.convertUTC2Date("2010-12-1T11:22:33.567Z"));//Wed Dec 01 19:22:33 CST 2010
        System.out.println(JodaTest3.ConvertDate2UTC(new Date()));//2017-06-26T14:09:53.606Z
        System.out.println(JodaTest3.convertDate2LocalByDateformat(new Date(),"yyyy-MM-dd HH:mm:ss"));//2017-06-26 22:09:53
    }
}
複製程式碼

什麼是UTC時間?
    沒有時區概念,比如utc時間為2010-12-1T11:22:33.567Z,如果是表示時區概念一般2010-12-1T11:22:33.567+08:00

    關於Joda Time其他的日期和時間api可以看其依賴包下的具體類,具體使用方式也很簡單看齊javadoc即可。

二、java8時間api

1. LocalDate,LocalTime

(1). LocalDate

    LocalDate類的例項是一個不可變的物件,只提供了簡單的日期,並不包含當前的時間資訊(只關注與年月日)。也不附帶任何與時區相關的資訊。 LocalTime類關注時分秒。

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.MonthDay;

public class Java8TimeTest {
    public static void main(String[] args) {
        //關注與年月日
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate);  //2017-06-26

        System.out.println(localDate.getYear()); //2017,年
        System.out.println(localDate.getMonthValue()); //6,月
        System.out.println(localDate.getDayOfMonth()); //26,日
        System.out.println(localDate.getDayOfWeek()); //MONDAY,星期幾
        System.out.println(localDate.lengthOfMonth()); //30,返回當前月份的長度
        System.out.println(localDate.isLeapYear());//false,是否是閏年

        System.out.println("------------------");

        LocalDate localDate2 = LocalDate.of(2017,4,1);
        System.out.println(localDate2); //2017-04-01

        System.out.println("------------------");

        //MonthDay關注月份和日
        LocalDate localDate3 = LocalDate.of(2017,3,25);
        MonthDay monthDay = MonthDay.of(localDate3.getMonth(),localDate3.getDayOfMonth());
        System.out.println(monthDay); //--03-25
        MonthDay monthDay2 = MonthDay.from(LocalDate.of(2014,3,25));
        System.out.println(monthDay2); //--03-25

        if(monthDay.equals(monthDay2)){
            System.out.println("equal");
        }else{
            System.out.println("not equal");
        }

        //關注與時分秒
        LocalTime time = LocalTime.now();
        System.out.println(time); //22:30:01.512
        System.out.println(time.getHour()); //22,時
        System.out.println(time.getMinute()); //30,分
        System.out.println(time.getSecond());  //01,秒

        LocalTime time2 = time.plusHours(3).plusMinutes(40);
        System.out.println(time2);  //02:10:01.512
    }
}
================================================================================
import java.time.LocalDate;

public class Java8TimeTest1 {
    public static void main(String[] args) {
        //LocalDate的parse只能轉換2007-12-03這樣的格式的,不能解析的也會丟擲一個RuntimeException
        //或者DateTimeParseException
        LocalDate date = LocalDate.parse("2014-03-18");
        LocalDate date2 = LocalDate.parse("2017-03-18");
        System.out.println(date);
        System.out.println(date2);

        LocalDate nowdate = LocalDate.now();
        String date3 = nowdate.toString();
        System.out.println(date3); //2017-06-26
    }
}
================================================================================
import java.time.*;
import java.time.temporal.ChronoUnit;

public class Java8TimeTest2 {
    public static void main(String[] args) {
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate); //2017-06-27

        //當前日期的二週後
        LocalDate localDate2 = localDate.plus(2, ChronoUnit.WEEKS);
        System.out.println(localDate2); //2017-07-11
        System.out.println("............");

        //當前時間的二個月之前
        LocalDate localDate3 = localDate.minus(2,ChronoUnit.MONTHS); 
        System.out.println(localDate3);//2017-04-27
        System.out.println("..............");

        Clock clock = Clock.systemDefaultZone(); //當前時區的時刻
        System.out.println(clock.millis()); //獲得當前的毫秒數,1498529786982


        LocalDate localDate5 = LocalDate.now();
        LocalDate localDate6 = LocalDate.of(2017,4,12);
        System.out.println(localDate5.isBefore(localDate6));  //判斷時間在什麼時間之前
        System.out.println(localDate5.isAfter(localDate6)); //判斷時間在什麼時間之後
        System.out.println(localDate5.isEqual(localDate6)); //判斷時間和什麼時間相等
        System.out.println("..............");

    }
}
複製程式碼

(2). LocalDateTime

    一個沒有時區概念的日期-時間類在ISO-8601 日期系統中,比如2007-12-03T10:15:30

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;

public class Java8TimeTest11 {
    public static void main(String[] args) {
        LocalDateTime dt1 = LocalDateTime.of(2017, Month.APRIL,18,13,45,20);
        System.out.println(dt1);
        LocalDate date1 = dt1.toLocalDate(); //通過LocalDateTime獲得LocalDate
        LocalTime time1 = dt1.toLocalTime(); //通過LocalDateTime獲得LocalTime
        System.out.println("date1======="+date1+"time1===="+time1);
        
        LocalDate date = LocalDate.of(2014,02,26);
        LocalTime time = LocalTime.of(12,23,20);
        LocalDateTime dt2 = LocalDateTime.of(date,time);
        System.out.println(dt2);

        //LocalDate結合LocalTime成一個LocalDateTime
        LocalDateTime dt3 = date.atTime(13,45,20);
        System.out.println(dt3); //2014-02-26T13:45:20

        //LocalDate結合LocalTime成一個LocalDateTime
        LocalDateTime dt4 = date.atTime(time);
        System.out.println(dt4); //2014-02-26T12:23:20

        //LocalTime結合LocalDate成LocalDateTime
        LocalDateTime dt5 = time.atDate(date);
        System.out.println(dt5); //2014-02-26T12:23:20
    }
}
複製程式碼

2.機器的日期和時間格式

    作為人,我們習慣與以星期幾,幾號,幾點,幾分這樣的方式理解日期和時間。對於計算機來說,建模時間最自然的格式是表示一個持續時間段上某個點的單一大整型數。這也是新的java.time.Instant類對時間建模的方式,基本上它是以Unix元年時間(傳統的設定為UTC時區1970年1月1日午夜時分)開始經歷的秒數進行計算。

ackage com.zhihao.date;
import java.time.Instant;

public class InstantTest {
    public static void main(String[] args) {
        Instant instant1 = Instant.ofEpochSecond(3);
        System.out.println(instant1);//1970-01-01T00:00:03Z

        //第一個引數是秒,第二個是納秒引數,納秒的儲存範圍是0至999,999,999
        Instant instant2 = Instant.ofEpochSecond(3,0);
        System.out.println(instant2);//1970-01-01T00:00:03Z

        //2s之後的在加上100萬納秒(1s)
        Instant instant3 = Instant.ofEpochSecond(2,1000000000);
        System.out.println(instant3); //1970-01-01T00:00:03Z

        Instant instant4 = Instant.ofEpochSecond(4,-1000000000);
        System.out.println(instant4); //1970-01-01T00:00:03Z

        Instant instant = Instant.now();
        System.out.println(instant);
    }
}
複製程式碼

3. Duration與Period

import java.time.*;

public class DurationTest {
    public static void main(String[] args) {
        LocalTime time1 = LocalTime.of(18,23,45);
        LocalTime time2 = LocalTime.of(23,34,56);

        Duration d1 = Duration.between(time1,time2);
        System.out.println(d1.getSeconds()); //18671


        LocalDateTime localDateTime1 = LocalDateTime.of(2016,Month.MAY,12,11,13,11);
        LocalDateTime localDateTime2 = LocalDateTime.of(2017, Month.AUGUST,18,23,45,20);

        Duration d2 = Duration.between(localDateTime1,localDateTime2);
        System.out.println(d2.getSeconds()); //40048329


        Instant instant1 = Instant.ofEpochSecond(3);
        Instant instant2 = Instant.ofEpochSecond(6);

        Duration d3 = Duration.between(instant1,instant2);
        System.out.println(d3.getSeconds()); //3

    }
}
複製程式碼

    以年,月,日方式建模,可以使用Period類。

import java.time.Instant;
import java.time.LocalDate;
import java.time.Period;

public class Java8TimeTest4 {
    public static void main(String[] args) {
        //LocalDate localDate1 = LocalDate.now();
        LocalDate localDate1 = LocalDate.of(2017,4,12);
        LocalDate localDate2 = LocalDate.of(2018,3,16);

        Period period = Period.between(localDate1,localDate2);
        System.out.println(period.getYears()); //獲取相隔的年份差 0
        System.out.println(period.getMonths()); //獲取相隔的月份差 11
        System.out.println(period.getDays()); //獲取相隔的日子差 4

        System.out.println("...............");

        System.out.println(Instant.now()); //表示當前的不帶時區的UTC標準時間,2017-04-12T14:40:29.309Z
    }
}
複製程式碼

    關於二者其他的api可以對照java api文件進行檢視,比較淺顯。

4. ZoneId和ZonedDateTime

    新的java.time.ZoneId替代了老版本的java.util.TimeZone.

import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Set;
import java.util.TreeSet;

public class Java8TimeTest3 {
    public static void main(String[] args) {
        //時區
        Set<String> sets = ZoneId.getAvailableZoneIds();

        //sets.stream().forEach(System.out::println);

        //構建一個TreeSet的匿名內部類,然後裡面是個程式碼塊表示在例項建立的時候執行這個程式碼塊
        TreeSet<String> treeSet = new TreeSet<String>(){
            {
                addAll(sets);
            }
        };

        treeSet.stream().forEach(System.out::println);

        System.out.println("........................");

        ZoneId zoneId = ZoneId.of("Asia/Shanghai");
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime);//2017-04-12T22:05:22.500

        ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime,zoneId);
        System.out.println(zonedDateTime); //2017-04-12T22:05:22.500+08:00[Asia/Shanghai]

        System.out.println("..................");

        YearMonth yearMonth = YearMonth.now();
        System.out.println(yearMonth); //2017-06
        System.out.println(yearMonth.lengthOfMonth());  //當月有多少天,30
        System.out.println(yearMonth.isLeapYear()); //是否是閏年,false

        System.out.println(".............");

        YearMonth yearMonth2 = YearMonth.of(2016,2);
        System.out.println(yearMonth2); //2016-02
        System.out.println(yearMonth2.lengthOfMonth()); //當前的月有多少天,29
        System.out.println(yearMonth2.lengthOfYear()); //一年有多少天,366
        System.out.println(yearMonth2.isLeapYear()); //是否是閏年,true

    }
}

================================================================================

import java.time.LocalDateTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class ZoneOffsetTest {
    public static void main(String[] args) {
        ZoneOffset zoneOffset = ZoneOffset.of("-05:00");

        LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH,18,13,45);
        OffsetDateTime offsetDateTime = OffsetDateTime.of(dateTime,zoneOffset);
        System.out.println(offsetDateTime); //2014-03-18T13:45-05:00
    }
}
複製程式碼

    java8還提供了一些別的日曆系統,這些日曆系統中的每一個都有一個ThaiBuddhistDate,MinguoDate,JapaneseDate對應的日誌類。這邊不做介紹。

5. 格式化與解析時間物件DateTimeFormatter

    建立格式器最簡單的方法是通過DateTimeFormatter的靜態工廠方法以及常量。像BASIC_ISO_DATE 和ISO_LOCAL_DATE這 樣 的 常 量 是DateTimeFormatter類 的 預 定 義 實 例 。 所 有 的 DateTimeFormatter例項都能用於以一定的格式建立代表特定日期或時間的字串。

    和老的java.util.DateFormat相比較,所有的DateTimeFormatter例項都是執行緒安全的。所以,你能夠以單例模式建立格式器例項,就像DateTimeFormatter所定義的那些常量,並能在多個執行緒間共享這些例項。DateTimeFormatter類還支援一個靜態工廠方法,它可以按照某個特定的模式建立格式器.

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class DateUtil {
    public static void main(String[] args) {
        Date date = new Date();
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formatdate = localDateTime.format(formatter);
        System.out.println(formatdate);

        System.out.println("...........");
        LocalDate localDate = LocalDate.of(2017,3,17);
        //BASIC_ISO_DATE格式,20111203
        String str = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println(str);

        //DateTimeFormatter.ISO_LOCAL_DATE 格式 2017-03-17
        String str2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
        System.out.println(str2);

        //定義
        String str3 = localDate.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        System.out.println(str3);

        LocalDate localDate1 = LocalDate.parse("20111203",DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println(localDate1); //2011-12-03

        LocalDate localDate2 = LocalDate.parse("2017-03-17",DateTimeFormatter.ISO_LOCAL_DATE);
        System.out.println(localDate2); //2017-03-17

    }
}
複製程式碼

6. 使用TemporalAdjuster類更精確的操縱日期

    使用TemporalAdjuster類更精確的操縱日期,不在侷限於一次只能改變它的一個只,並且你還可以按照需求定義自己的日期轉換器。

TemporalAdjusters工廠類為我們提供了很多便捷的操作。

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

public class TemporalAdjusterTest {
    public static void main(String[] args) {
        LocalDate localDate = LocalDate.of(2017,6,20);
        System.out.println(localDate); //2017-06-20

        //下一週的星期天
        LocalDate newdata = localDate.with(nextOrSame(DayOfWeek.SUNDAY));
        System.out.println(newdata); //2017-06-25

        LocalDate lastDate = localDate.with(lastDayOfMonth());
        System.out.println(lastDate); //2017-06-30

        //表示當前月的第二週的星期天
        LocalDate date1 = localDate.with(dayOfWeekInMonth(2,DayOfWeek.SUNDAY));
        System.out.println(date1); //2017-06-11

        //當前月的第一天
        LocalDate date2 = localDate.with(firstDayOfMonth());
        System.out.println(date2);

        //下個月的第一天
        LocalDate date3 = localDate.with(firstDayOfNextMonth());
        System.out.println(date3); //2017-07-01

        //明年的第一天
        LocalDate date4 = localDate.with(firstDayOfNextYear());
        System.out.println(date4); //2018-01-01

        //當前的以一天
        LocalDate date5 = localDate.with(firstDayOfYear());
        System.out.println(date5); //2017-01-01

        //本月第一個滿足星期三的日期
        LocalDate date6 =localDate.with(firstInMonth(DayOfWeek.WEDNESDAY));
        System.out.println(date6); //2017-06-07

        //今年的最後一天
        LocalDate date7 = localDate.with(lastDayOfYear());
        System.out.println(date7); //2017-12-31

        //當月最後一個滿足是星期四的日期
        LocalDate date8 = localDate.with(lastInMonth(DayOfWeek.TUESDAY));
        System.out.println(date8); //2017-06-30

        //下個星期天的日期
        LocalDate date9 = localDate.with(next(DayOfWeek.SUNDAY));
        System.out.println(date9); //2017-06-25
        System.out.println("localDate======="+localDate); //localDate=======2017-06-20
        LocalDate localDate2 = LocalDate.of(2017,6,20);
        LocalDate date13 = localDate2.with(nextOrSame(DayOfWeek.SUNDAY));
        System.out.println(date13);
        System.out.println("localDate2==="+localDate2);

        //上個星期天的日期
        LocalDate date10 = localDate.with(previous(DayOfWeek.SUNDAY));
        System.out.println(date10); //2017-06-18


        LocalDate date11 = localDate.with(previousOrSame(DayOfWeek.SUNDAY));
        System.out.println(date11); //2017-06-18

        LocalDate localDate1 = LocalDate.of(2017,6,7); //為本月第一個星期三
        LocalDate date12 = localDate1.with(previousOrSame(DayOfWeek.WEDNESDAY));
        System.out.println(date12); //2017-06-07
    }
}
複製程式碼

    next/previous 建立一個新的日期,並將其值設定為日期調整後或者調整前,第一個符合指定星 期幾要求的日期。

    nextOrSame/previousOrSame建立一個新的日期,並將其值設定為日期調整後或者調整前,第一個符合指定星 期幾要求的日期,如果該日期已經符合要求,直接返回該物件。

總結

    java8提供的日期-時間物件是不可變的。操作的結果總是返回一個新的實列,老的日期時間物件不會發生改變。所以提供的這些類都很簡單,但是需要我們多去使用它。

作者:二月_春風
連結:https://www.jianshu.com/p/797716dc49fb
來源:簡書
複製程式碼

相關文章