Java 8 系列文章 持續更新中
日期時間API 也是Java 8重要的更新之一,Java從一開始就缺少一致的日期和時間方法,Java 8 Date Time API是Java核心API的一個非常好的補充,
為什么需要新的日期時間API
Java中現有的與日期和時間相關的類存在一些問題:
- 日期時間類的定義不一致,在
java.util和java.sql包中都有Date類,同樣,格式化和決議類是在java.text包中定義的, java.util.Date同時包含日期和時間值,而java.sql.Date只包含日期值,把它放在java.sql包中是沒有意義的,而且這兩個類的名稱相同,這本身就是一個非常糟糕的設計,- 沒有為時間、時間戳、格式化和決議明確定義的類,我們有
java.text.DateFormat抽象類用于決議和格式化,通常使用SimpleDateFormat類決議和格式化, - 所有
Date類都是可變的,所以它們不是執行緒安全的,這也是JavaDate和Calendar類最大的問題之一, Date類不提供國際化,不支持時區,雖然引入了java.util.Calendar和java.util.TimeZone,但是它們也存在上面的問題,
在Date和Calendar類中定義的方法還有一些其他的問題,但是上面的問題清楚地表明,Java中需要一個健壯的日期時間API,這就是為什么 Joda Time 可以成為Java 日期時間高質量的替代品,
Java 8日期時間設計原則
Java 8日期時間API是基于 JSR-310 規范實作的,目的是為了解決遺留日期時間實作中的所有缺陷,新的日期時間API的一些設計原則如下:
-
不變性:新的日期時間API中的所有類都是不可變的,適用于多執行緒環境,
-
關注點分離:新的日期時間API明確區分了人類可讀的日期、時間和機器時間(Unix時間戳),它為
Date、Time、DateTime、Timestamp、Timezone等定義單獨的類, -
清晰性:所有的類中都清晰地定義了方法,并執行相同的操作,例如,要獲取當前時間實體可以用
now()方法,在所有這些類中都定義了format()和parse()方法,而不是為它們單獨定義一個類,所有類都使用工廠模式和策略模式來更好地操作,一旦您使用了其中一個類中的方法,使用其他類并不困難,
-
實用的操作:所有新的日期時間API類都常見的方法,比如加、減、格式化、決議、在日期/時間中獲取單獨的部分等等,
-
可擴展:新的日期時間API可以在
ISO-8601日歷系統上作業,但是我們也可以在其他非ISO日歷上使用它,
Java 8日期時間API的包
Java8日期時間API由以下包組成,
java.time:這是Java 8日期時間API的基本包,所有主要的基類都是這個包的一部分,例如LocalDate、LocalTime、LocalDateTime、Instant、Period、Duration等,所有這些類都是不可變的和執行緒安全的,大多數情況下,這些類足以處理常見的需求,java.time.chrono:這個包為非ISO日歷系統定義了通用API,我們可以擴展AbstractChronology類來創建我們自己的日歷系統,java.time.format:這個包包含用于格式化和決議日期時間物件的類,大多數時候我們不會直接使用它們,因為java.time包中的日期時間類已經提供了格式化和決議方法,java.time.temporal:這個包包含temporal物件,我們可以使用它來找出與日期/時間物件相關的特定日期或時間,例如,我們可以使用它們來查找一個月的第一天或最后一天,您可以很容易地識別這些方法,因為它們的格式總是withXXX,java.time.zone:這個包包含用于支持不同時區及其規則的類,
Java 8日期時間API類的示例
下面通過一些日期時間API類的示例,來更好的了解Java 8日期時間API
1.LocalDate
LocalDate是一個不可變的日期類,它以yyyy-MM-dd的默認格式表示日期,可以使用now()方法來獲取當前日期,還可以提供年、月和日期的輸入引數來創建LocalDate實體,
這個類為now()提供了一個多載方法,在這里可以傳遞ZoneId來獲取特定時區中的日期,這個類提供了與java.sql.Date相同的功能,
// 當前日期
LocalDate today = LocalDate.now();
System.out.println("當前日期=" + today);
// 通過提供年月日引數創建日期
LocalDate nowYear_1024 = LocalDate.of(2022, Month.OCTOBER, 24);
System.out.println("引數日期=" + nowYear_1024);
// 通過時區獲取當前日期
LocalDate todayShanghai = LocalDate.now(ZoneId.of("Asia/Shanghai"));
System.out.println("當前日期(CTT)=" + todayShanghai);
// 從紀元日(1970-01-01)開始的第多少天
LocalDate dateFromBase = LocalDate.ofEpochDay(365);
System.out.println("1970-01-01的第365天= " + dateFromBase);
// 某年的第多少天
LocalDate hundredDay2022 = LocalDate.ofYearDay(2022, 100);
System.out.println("2022年的第100天=" + hundredDay2022);
運行之后結果如下:
當前日期=2022-10-26
特殊日期=2022-10-24
當前日期(CTT)=2022-10-26
1970-01-01的第365天= 1971-01-01
2022年的第100天=2022-04-10
2.LocalTime
LocalTime是一個不可變的時間類,它以HH:mm:ss.SSS的默認格式表示時間,與LocalDate一樣,這個類提供了時區支持,并可以通過傳遞小時、分鐘和秒作為輸入引數來創建實體,
// 當前時間
LocalTime time = LocalTime.now();
System.out.println("當前時間=" + time);
// 通過提供時分秒引數創建日期
LocalTime specificTime = LocalTime.of(12, 20, 25, 40);
System.out.println("引數時間=" + specificTime);
// 通過時區獲取當前時間
LocalTime timeShanghai = LocalTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("當前時間(CTT)=" + timeShanghai);
// 從紀元日開始的第多少秒
LocalTime specificSecondTime = LocalTime.ofSecondOfDay(100);
System.out.println("從紀元日開始的第100秒=" + specificSecondTime);
運行之后結果如下:
當前時間=15:39:18.948
引數時間=12:20:25.000000040
當前時間(CTT)=15:39:18.949
從0開始的第100秒=00:01:40
3.LocalDateTime
LocalDateTime是一個不可變的日期時間類,它以yyyy-MM-ddTHH:mm:ss.SSS的默認格式表示時間日期,它提供了一個工廠方法,該方法使用LocalDate和LocalTime作為引數創建LocalDateTime實體,
// 當前日期時間
LocalDateTime now = LocalDateTime.now();
System.out.println("當前日期時間=" + now);
// 通過提供LocalDate和LocalTime引數創建日期時間
now = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("當前日期時間=" + now);
// 通過提供年月日時分秒引數創建日期時間
LocalDateTime specificTime = LocalDateTime.of(2022, Month.OCTOBER, 24, 10, 24, 24);
System.out.println("引數日期時間=" + specificTime);
// 通過時區獲取當前日期時間
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("當前日期時間(CTT)=" + todayKolkata);
// 從紀元日開始的第多少秒
LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(100, 0, ZoneOffset.UTC);
System.out.println("從紀元日開始的第100秒= " + dateFromBase);
運行之后結果如下:
當前日期時間=2022-10-26T15:51:59.070
當前日期時間=2022-10-26T15:51:59.071
引數日期時間=2022-10-24T10:24:24
當前日期時間(CTT)=2022-10-26T15:51:59.071
從紀元日開始的第100秒=1970-01-01T00:01:40
注意:以上例子通過輸入引數創建實體時,如果輸入了無效的引數name將會拋出java.time.DateTimeException
4.Instant
instant類用于處理機器可讀的時間格式,instant類將日期時間存盤在unix時間戳中,
// 當期時間戳
Instant timestamp = Instant.now();
System.out.println("當期時間戳= "+timestamp);
// 從紀元日開始的第多少毫秒
Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
System.out.println("從紀元日開始="+specificTime);
運行之后結果如下:
當期時間戳=2022-10-26T08:08:40.429Z
從紀元日開始=2022-10-26T08:08:40.429Z
Java8日期時間API類的實用方法
大多數日期時間類都會提供各種實用方法,例如加/減天數、周數、月數等,還有一些其他實用方法可以使用時間調整器TemporalAdjuster調整日期,并計算兩個日期之間的時間段,
LocalDate today = LocalDate.now();
//獲取年份,判斷年份是否是閏年
System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
//比較兩個時間
System.out.println("Today is before 01/01/2023? "+today.isBefore(LocalDate.of(2023,1,1)));
//通過LocalDate創建LocalDateTime
System.out.println("Current Time="+today.atTime(LocalTime.now()));
//加減操作
System.out.println("10 days after today will be "+today.plusDays(10));
System.out.println("3 weeks after today will be "+today.plusWeeks(3));
System.out.println("20 months after today will be "+today.plusMonths(20));
System.out.println("10 days before today will be "+today.minusDays(10));
System.out.println("3 weeks before today will be "+today.minusWeeks(3));
System.out.println("20 months before today will be "+today.minusMonths(20));
//時間調整器調整時間
System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("Last date of this year= "+lastDayOfYear);
Period period = today.until(lastDayOfYear);
System.out.println("Period Format= "+period);
System.out.println("Months remaining in the year= "+period.getMonths());
運行之后結果如下:
Year 2022 is Leap Year? false
Today is before 01/01/2023? true
Current Time=2022-10-26T16:25:04.740
10 days after today will be 2022-11-05
3 weeks after today will be 2022-11-16
20 months after today will be 2024-06-26
10 days before today will be 2022-10-16
3 weeks before today will be 2022-10-05
20 months before today will be 2021-02-26
First date of this month= 2022-10-01
Last date of this year= 2022-12-31
Period Format= P2M5D
Months remaining in the year= 2
Java8日期時間的決議和格式化
經常用到的操作有:將日期時間格式化為不同格式String,決議String以獲得日期時間物件,
// 格式化
LocalDate date = LocalDate.now();
// 默認格式
System.out.println("Default format of LocalDate=" + date);
// 自定義格式
System.out.println(date.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
LocalDateTime dateTime = LocalDateTime.now();
// 默認格式
System.out.println("Default format of LocalDateTime=" + dateTime);
// 自定義格式
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH時mm分ss秒")));
System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
Instant timestamp = Instant.now();
// 默認格式
System.out.println("Default format of Instant=" + timestamp);
// 決議
LocalDateTime dt = LocalDateTime.parse("2022年10月24日10時24分24秒",
DateTimeFormatter.ofPattern("yyyy年MM月dd日HH時mm分ss秒"));
System.out.println("Default format after parsing = " + dt);
運行之后結果如下:
Default format of LocalDate=2022-10-26
2022年10月26日
20221026
Default format of LocalDateTime=2022-10-26T16:37:51.300
2022年10月26日16時37分51秒
20221026
Default format of Instant=2022-10-26T08:37:51.301Z
Default format after parsing = 2022-10-24T10:24:24
對遺留日期時間的支持
遺留日期/時間類幾乎在所有應用程式中都使用,因此必須有向下兼容,這就是為什么我們可以通過一些實用方法將遺留類轉換為新類,反之亦然,
//Date轉Instant
Instant timestamp = new Date().toInstant();
//Instant轉LocalDateTime
LocalDateTime date = LocalDateTime.ofInstant(timestamp,
ZoneId.of(ZoneId.SHORT_IDS.get("CTT")));
System.out.println("Date = " + date);
//Calendar轉Instant
Instant time = Calendar.getInstance().toInstant();
System.out.println(time);
//TimeZone轉ZoneId
ZoneId defaultZone = TimeZone.getDefault().toZoneId();
System.out.println(defaultZone);
//ZonedDateTime from specific Calendar
ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
System.out.println(gregorianCalendarDateTime);
//Date API to Legacy classes
Date dt = Date.from(Instant.now());
System.out.println(dt);
TimeZone tz = TimeZone.getTimeZone(defaultZone);
System.out.println(tz);
GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
System.out.println(gc);
運行之后結果如下:
Date = 2022-10-26T16:47:38.329
2022-10-26T08:47:38.429Z
Asia/Shanghai
2022-10-26T16:47:38.455+08:00[Asia/Shanghai]
Wed Oct 26 16:47:38 CST 2022
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null]
java.util.GregorianCalendar[time=1666774058455,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2022,MONTH=9,WEEK_OF_YEAR=43,WEEK_OF_MONTH=4,DAY_OF_MONTH=26,DAY_OF_YEAR=299,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=47,SECOND=38,MILLISECOND=455,ZONE_OFFSET=28800000,DST_OFFSET=0]
可以看到,遺留的TimeZone和GregorianCalendar類toString()方法過于冗長,對用戶不友好,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/520582.html
標籤:Java
上一篇:Java實作郵件發送
