現實生活的世界裏,時間是不斷向前的,若是向前追溯時間的起點,多是宇宙出生時,又或是是宇宙出現以前,
但確定是咱們目前沒法找到的,咱們不知道如今距離時間原點的精確距離。因此咱們要表示時間,
就須要人爲定義一個原點。java
原點被規定爲,格林威治時間(GMT)1970年1月1日的午夜 爲起點,之於爲啥是GMT時間,大概是由於本初子午線在那的緣由吧。sql
若是你跟你朋友說:「咱們 1484301456 一塊兒去吃飯,別遲到!」,而你朋友能立刻理解你說的時間,表示時間就會很簡單,
只須要一個long值來表示原點的偏移量,這是個絕對時間,在世界範圍內都適用。但實際上咱們不能立刻理解這串數字,
並且咱們須要不一樣的時間單位來表示時間的跨度,好比一個季度是3個月,一個月有30天等。
你能夠跟朋友約好「明天這個時候再見面」,你朋友很容易理解明天的意思,但要是沒有’天’這個單位,
他就須要在那串數字上加上86400(一天是86400秒)。數據庫
Java三次引入處理時間的API,JDK1.0中包含了一個Date
類,但大多數方法在java1.1引入Calendear
類以後被棄用了。
它的實例都是可變的,並且它的API很難使用,好比月份是從0開始這種反人類的設置。api
java8引入的java.time
API 已經糾正了以前的問題。它已經徹底實現了JSR310
規範。安全
在新的時間API中,Instant
表示一個精確的時間點,Duration
和Period
表示兩個時間點之間的時間量。 LocalDate
表示日期,即xx年xx月xx日,即不包括時間也不帶時區。LocalTime
與LocalDate
相似,
但只包含時間。LocalDateTime
則包含日期和時間。ZoneDateTime
表示一個帶時區的時間。 DateTimeFormatter
提供格式化和解析功能。下面詳細的介紹使用方法。markdown
Instant
表示一個精確的時間,時間數軸就是由無數個時間點組成,數軸的原點就是上面提
到的1970-1-1 00:00:00
,Instant
由兩部分組成,一是從原點開始到指定時間點的秒數s,
二是距離該秒數s的納秒數。ide
使用靜態方法Instant.now()
能夠獲取當前的時間點,該方法默認使用的是UTC(協調世界時——由原子鐘提供)時間,可使用equeal
和 compareTo
來比較兩個時間點的值。post
計算某段代碼執行時間可使用下面的方式:測試
Instant start = Instant.now(); doSomething(); Instant end = Instant.now(); Duration timeElapsed = Duration.between(start, end); long millis = timeElapsed.toMillis(); System.out.println("millis = " + millis);
Duration對象表示兩個時間點之間的距離,經過相似toMillis()
toDays()
getSeconds()
等方法,
獲得各類時間單位表示的Duration對象。若是確實須要使用納秒來作一些計算,能夠調用toNanos()
得到一個long類型的值,該值表示距離原點的納秒值。大概300年的納秒值會致使long值溢出。spa
Duration內部使用一個long類型來保存秒鐘的值,使用一個int來保存納秒的值,與Instant相似,
這個納秒保存的是距離該秒鐘的納秒值.
Instant與Duration均可以進行一些運算,來調整表示的時間,好比:plus()
minus
方法,
表示增長或減小一段時間,plusSeconds()
minusSeconds()
plusXxx()
等表示增長或減小相應時間單位的一段時間。
Duration能夠進行multipliedBy()
乘法和dividedBy()
除法運算。negated()
作取反運算,即1.2秒取反後爲-1.2秒。
很是重要的是,Instant 和 Duration類都是不可變的,他們的全部方法都返回一個新的實例。不可變類有不少優勢:
不可變類使用起來不容易出錯,其本質上是線程安全的,對象能夠被自由的共享,而不用擔憂被某個方法修改。
上面介紹的Instant是一個絕對的準確時間點,是人類不容易理解的時間,如今介紹人類使用的時間。
LocalDate 表示像 2017-01-01
這樣的日期。它包含有年份、月份、當月天數,它不不包含一天中的時間,
以及時區信息。因爲上面的這些特色,因此LocalDate不能表示一個準確的時間點,即Instant。
有不少時間的計算是不須要時區的,並且有一些狀況下使用時區會致使一些問題,例如你在中國設置了一個 2017-01-01 UT+8:00
的放假提醒,但以後你去了美國,到了2017-01-01 UT+8:00
時間時你收到了提醒,
可是此時美國還沒到放假的時間。
API的設計者推薦使用不帶時區的時間,除非真的但願表示絕對的時間點。
可使用靜態方法now()
和of()
建立LocalDate。java.util.Date
使用0做爲月份的開始,年份從1990年開始算起,
而新的API中徹底是用生活中同樣的方式來表示年和月份。
//獲取當前日期 LocalDate now = LocalDate.now(); //2017-01-01 LocalDate newYear = LocalDate.of(2017, 1, 1);
能夠經過一些方法對日期作一些運算。
//三天後 now.plusDays(3); //一週後 now.plusWeeks(1) //兩天前 now.minusDays(2) //增長一個月不會出現2017-02-31 而是會返回該月的最後一個有效日期,即2017-02-28 LocalDate.of(2017, 1, 31).plusMonths(1) LocalDate feb = LocalDate.of(2017, 2, 1); //withXxx()表示以該日期爲基礎,修改年、月、日字段,並返回一個新的日期 //2019-2-1 feb.withYear(2019); //2017-1-10 feb.withDayOfYear(10); //2017-2-10 feb.withDayOfMonth(10);
上面講過Duration表示的是Instant對應的時間段,LocalDate對應的表示時間段的是Period,
Period內部使用三個int值分表表示年、月、日。
Duration和Period都是TemporalAmount接口的實現,該接口表示時間量。
LocalDate 也能夠增長或減小一段時間:
//2019-02-01 feb.plus(Period.ofYears(2)); //2015-02-01 feb.minus(Period.ofYears(2);
使用until得到兩個日期之間的Period對象
//輸出P9D,表示相差9天 feb.until(LocalDate.of(2017, 2, 10));//輸出---> P9D
LocalDate提供了一些測試方法: isBefore
isAfter
比較兩個LocalDate,isLeapYear
判斷是不是閏年。
LocalDate還提供了各類getXxx方法來返回所須要的數據,其中getDayOfWeek()
返回DayOfWeek
枚舉。 DayOfWeek
提供了plus
minus
來方便計算星期。
//SUNDAY
LocalDate.of(2017, 1, 1).getDayOfWeek(); //TUESDAY DayOfWeek.SUNDAY.plus(2);
除了LocalDate,Java8還提供了Year
MonthDay
YearMonth
來表示部分日期,例如MonthDay
能夠表示1月1日。
若是想找到某個月的第一個週五,或是某個月的最後一天,像這樣的日期就可使用TemporalAdjuster
來進行日期調整。 TemporalAdjusters
提供一些靜態方法,返回經常使用的TemporalAdjuster
。
//2017-02-03的下一個星期五(包含當天) 2017-03-03 LocalDate.of(2017, 2, 3).with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY)); //2017-02-03的下一個星期五(不包含當天) 2017-02-10 LocalDate.of(2017, 2, 3).with(TemporalAdjusters.next(DayOfWeek.FRIDAY)); //2月中的第3個星期五 2017-02-17 LocalDate.of(2017, 2, 3).with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.FRIDAY)); //2月中的最後一個星期五 2017-02-24 LocalDate.of(2017, 2, 3).with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)); //下個月的第一天 LocalDate.of(2017, 2, 3).with(TemporalAdjusters.firstDayOfNextMonth());
這是上面例子對應的當月日曆
LocalTime表示一天中的某個時間,例如18:00:00
。LocaTime與LocalDate相似,他們也有類似的API。
須要注意的是:LocalTime自己不關心是AM仍是PM,而是格式化程序來負責這個事情。
LocalDateTime表示一個日期和時間,它適合用來存儲肯定時區的某個時間點。不適合跨時區的問題。
若須要處理跨時區的時間,須要使用ZonedDateTime.
時區(Time Zone)是地球上的區域使用同一個時間定義。1884年在華盛頓召開國際經度會議時,
爲了克服時間上的混亂,規定將全球劃分爲24個時區。
因爲實用上經常1個國家,或1個省份同時跨着2個或更多時區,爲了照顧到行政上的方便,
常將1個國家或1個省份劃在一塊兒。因此時區並不嚴格按南北直線來劃分,而是按天然條件來劃分。
Java使用ZoneId
來標識不一樣的時區.
//得到全部可用的時區 size=590 ZoneId.getAvailableZoneIds(); //獲取默認ZoneId對象 ZoneId defZoneId = ZoneId.systemDefault(); //獲取指定時區的ZoneId對象 ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai"); //ZoneId.SHORT_IDS返回一個Map<String, String> 是時區的簡稱與全稱的映射。下面能夠獲得字符串 Asia/Shanghai String shanghai = ZoneId.SHORT_IDS.get("CTT");
我在測試的時候一共有590個時區可用,但要知道,這個時區的個數不是固定的。
IANA(Internet Assigned Numbers Authority,因特網撥號管理局)維護着一份全球全部已知的時區數據庫,
每一年會更新幾回,主要處理夏令時規則的改變。Java使用了IANA的數據庫。
//2017-01-20T17:35:20.885+08:00[Asia/Shanghai] ZonedDateTime.now(); //2017-01-01T12:00+08:00[Asia/Shanghai] ZonedDateTime.of(2017, 1, 1, 12, 0, 0, 0, ZoneId.of("Asia/Shanghai")); //使用一個準確的時間點來建立ZonedDateTime,下面這個代碼會獲得當前的UTC時間,會比北京時間早8個小時 ZonedDateTime.ofInstant(Instant.now(), ZoneId.of("UTC"));
//atZone方法能夠將LocalDateTime轉換爲ZonedDateTime,下面的方法將時區設置爲UTC。 //假設如今的LocalDateTime是2017-01-20 17:55:00 轉換後的時間爲2017-01-20 17:55:00[UTC] LocalDateTime.now().atZone(ZoneId.of("UTC")); //使用靜態of方法建立zonedDateTime ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("UTC"));
ZonedDateTime的許多方法與LocalDateTime、LocalDate、LocalTime相似,下面簡單介紹幾個方法的使用。
ZonedDateTime utcDateTime = ZonedDateTime.of(2017, 1, 1, 12, 0, 0, 0, ZoneId.of("UTC"));//2017-01-01T12:00Z[UTC] //withZoneSameLocal返回指定時區中的一個新ZonedDateTime,替換時區爲指定時區,表示相同的本地時間的該時區時間。 utcDateTime.withZoneSameLocal(ZoneId.of("Asia/Shanghai"));//2017-01-01T12:00+08:00[Asia/Shanghai] //withZoneSameInstant返回指定時區中的一個新ZonedDateTime,替換爲指定時區,表示相同時間點的該時區時間。 utcDateTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));//2017-01-01T20:00+08:00[Asia/Shanghai]
有一些國家和地區使用夏令時,處理起來須要注意,但在中國沒有該問題,
須要注意的是使用plus()
時要用Period
對象表示的時間量,而不該該用Duration
表示的時間量, Duration
不能處理夏令時。
utcDateTime.plus(Duration.ofDays(7));//不能處理夏令時 utcDateTime.plus(Period.ofDays(7));//正確方式
DateTimeFormatter
是不可變類,而SimpleDateFormat
是非線程安全的,是一個常見的坑。
DateTimeFormatter使用了三種格式化方法來打印日期和時間
DateTimeFormatter
預約義了一些格式,能夠直接調用format方法
//2017-01-01 DateTimeFormatter.ISO_LOCAL_DATE.format(LocalDate.of(2017, 1, 1)) //20170101 DateTimeFormatter.BASIC_ISO_DATE.format(LocalDate.of(2017, 1, 1)); //2017-01-01T09:10:00 DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.of(2017, 1, 1, 9, 10, 0));
語言環境相關的格式化風格
根據當前操做系統語言環境,有SHORET
MEDIUM
LONG
FULL
四種不一樣的風格來格式化。
能夠經過DateTimeFormatter
的靜態方法ofLocalizedDate
ofLocalizedTime
ofLocalizedDateTime
//2017年1月1日 星期日 DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(LocalDate.of(2017, 1, 1)); //上午09時10分00秒 DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG).format(LocalTime.of(9, 10, 0)); //2017-2-27 22:32:03 DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).format(LocalDateTime.now());
上面的方法都使用的是默認的語言環境,若是想改語言環境,須要使用withLocale
方法來改變。
//Feb 27, 2017 10:34:36 PM DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.US).format(LocalDateTime.now());
使用自定義模式格式化
//2017-02-27 22:48:52 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now())
//使用的ISO_LOCAL_DATE格式解析 2017-01-01 LocalDate.parse("2017-01-01"); //使用自定義格式解析 2017-01-01T08:08:08 LocalDateTime.parse("2017-01-01 08:08:08", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Instant
相似於java.util.Date
ZonedDateTime
相似於java.util.GregorianCalendar
//Date --> Instant Instant timestamp = new Date().toInstant(); //Instant --> Date Date.from(Instant.now()); //GregorianCalendar --> ZonedDateTime new GregorianCalendar().toZonedDateTime(); //ZonedDateTime --> GregorianCalendar GregorianCalendar.from(zonedDateTime); //2017-02-27T21:16:13.647 LocalDateTime.ofInstant(timestamp, ZoneId.of(ZoneId.SHORT_IDS.get("PST"))); //Calendar --> Instant //2017-02-28T05:16:13.656Z Calendar.getInstance().toInstant();