Java8
日期/時間API
是JSR-310(Java Specification Requests)
的實現(xiàn)微饥,它的實現(xiàn)目標(biāo)是克服舊的日期時間實現(xiàn)中所有的缺陷,利用它可以更加方便我們?nèi)ヌ幚頃r間和日期的計算等問題。
設(shè)計原則
新的日期/時間API
它遵循以下的設(shè)計原則:
-
不變性:在新的日期/時間
API
中蟀淮,所有的類都被設(shè)計成不可變的蚪腐,這在多線程環(huán)境下也是沒有問題的。 -
關(guān)注點分離:在
Java8
中侨糟,相比于之前舊的時間和日歷類碍扔,較大的改變是將人可讀的日期時間和機(jī)器時間(unix timestamp)
明確分離,為日期(Date)
秕重、時間(Time)
不同、日期時間(DateTime)
、時間戳(unix timestamp)
以及時區(qū)定義了不同的類溶耘。 -
清晰:在所有的類中二拐,方法都被明確定義用以完成相同的行為。舉個例子凳兵,要拿到當(dāng)前實例我們可以使用
now()
方法百新,在所有的類中都定義了format()
和parse()
方法,而不是像以前那樣專門有一個獨立的類庐扫。為了更好的處理問題饭望,所有的類都使用了工廠模式和策略模式,一旦你使用了其中某個類的方法形庭,與其他類協(xié)同工作并不困難铅辞。 -
實用操作:所有新的日期/時間
API
類都實現(xiàn)了一系列方法用以完成通用的任務(wù),如:加碘勉、減巷挥、 格式化、解析验靡、從日期/時間中提取單獨部分倍宾,等等雏节。 -
可擴(kuò)展性: 新的日期/時間
API
是工作在ISO-8601
日歷系統(tǒng)上的,但我們也可以將其應(yīng)用在非ISO
的日歷上高职。
time 包
java.time
包中的是類是不可變且線程安全的钩乍,下面是一些比較常用的類:
-
Instant
:表示時間戳。 -
LocalDate
:不包含具體時間點的日期怔锌。 -
LocalTime
:不包含日期的時間點寥粹。 -
LocalDateTime
:具體的日期時間點,不帶時區(qū)埃元。 -
ZonedDateTime
:包含時區(qū)的完整的日期時間點涝涤,偏移量是以UTC
/格林威治時間為基準(zhǔn)的。
常見用法
對于這些類岛杀,它們都有以下一些方法:
-
of
:靜態(tài)工廠方法阔拳。 -
parse
:靜態(tài)工廠方法,關(guān)注于解析类嗤。 -
get
:獲取某些東西的值糊肠。 -
is
:檢查某些東西的是否是true。 -
with
:不可變的setter等價物遗锣。 -
plus
:加一些量到某個對象货裹。 -
minus
:從某個對象減去一些量。 -
to
:轉(zhuǎn)換到另一個類型精偿。 -
at
:把這個對象與另一個對象組合起來弧圆。
下面我們就來看看他們具體都是怎么用的。
計算日期/時間
獲取當(dāng)前時間/日期
以前有Date
類可以獲取當(dāng)前時間还最,有Calendar
類來做日歷相關(guān)操作墓阀。而在Java8
中毡惜,提供了多個類來獲取當(dāng)前的日期拓轻、時間、時間戳等信息经伙。
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前日期
System.out.println(LocalDate.now());
// 獲取當(dāng)前時間點
System.out.println(LocalTime.now());
// 獲取當(dāng)前時間點去掉納秒
System.out.println(LocalTime.now().withNano(0));
// 獲取當(dāng)前的日期時間
System.out.println(LocalDateTime.now());
// 獲取當(dāng)前年
System.out.println(LocalDate.now().getYear());
}
}
輸出:
2017-09-04
22:03:49.502
22:03:49
2017-09-04T22:03:49.502
2017
不要在意這個雞毛年份扶叉,文章寫于N
久前,最近改改重新發(fā)出來的帕膜。
判斷兩個日期是否相等
在Java8
的眾多類中枣氧,它們都重寫了toString()
方法。所以對于日期是否相同的判斷垮刹,我們可以直接使用它的equals()
方法达吞。
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前日期
LocalDate today = LocalDate.now();
// 構(gòu)建2017-09-03
LocalDate date = LocalDate.of(2017, 9, 3);
// 判斷是否相等
System.out.println(today.equals(date));
}
}
輸出:
false
判斷一個日期是否在另一個日期之前
有時候可能會有這樣的需求,需要判斷一個日期在另一個日期的前面還是后面荒典,這時候就會用到isBefore()
和isAfter()
方法酪劫,這樣就可以方便的作出判斷吞鸭。
public class Test {
public static void main(String[] args) {
// 構(gòu)建2017-09-04
LocalDate d1 = LocalDate.of(2017, 9, 4);
// 構(gòu)建2017-09-03
LocalDate d2 = LocalDate.of(2017, 9, 3);
// 判斷d1是否在d2后面
System.out.println(d1.isAfter(d2));
// 判斷d1是否在d2前面
System.out.println(d1.isBefore(d2));
}
}
輸出:
true
false
可以看到我們直接可以使用isBefore()
和isAfter()
來判斷兩個日期誰前誰后了,而不需要向之前使用Calendar
來進(jìn)行麻煩的操作了覆糟。
檢查重復(fù)日期
對于一些特定的日期刻剥,比如生日、紀(jì)念日等滩字,我們可以輕易來判斷某一日期是不是我們需要的特定日期造虏。
public class Test {
public static void main(String[] args) {
LocalDate dateOfBirth = LocalDate.of(1992, 9, 4);
MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(LocalDate.now());
if(currentMonthDay.equals(birthday)){
System.out.println("Happy Birthday !");
}else{
System.out.println("Sorry, today is not your birthday!");
}
}
}
輸出:
Happy Birthday !
判斷某個日期是不是周幾
同樣麦箍,我們可以很方便的來判斷某天是周幾漓藕。DayOfWeek
構(gòu)建的是周幾,然后使用from()
方法挟裂,來構(gòu)建某一日期為周幾撵术,這樣我們就可以輕松判斷出某一日期是周幾,而不必向以前一樣麻煩话瞧。
public class Test {
public static void main(String[] args) {
DayOfWeek dayOfWeek = DayOfWeek.of(2);
DayOfWeek from = DayOfWeek.from(LocalDate.now());
System.out.println(from.equals(dayOfWeek));
}
}
輸出:
true
延遲或者推前時間
有時候我們可能會需要用到一些日期計算嫩与,比如,昨天交排,前天划滋,明天,一周前埃篓,一年前等处坪。在Java8中可以輕松實現(xiàn),因為這些類中已經(jīng)提供了相關(guān)方法架专。plus
開頭的表示往后算同窘,minus
表示往前算。
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前日期
LocalDate today = LocalDate.now();
System.out.println(today);
// 推前一天
System.out.println(today.minusDays(1));
// 延后一天
System.out.println(today.plusDays(1));
}
}
輸出:
2017-09-04
2017-09-03
2017-09-05
時鐘
Java8
提供了時鐘類部脚,利用時鐘類可以實現(xiàn)和System.currentTimeMillis()
一樣的功能想邦,還能夠獲取當(dāng)前時區(qū)。
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前時間戳
System.out.println(Clock.systemUTC().millis());
// 獲取當(dāng)前時間戳
System.out.println(System.currentTimeMillis());
// 獲取當(dāng)前系統(tǒng)默認(rèn)時區(qū)
System.out.println(Clock.systemDefaultZone().getZone());
}
}
輸出:
1504478880531
1504478880531
Asia/Shanghai
檢查閏年
可以使用isLeapYear()
方法直接判斷是否為閏年委刘,而不用我們再自己去計算丧没。
public class Test {
public static void main(String[] args) {
System.out.println(LocalDate.of(2020, 1, 1).isLeapYear());
}
}
輸出:
true
帶時區(qū)計算
Java 8
不僅將日期和時間進(jìn)行了分離,同時還有時區(qū)∥疲現(xiàn)在已經(jīng)有好幾組與時區(qū)相關(guān)的類了呕童,比如ZonId
代表的是某個特定的時區(qū),而ZonedDateTime
代表的是帶時區(qū)的時間淆珊。它等同于Java 8
以前的GregorianCalendar
類夺饲。使用這個類,你可以將本地時間轉(zhuǎn)換成另一個時區(qū)中的對應(yīng)時間。用ZoneOffset
類來代表某個時區(qū)往声,比如印度是GMT
或者UTC5:30
茫蛹,你可以使用它的靜態(tài)方法ZoneOffset.of()
方法來獲取對應(yīng)的時區(qū)。只要獲取到了這個偏移量烁挟,你就可以拿LocalDateTime
和這個偏移量創(chuàng)建出一個OffsetDateTime
婴洼。
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前時間
LocalDateTime now = LocalDateTime.now();
// 設(shè)置時區(qū)
ZonedDateTime zonedDateTime = ZonedDateTime.of(now, ZoneId.of("Australia/Darwin"));
System.out.println("Current date and time in a particular timezone : " + zonedDateTime);
// 構(gòu)建一個時間
LocalDateTime datetime = LocalDateTime.of(2017, Month.SEPTEMBER, 5, 7, 50);
// 設(shè)置偏移量
ZoneOffset offset = ZoneOffset.of("+05:30");
// 構(gòu)建帶偏移量的日期和時間
OffsetDateTime date = OffsetDateTime.of(datetime, offset);
System.out.println("Date and Time with timezone offset in Java : " + date);
}
}
輸出:
Current date and time in a particular timezone : 2017-09-05T07:50:43.187+09:30[Australia/Darwin]
Date and Time with timezone offset in Java : 2017-09-05T07:50+05:30
OffSetDateTime
主要是給機(jī)器來理解的,如果是給人看的撼嗓,可以使用ZoneDateTime
類柬采。
固定日期
在前面我們用過一個MonthDay
的類,用來構(gòu)建特定的幾月幾日且警。這里用到了另一個YearMonth
粉捻,它表示某年某月,我們可以它來知道某年某月有多少天斑芜,使用YearMonth
的lengthOfMonth()
方法肩刃。
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前年月
YearMonth current = YearMonth.now();
System.out.println(current);
// 獲取這個月有多少天
System.out.println(current.lengthOfMonth());
// 構(gòu)建一個固定日期
YearMonth expire = YearMonth.of(2017, Month.NOVEMBER);
System.out.println(expire);
}
}
輸出:
2017-09
30
2017-11
格式化日期
public class Test {
public static void main(String[] args) {
String dayAfterTomorrow = "20170905";
LocalDate formatted = LocalDate.parse(dayAfterTomorrow,
DateTimeFormatter.BASIC_ISO_DATE);
System.out.printf("Date generated from String %s is %s %n", dayAfterTomorrow, formatted);
String goodFriday = "Sep 05 2017";
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy");
LocalDate holiday = LocalDate.parse(goodFriday, formatter);
System.out.printf("Successfully parsed String %s, date is %s%n", goodFriday, holiday);
} catch (DateTimeParseException ex) {
System.out.printf("%s is not parsable!%n", goodFriday);
ex.printStackTrace();
}
LocalDateTime arrivalDate = LocalDateTime.now();
try {
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm a");
String landing = arrivalDate.format(format);
System.out.printf("Arriving at : %s %n", landing);
} catch (DateTimeException ex) {
System.out.printf("%s can't be formatted!%n", arrivalDate);
ex.printStackTrace();
}
}
}
輸出:
Date generated from String 20170905 is 2017-09-05
Successfully parsed String Sep 05 2018, date is 2017-09-05
Arriving at : Sep 06 2017 07:47 AM
計算時間間隔
Java8
為我們提供了三個類來方便計算時間間隔,分別是
-
Duration
:計算秒杏头、納秒盈包。 -
ChronoUnit
:計算天、時醇王、分呢燥、秒。 -
Period
:計算年寓娩、月叛氨、日。
秒棘伴、納秒
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前時間戳
Instant i1 = Instant.now();
// 當(dāng)前時間推后10s
Instant i2 = i1.plusSeconds(10);
// 獲取時間差
Duration between = Duration.between(i1, i2);
// 獲取時間差的毫秒值
System.out.println(between.toMillis());
// 獲取時間差的納秒值
System.out.println(between.toNanos());
// 獲取時間差的天數(shù)值
System.out.println(between.toDays());
// 獲取時間差的小時值
System.out.println(between.toHours());
// 獲取時間差的分鐘值
System.out.println(between.toMinutes());
// 獲取時間差的秒數(shù)值
System.out.println(between.getSeconds());
}
}
輸出:
10000
10000000000
0
0
0
10
天寞埠、時、分焊夸、秒
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前時間
LocalDateTime d1 = LocalDateTime.now();
// 當(dāng)前時間延后90000s
LocalDateTime d2 = d1.plusSeconds(90000);
// 獲取兩個時間之間隔了幾個半天
long b1 = ChronoUnit.HALF_DAYS.between(d1, d2);
System.out.println(b1);
// 獲取兩個時間之間隔了幾個小時
long b2 = ChronoUnit.HOURS.between(d1, d2);
System.out.println(b2);
// 獲取兩個時間之間隔了多少毫秒
long b3 = ChronoUnit.MILLIS.between(d1, d2);
System.out.println(b3);
}
}
輸出:
2
25
90000000
年仁连、月、日
public class Test {
public static void main(String[] args) {
// 獲取當(dāng)前日期
LocalDate d1 = LocalDate.now();
// 構(gòu)建2018-10-01
LocalDate d2 = LocalDate.of(2018, 10, 1);
// 獲取兩個日期的差值
Period between = Period.between(d1, d2);
// 獲取兩個日期隔了多少年
System.out.println(between.getYears());
// 獲取兩個日期隔了多少月
System.out.println(between.getMonths());
// 獲取兩個日期隔了多少天
System.out.println(between.getDays());
}
}
輸出:
0
0
26
獲取某天的日期
比如有時候我們還會有這樣的需求淳地,獲取當(dāng)前日期所在的周一怖糊,那么這種又該怎么處理呢帅容;
public class Test {
public static void main(String[] args) {
LocalDate now = LocalDate.now();
LocalDate date = now.with(DayOfWeek.MONDAY);
System.out.println(date);
}
}
輸出:
2017-09-04
結(jié)語
好了颇象,常用的用法就講到這里,time
包下的類搭配起來可以有更多的用法并徘,需要同學(xué)們在使用過程中去摸索了遣钳,這里只是列舉了部分常見的用法,可能在日常開發(fā)中能夠用到麦乞,這也是我平時在開發(fā)中常用到的一些API
蕴茴。這里再提供兩個工具類供參考一些格式化時間的方式以及一些常用轉(zhuǎn)換處理劝评,戳這里查看詳情。