java8-新的日期API

背景

java的日期和時間API設計不理想,java8引入新的時間和日期API就是爲了解決這個問題。
老的日期API的核心類 缺點
Date 月從0開始,年最小從1900年開始,沒有時區的概念
Calendar 月從0開始
DateFormat 線程不安全
其它 同時存在Date和Calendar難以選擇; Date和Calendar類都是可變的,維護噩夢
java8引入了相似joda-time的新特性。核心類以下:

LocalDate

標識日期。下面是javadoc的翻譯:
日期沒有時區,處在ISO-8601日曆系統下。例如:2007-12-03
是一個不可變日期對象常見表示是年月日,其它的日期字段好比dayOfYear,dayOfWeek,weekOfYear也是能夠訪問的。
LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR);
舉個例子, 2007年10月2日能夠存放在LocalDate裏。
這個類沒有存儲或者表明時間或者時區。
相反,它描敘了日期,好比能夠用來表示生日,他不能表明一個時間線上的沒有附加信息的瞬間,好比一個偏移量後者時區。

這是一個基於值的類,使用標識敏感的操做,好比 == , hashCode(), 或者LocalDate對象的同步操做可能會有沒法預測的結果,而且應該避免。equals方法應該被用來比較。html

這個類是不可變而且線程安全的。java

下面是javadoc原文,不重要的內容我刪掉了。

/**
 * A date without a time-zone in the ISO-8601 calendar system,
 * such as {@code 2007-12-03}.
 * <p>
 * {@code LocalDate} is an immutable date-time object that represents a date,
 * often viewed as year-month-day. Other date fields, such as day-of-year,
 * day-of-week and week-of-year, can also be accessed.
 * For example, the value "2nd October 2007" can be stored in a {@code LocalDate}.
 * <p>
 * This class does not store or represent a time or time-zone.
 * Instead, it is a description of the date, as used for birthdays.
 * It cannot represent an instant on the time-line without additional information
 * such as an offset or time-zone.
 * <p>
 * This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>
 * class; use of identity-sensitive operations (including reference equality
 * ({@code ==}), identity hash code, or synchronization) on instances of
 * {@code LocalDate} may have unpredictable results and should be avoided.
 * The {@code equals} method should be used for comparisons.

 * This class is immutable and thread-safe.
public final class LocalDate
        implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable

file

主要有4種構造方法:segmentfault

構造方法種類 說明
of類 有4個, of(int year,int month, int day) , of(int year, Month month, int day) , ofYearDay(int year, int dayofYear), ofEpochday(long epochDay) 1970年1月1日爲機器元年
now類3個 實際上重載了now(Clock clock), 其它兩個 now(), now(ZoneId zoneId) 取得是機器在某個時區的始終,取的日期,而後調用ofEpochday(long epochDay)來初始化日期
from類1個 from(TemporalAccessor temporal) 經過一個Temporal對象來初始化日期,具體用法見例子
parse類2個 pase(string date),parse(String date , DateTimeFormatter formater)以字符串的方式初始化日期

獲取類方法:
fileapi

獲取類方法 說明
getYear() 獲取年
getMonthValue() 獲取月份數值
getMonth() 獲得月份對象
getDayOfMonth 獲得月份的天數值
getDayOfWeek 獲得一週的星期數
package com.test.time;

import java.time.LocalDate;
import java.time.chrono.ChronoLocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Objects;

/**
 * 說明:localDate類研究
 * @author carter
 * 建立時間: 2019年11月11日 15:31
 **/

public class LocalDateTest {

    public static void main(String[] args) {

        LocalDate localDate = LocalDate.of(2019,11,11);


        print("localDate.getYear()",localDate.getYear());
        print("localDate.getMonth().getValue()",localDate.getMonth().getValue());
        print("localDate.lengthOfMonth()",localDate.lengthOfMonth());
        print("localDate.getMonthValue()",localDate.getMonthValue());
        print("localDate.getDayOfMonth()",localDate.getDayOfMonth());

        print("localDate.getDayOfWeek().getValue()",localDate.getDayOfWeek().getValue());
        print("localDate.getDayOfYear()",localDate.getDayOfYear());
        print("localDate.lengthOfYear()",localDate.lengthOfYear());

        print("localDate.getChronology()",localDate.getChronology());
        print("localDate.getEra()",localDate.getEra());
        print("localDate.isLeapYear()",localDate.isLeapYear());


        final LocalDate localDateNow = LocalDate.now();

        print("localDateNow.atStartOfDay()",localDateNow.atStartOfDay());

        final LocalDate localDateOfEpo = LocalDate.ofEpochDay(1L);

        print("localDateOfEpo.format(DateTimeFormatter.BASIC_ISO_DATE)",localDateOfEpo.format(DateTimeFormatter.BASIC_ISO_DATE));

        final LocalDate localDateFrom = LocalDate.from(new TemporalAccessor() {
            @Override
            public boolean isSupported(TemporalField field) {
                return true;
            }

            @Override
            public long getLong(TemporalField field) {
                //這塊實際上設置的是epochDay,即機器的增量日期
                return 2;
            }
        });

        print("localDateFrom.format(DateTimeFormatter.BASIC_ISO_DATE)",localDateFrom.format(DateTimeFormatter.BASIC_ISO_DATE));

        final LocalDate localDateParse = LocalDate.parse("2019-11-11");

        print("localDateParse.format(DateTimeFormatter.BASIC_ISO_DATE)",localDateParse.format(DateTimeFormatter.BASIC_ISO_DATE));

    }


    private static void print(String title,Object printContent){

        System.out.println(title + " : " + Objects.toString(printContent));
    }

}

輸出:安全

localDate.getYear() : 2019
localDate.getMonth().getValue() : 11
localDate.lengthOfMonth() : 30
localDate.getMonthValue() : 11
localDate.getDayOfMonth() : 11
localDate.getDayOfWeek().getValue() : 1
localDate.getDayOfYear() : 315
localDate.lengthOfYear() : 365
localDate.getChronology() : ISO
localDate.getEra() : CE
localDate.isLeapYear() : false
localDateNow.atStartOfDay() : 2019-11-12T00:00
localDateOfEpo.format(DateTimeFormatter.BASIC_ISO_DATE) : 19700102
localDateFrom.format(DateTimeFormatter.BASIC_ISO_DATE) : 19700103
localDateParse.format(DateTimeFormatter.BASIC_ISO_DATE) : 20191111
其它的方法先不作探究,後面會研究到。先提出這塊的代碼參考。

LocalTime

跟LocalDate相似,javadoc相似,區別是它能夠標示的值一般表現爲時分秒,能夠精確到納秒。

file

經過獲取機器時間的時分秒納秒部分來初始化。app

package com.test.time;

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

/**
 * 說明:localTime類研究
 * @author carter
 * 建立時間: 2019年11月12日 10:35
 **/

public class LocalTimeTest {

    public static void main(String[] args) {

        final LocalTime localTime = LocalTime.of(12, 0, 0, 0);

        System.out.println(localTime);
        System.out.println( "LocalTime.hour"+ " : "+localTime.getHour());
        System.out.println( "LocalTime.getMinute"+ " : "+localTime.getMinute());
        System.out.println( "LocalTime.getSecond"+ " : "+localTime.getSecond());
        System.out.println( "LocalTime.getNano"+ " : "+localTime.getNano());

        System.out.println( "LocalTime.MIDNIGHT"+ " : "+LocalTime.MIDNIGHT);
        System.out.println("LocalTime.NOON"+ " : "+LocalTime.NOON);
        System.out.println("LocalTime.MIN"+ " : "+LocalTime.MIN);
        System.out.println("LocalTime.MAX"+ " : "+LocalTime.MAX);

    }


}

LocalDateTime

是localDate,localTime的合體,標示日期時間。
package com.test.time;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;

/**
 * 說明:localdatetime的簡單研究
 * @author carter
 * 建立時間: 2019年11月12日 10:47
 **/

public class LocalDateTimeTest {

    public static void main(String[] args) {

        LocalDateTime localDateTime = LocalDateTime.now();


        System.out.println("localDateTime.getYear()" + " : " + localDateTime.getYear());
        System.out.println("localDateTime.getMonthValue()" + " : " + localDateTime.getMonthValue());
        System.out.println("localDateTime.getDayOfMonth()" + " : " + localDateTime.getDayOfMonth());
        System.out.println("localDateTime.getHour()" + " : " + localDateTime.getHour());
        System.out.println("localDateTime.getMinute()" + " : " + localDateTime.getMinute());
        System.out.println("localDateTime.getSecond()" + " : " + localDateTime.getSecond());
        System.out.println("localDateTime.getNano()" + " : " + localDateTime.getNano());

        final LocalDateTime ofEpochSecond = LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0,ZoneOffset.ofHours(8));

        System.out.println("ofEpochSecond" + " : " + ofEpochSecond);


        //經過localdatetime獲取localdate, localtime,也能夠經過localdate,localtime獲得localdatetime

        final LocalDate localDate = localDateTime.toLocalDate();

        final LocalTime localTime = localDateTime.toLocalTime();

        System.out.println("localDate" + " : " + localDate);
        System.out.println("localTime" + " : " + localTime);

        final LocalDateTime localDateTimeFromLocalDate = localDate.atTime(LocalTime.MIN);

        final LocalDateTime localDateTimeFromLoalTime = localTime.atDate(LocalDate.now());
        System.out.println("localDateTimeFromLocalDate" + " : " + localDateTimeFromLocalDate);
        System.out.println("localDateTimeFromLoalTime" + " : " + localDateTimeFromLoalTime);


    }

}

Instant

機器的時間模型:是以機器元年1970年1月1日經歷的秒數來計算;等同於LocalDateTime. 可是隻能從中獲取秒和納秒,沒法獲取年月日時分等時間。
package com.test.time;

import java.time.Instant;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2019年11月12日 11:08
 **/

public class InstanceTest {


    public static void main(String[] args) {


        Instant instant1 = Instant.ofEpochSecond(3);
        Instant instant2 = Instant.ofEpochSecond(2,1000000000);

        System.out.println("instant1" + "  :  " + instant1);
        System.out.println("instant2" + "  :  " + instant2);

        System.out.println(instant1.get(ChronoField.DAY_OF_YEAR));

    }
}
instant1 : 1970-01-01T00:00:03Z

instant2 : 1970-01-01T00:00:03Zide

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfYearui

at java.time.Instant.get(Instant.java:566)
at com.test.time.InstanceTest.main(InstanceTest.java:25)

LocalDate,LocalTime.LocalDateTime.Instant都實現Temporal接口,能夠讀取和設置時間,接下來的Duration,Period是基於兩個Temporal接口來建模的。spa


Duration

主要獲取秒和納秒的單位,不可計算LocalDate的時間差;

file
構造方法:線程

構造方法種類 說明
of系列 ofDays(long days),ofHours(long hours),ofMinutes(long minutes),ofSeconds(long seconds),ofSeconds(long secends,long nano),ofMilis(long minis),ofNanos(long nanos),of(long time,TemporalUnit)最後經過of方法重載
from類 from(TemporalAmount amount)
parse類
between類 兩個Temporal之間的差值
package com.test.time;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;
import java.util.List;

/**
 * 說明:Duration
 * @author carter
 * 建立時間: 2019年11月12日 13:09
 **/

public class DurationTest {


    public static void main(String[] args) {

        final Duration duration = Duration.between(LocalDateTime.of(1988, 1, 2, 0, 0, 0), LocalDateTime.now());

        System.out.println("duration "+ " : " + duration);

        System.out.println("duration.toDays() "+ " : " + duration.toDays());
        System.out.println("duration.toHours() "+ " : " + duration.toHours());
        System.out.println("duration.toMinutes() "+ " : " + duration.toMinutes());


        final Duration durationHours = Duration.ofHours(1);

        System.out.println("durationHours "+ " : " + durationHours);
        System.out.println("durationHours.toHours() "+ " : " + durationHours.toHours());
        System.out.println("durationHours.toMinutes() "+ " : " + durationHours.toMinutes());
        System.out.println("durationHours.getSeconds() "+ " : " + durationHours.getSeconds());


        final Duration fromDuration = Duration.from(Duration.ofDays(1));

        System.out.println(":" + fromDuration);

        final Duration durationParse = Duration.parse("PT279301H32M6.488S");

        System.out.println(durationParse);

    }
}
duration  : PT279301H32M50.61S
duration.toDays()  : 11637
duration.toHours()  : 279301
duration.toMinutes()  : 16758092
durationHours  : PT1H
durationHours.toHours()  : 1
durationHours.toMinutes()  : 60
durationHours.getSeconds()  : 3600
:PT24H
PT279301H32M6.488S

Period

能夠獲取年,月,日的差值;
package com.test.time;

import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2019年11月12日 13:04
 **/

public class TestPeriod {

    public static void main(String[] args) {


        final Period period = Period.between(LocalDate.of(1988, 1, 2), LocalDate.now());

        System.out.println(period);

        System.out.println(period.getYears());
        System.out.println(period.getMonths());
        System.out.println(period.getDays());
        System.out.println(period.get(ChronoUnit.DAYS));


        System.out.println( period.toTotalMonths());

    }

}

ChronoUnit

也能夠用來計算差值,沒有限制。
final long daysBetween = ChronoUnit.YEARS.between(LocalDate.of(1988, 1, 2), LocalDate.now());
System.out.println(daysBetween);

操縱和格式化日期

操做類 說明
with類 設置時間的數值,with(ChronoField field,Long newvalue)
plus 加多少時間單位
minus 減掉多少時間

file

with(TemproalAdjuster adjuster)

提供了快速的方法去設置時間。

快速方法 說明
firstDayOfMonth 設置爲月的第一天
lastDayOfMonth 設置爲月的最後一天
firstDayOfNextMonth 下月的第一天
firstDayOfYear 年的第一天
lastDayOfYear 年的最後一天
firstDayOfNextYear 下年的第一天
lastInMonth 最後一個符合星期幾的
package com.test.time;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.FormatStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalField;

/**
 * 說明:日期操做和格式化
 * @author carter
 * 建立時間: 2019年11月12日 13:58
 **/

public class OpTest {

    public static void main(String[] args) {

        final LocalDate localDate = LocalDate.now().withYear(1988).withDayOfYear(2).with(ChronoField.MONTH_OF_YEAR, 2L);

        System.out.println(localDate);


        final LocalTime localTime = LocalTime.now().plusHours(1).plusHours(1).plusMinutes(10);

        System.out.println(localTime);


        final LocalDate localDate1 = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
        System.out.println(localDate1);

        final LocalDate localDate2 = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY));
        System.out.println("本月的第一個星期五:" + localDate2);

        final LocalDate localDate3 = LocalDate.now().with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));
        System.out.println("本月的最後一個星期五:" + localDate3);


        final LocalDate localDate4 = LocalDate.now().withDayOfMonth(15);
        System.out.println( localDate4 +  "下一個工做日是: " + localDate4.with(getNextWorkDay(localDate4)));


        System.out.println(localDate4.format(DateTimeFormatter.ofPattern("YYYY-MM-dd")));
        System.out.println(localDate4.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)));
        System.out.println(localDate4.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)));
        System.out.println(localDate4.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
        System.out.println(localDate4.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)));

        System.out.println(localDate4.format(new DateTimeFormatterBuilder().appendText(ChronoField.YEAR).appendLiteral("====").appendText(ChronoField.MONTH_OF_YEAR).toFormatter()));

    }


    public static LocalDate getNextWorkDay(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.ofDateAdjuster((temp) -> {
            final DayOfWeek dayOfWeek = temp.getDayOfWeek();
            if (dayOfWeek.getValue() == 5) {
                return temp.plusDays(3);
            } else if (dayOfWeek.getValue() == 6) {
                return temp.plusDays(2);
            } else {
                return temp.plusDays(1);
            }

        }));
    }


}

處理不一樣的時區

時間是相對時區來講的,時區是按照必定規則將區域劃分紅標準時間相同的區間。獲得一個時區以後,zoneId,能夠結合LocalDate,LocalDateTime,Instant 結合起來,構造一個ZonedDateTime
package com.test.time;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.TimeZone;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2019年11月12日 14:52
 **/

public class ZoneTest {

    public static void main(String[] args) {


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

        System.out.println(zoneId);

        final ZoneId zoneId1 = ZoneId.of("Asia/Shanghai");

        System.out.println(zoneId1.toString());


        //LocalDateTime和 Instant互相轉換;

        LocalDateTime localDateTime = LocalDateTime.now();
        final ZoneOffset zoneOffset = ZoneOffset.of("+8");
        Instant instant = localDateTime.toInstant(zoneOffset);

        System.out.println(localDateTime);

        System.out.println(instant);

        LocalDateTime localDateTime1 = LocalDateTime.ofInstant(instant,ZoneId.systemDefault());

        System.out.println(localDateTime1);

        //基於時區的日期1
        ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(),ZoneId.systemDefault());
        System.out.println(zonedDateTime);

        //基於時區的日期2
        OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.now(),zoneOffset);
        System.out.println(offsetDateTime);


    }

}

處理不一樣的日曆系統

ISO-8601日曆系統是世界文明日曆系統的事實標準。可是,Java 8中另外還提供了4種其餘的 日曆系統。這些日曆系統中的每個都有一個對應的日誌類,分別是ThaiBuddhistDate、 MinguoDate 、 JapaneseDate 以 及 HijrahDate 。
//泰國佛教日期
        final ThaiBuddhistDate thaiBuddhistDate = ThaiBuddhistDate.from(LocalDateTime.now());

        System.out.println(thaiBuddhistDate);

        //民國日期
        final MinguoDate minguoDate = MinguoDate.from(LocalDateTime.now());

        System.out.println(minguoDate);

小結

java8以前的老版本的Date,Calendar有不少設計缺陷,本身設計的api中應該退出歷史舞臺。 新版的日期API中日期都是不可變的,而且是線程安全的; 新版的日期API中區別人和機器的方式標識時間; 你能夠靈活的改變日期,或者經過更靈活的TemporalAdjuster來操做日期 你能夠相對於某個時區和位置與格林尼治時間的誤差的方式來小時時區,並應用到日期和時間對象上進行本地化 原創不易,轉載請註明出處,歡迎溝通交流。
相關文章
相關標籤/搜索