從Java版本1.0開始就支持日期和時間,主要通過java.util.Date
類。 但是,Date
類設計不佳。 例如,Date
中的月份從1開始,但從日期卻從0開始。在JDK 1.1中使用它的許多方法已經廢棄,同時java.util.Calendar
被引入來接管Date
中的一些功能。 這兩個是處理日期和時間的主要類,直到JDK 1.7,盡管他們被認為是不足夠並且不容易處理,導致許多人訴諸第三方替代品,例如Joda Time(http://joda.org)。 JDK 1.8中的新日期和時間API解決了舊API中的許多問題,並且與Joda Time API類似。
這里介紹JDK 1.8中的日期 - 時間的API。
Overview
新的日期和時間API使得使用日期和時間非常容易。java.time
包中包含API中的核心類。 另外,還有其他四個包,其成員使用較少:java.time.chrono
,java.time.format
,java.time.temporal
和java.time.zone
。
在java.time
包中,Instant
類表示時間線上的一個點,通常用於對時間進行操作。 LocalDate
類為沒有時間和時區部分的日期建模,例如,用於表示生日。
如果你需要日期和時間,那么LocalDateTime
就是為你准備的。 例如,訂單發貨日期可能需要一個日期以外的時間來使訂單更容易跟蹤。 如果你需要一段時間但不關心日期,那么可以使用LocalTime
。
如果時區很重要,日期和時間API提供ZonedDateTime
類。 顧名思義,這個類表示帶有時區日期時間。 例如,你可以使用此類來計算位於不同時區的兩個機場之間的飛行時間。
然后有兩個類來測量時間總計,即Duration
類和Period
類。 這兩個類是相似的,除了Duration
是基於時間,但而Period
是基於日期的。 Duration
提供了納秒精度的時間量。 例如,可以模擬飛行時間,因為它通常以小時數和分鍾數表示。 另一方面,如果只關心天數,月數或年數,例如計算一個人的年齡,則Period
更為適用。
java.time
包也帶有兩個枚舉DayOfWeek
和Month
。 DayOfWeek
表示從一周的一天,從周一開始到周日。 Month
枚舉代表這一年的十二個月,從1月到12月。
處理日期和時間通常涉及解析和格式。 日期和時間API通過在所有主要類中提供parse
和format
方法來解決這兩個問題。 另外,java.time.format
包含一個用於格式化日期和時間的DateTimeFormatter
類。
Instant類
Instant
實例表示時間線上的一個點。 參考點是標准的Java紀元(epoch),即1970-01-01T00:00:00Z(1970年1月1日00:00 GMT)。 Instant類的
EPOCH屬性返回表示Java紀元的
Instant`實例。 在紀元之后的時間是正值,而在此之前的時間即是負值。
Instant
的靜態now
方法返回一個表示當前時間的Instant
對象:
Instant now = Instant.now();
getEpochSecond
方法返回自紀元以來經過的秒數。 getNano
方法返回自上一秒開始以來的納秒數。
Instant
類的一個常用用途是用來操作時間,如以下代碼所示。
import java.time.Duration;
import java.time.Instant;
public class InstantDemo1 {
public static void main(String[] args) {
Instant start = Instant.now();
// do something here
Instant end = Instant.now();
System.out.println(Duration.between(start, end).toMillis());
}
}
如上面代碼所示,Duration
類用於返回兩個Instant
之間時間數量的差異。
LocalDate類
LocalDate
類只包括日期沒有時間的部分。 它也沒有時區。 下表顯示了LocalDate
中一些重要的方法。
方法 | 描述 |
---|---|
now | 靜態方法,返回今天的日期 |
of | 從指定年份,月份和日期創建LocalDate的靜態方法 |
getDayOfMonth, getMonthValue, getYear | 以int形式返回此LocalDate的日,月或年 |
getMonth | 以Month枚舉常量返回此LocalDate的月份 |
plusDays, minusDays | 給LocalDate添加或減去指定的天數 |
plusWeeks, minusWeeks | 給LocalDate添加或減去指定的星期數 |
plusMonths, minusMonths | 給LocalDate添加或減去指定的月份數 |
plusYears, minusYears | 給LocalDate添加或減去指定的年數 |
isLeapYear | 檢查LocalDate指定的年份是否為閏年 |
isAfter, isBefore | 檢查此LocalDate是在給定日期之后還是之前 |
lengthOfMonth | 返回此LocalDate中月份的天數 |
withDayOfMonth | 返回此LocalDate的拷貝,將月份中的某天設置為給定值 |
withMonth | 返回此LocalDate的拷貝,其月份設置為給定值 |
withYear | 返回此LocalDate的拷貝,並將年份設置為給定值 |
LocalDate
提供了各種創建日期的方法。 例如,要創建代表今天日期的LocalDate
,使用靜態now
方法。
LocalDate today = LocalDate.now();
要創建代表特定年,月和日的LocalDate
,使用of
方法,該方法也是靜態的。 例如,以下代碼創建了一個代表2018年3月7日的LocalDate
實例。
LocalDate date = LocalDate.of(2018, 3, 7);
還有一個接受java.time.Month
枚舉的常量作為第二個參數的of
方法。 例如,下面是使用第二種方法重載構造相同日期的代碼。
LocalDate date = LocalDate.of(2018, Month.MARCH, 7);
還有獲取LocalDate
的日,月或年的方法,例如getDayOfMonth
,getMonth
,getMonthValue
和getYear
。 他們都沒有任何參數,並返回一個int或Month
的枚舉常量。 另外,還有一個get
方法,它接受一個TemporalField
並返回這個LocalDate
的一部分。 例如,傳遞ChronoField.YEAR以
獲取LocalDate
的年份部分。
int year = localDate.get(ChronoField.YEAR);
ChronoField
是一個實現TemporalField
接口的枚舉,因此可以傳遞一個ChronoField
常量來獲取。 TemporalField
和ChronoField
都是java.time.temporal
包的一部分。 但是,並非ChronoField
中的所有常量都可以get
獲取,因為並非所有常量都受支持。 例如,傳遞ChronoField.SECOND_OF_DAY
以引發異常。 因此,取而代之,最好使用getMonth
,getYear
或類似方法來獲取LocalDate
的組件。
此外,還有拷貝LocalDate
的方法,例如plusDays
,plusYears
,minusMonths
等等。 例如,要獲取表示明天的LocalDate
,可以創建一個代表今天的LocalDat
e,然后調用其plusDays
方法。
LocalDate tomorrow = LocalDate.now().plusDays(1);
要獲取昨天表示的LocalDate
,可以使用minusDays
方法。
LocalDate yesterday = LocalDate.now().minusDays(1);
另外,還有plus
和minus
方法以更通用的方式獲得LocalDate
的拷貝。 兩者都接受一個int參數和一個TemporalUnit
參數。 這些方法的簽名如下。
public LocalDate plus(long amountToAdd,
java.time.temporal.TemporalUnit unit)
public LocalDate minus(long amountToSubtract,
java.time.temporal.TemporalUnit unit)
例如,獲得一個從今天開始前20年的LocalDate
,可以使用這段代碼。
LocalDate pastDate = LocalDate.now().minus(2, ChronoUnit.DECADES);
ChronoUnit
是一個實現TemporalUnit
的枚舉,因此可以將ChronoUnit
常量傳遞給plus
和minus
方法。
LocalDate
是不可變的,因此無法更改。 任何返回LocalDate
的方法都返回LocalDate
的新實例。
以下是使用LocalDate
的例子。
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
public class LocalDateDemo1 {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDate oneDecadeAgo = today.minus(1, ChronoUnit.DECADES);
System.out.println("Day of month: " + today.getDayOfMonth());
System.out.println("Today is " + today);
System.out.println("Tomorrow is " + tomorrow);
System.out.println("A decade ago was " + oneDecadeAgo);
System.out.println("Year : " + today.get(ChronoField.YEAR));
System.out.println("Day of year:" + today.getDayOfYear());
}
}
Period類
Period
類基於日期的時間數量構建,例如五天,一周或三年。 下面列出了一些重要的方法。
方法 | 描述 |
---|---|
between | 在兩個LocalDates之間創建一個Period示例 |
ofDays, ofWeeks, ofMonths, ofYears | 創建代表給定天數/周/月/年的Period實例 |
of | 根據給定的年數,月數和天數創建一個Period實例 |
getDays, getMonths, getYears | 以int形式返回此Period的天數/月/年 |
isNegative | 如果此Period的三個部分中的任何一個為負數,則返回true。 否則返回false |
isZero | 如果此Period的所有三個部分均為零,則返回true。 否則,返回false |
plusDays, minusDays | 在此Period上添加或減去給定的天數 |
plusMonths, minusMonths | 在此Period上增加或減去給定的月數 |
plusYears, minusYears | 在此Period增加或減去給定的年數 |
withDays | 以指定的天數返回此Period的拷貝 |
withMonths | 以指定的月數返回此Period的拷貝 |
withYears | 以指定的年數返回此Period的拷貝 |
創建一個Period
很簡單,這要感謝between
, of
,ofDays
/ ofWeeks
/ ofMonths
/ ofYears
等靜態工廠方法。 例如,以下是如何創建代表兩周的Period
實例。
Period twoWeeks = Period.ofWeeks(2);
要創建代表一年兩個月三天的Period
實例,請使用of
方法。
Period p = Period.of(1, 2, 3);
要獲取某個期間的年/月/日組件,調用其getYears
/ getMonths
/ getDays
方法。 例如,以下代碼中的howManyDays
變量的值是14。
Period twoWeeks = Period.ofWeeks(2);
int howManyDays = twoWeeks.getDays();
最后,可以使用plusXXX
或minusXXX
方法以及withXXX
方法來創建Period
的拷貝。 Period
是不可變的,所以這些方法返回新的Period
實例。
例如,下面的代碼顯示了一個計算個人年齡的年齡計算器。 它從兩個LocalDate
創建一個Period
並調用它的getDays
,getMonths
和getYears
方法。
import java.time.LocalDate;
import java.time.Period;
public class PeriodDemo1 {
public static void main(String[] args) {
LocalDate dateA = LocalDate.of(1978, 8, 26);
LocalDate dateB = LocalDate.of(1988, 9, 28);
Period period = Period.between(dateA, dateB);
System.out.printf("Between %s and %s"
+ " there are %d years, %d months"
+ " and %d days%n", dateA, dateB,
period.getYears(),
period.getMonths(),
period.getDays());
}
}
運行PeriodDemo1
類打印下面字符串。
Between 1978-08-26 and 1988-09-28 there are 10 years, 1 months and 2 days
LocalDateTime類
LocalDateTime
類是一個沒有時區的日期時間的構建。 下表顯示了LocalDateTime
中一些重要的方法。 這些方法類似於LocalDate
的方法,以及用於修改時間部分的一些其他方法,例如在LocalDate
中不可用的plusHours
,plusMinutes
和plusSeconds
。
方法 | 描述 |
---|---|
now | 返回當前日期和時間的靜態方法。 |
of | 從指定年份,月份,日期,小時,分鍾,秒和毫秒創建LocalDateTime的靜態方法。 |
getYear, getMonthValue, getDayOfMonth, getHour, getMinute, getSecond | 以int形式返回此LocalDateTime的年,月,日,小時,分鍾或秒部分。 |
plusDays, minusDays | 給當前LocalDateTime添加或減去指定的天數。 |
plusWeeks, minusWeeks | 給當前LocalDateTime添加或減去指定的周數。 |
plusMonths, minusMonths | 給當前LocalDateTime添加或減去指定的月數。 |
plusYears, minusYears | 給當前LocalDateTime添加或減去指定的年數。 |
plusHours, minusHours | 給當前LocalDateTime添加或減去指定的小時數 |
plusMinutes, minusMinutes | 給當前LocalDateTime添加或減去指定的分鍾數 |
plusSeconds, minusSeconds | 給當前LocalDateTime添加或減去指定的秒數 |
IsAfter, isBefore | 檢查此LocalDateTime是否在指定的日期時間之后或之前 |
withDayOfMonth | 返回此LocalDateTime的拷貝,並將月份中的某天設置為指定值 |
withMonth, withYear | 返回此LocalDateTime的拷貝,其月或年設置為指定值 |
withHour, withMinute, withSecond | 返回此LocalDateTime的拷貝,其小時/分鍾/秒設置為指定值 |
LocalDateTime
提供了各種靜態方法來創建日期時間。 該方法現在帶有三個重載方法返回當前的日期時間。 無參的方法是最容易使用的:
LocalDateTime now = LocalDateTime.now();
要創建具有特定日期和時間的LocalDateTime
,請使用of
方法。 此方法有多個重載,並允許傳遞日期時間或LocalDate
和LocalTime
的單個部分。 以下是一些方法的簽名。
public static LocalDateTime of(int year, int month, int dayOfMonth,
int hour, int minute)
public static LocalDateTime of(int year, int month, int dayOfMonth,
int hour, int minute)
public static LocalDateTime of(int year, Month month,
int dayOfMonth, int hour, int minute)
public static LocalDateTime of(int year, Month month,
int dayOfMonth, int hour, int minute)
public static LocalDateTime of(LocalDate date, LocalTime time)
例如,下面的代碼段創建一個LocalDateTime
,代表2015年12月31日早上八點。
LocalDateTime endOfYear = LocalDateTime.of(2015, 12, 31, 8, 0);
可以使用plusXXX
或minusXXX
方法創建LocalDateTime
的拷貝。 例如,此代碼創建一個LocalDateTime
,它表示明天的同一時間。
LocalDateTime now = LocalDateTime.now();
LocalDateTime sameTimeTomorrow = now.plusHours(24);
Time Zones
互聯網數字分配機構(IANA)維護一個可從此網頁下載的時區數據庫:
[http://www.iana.org/time-zones](http://www.iana.org/time-zones)
但為了便於查看,可以訪問此Wikipedia頁面:
http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
Java日期和時間API也適用於時區。 抽象類ZoneId
(在java.time
包中)表示一個區域標識符。 它有一個名為getAvailableZoneIds
的靜態方法,它返回所有區域標識符。 下面展示了如何使用這種方法打印所有時區的排序列表。
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class TimeZoneDemo1 {
public static void main(String[] args) {
Set<String> allZoneIds = ZoneId.getAvailableZoneIds();
List<String> zoneList = new ArrayList<>(allZoneIds);
Collections.sort(zoneList);
for (String zoneId : zoneList) {
System.out.println(zoneId);
}
// alternatively, you can use this line of code to
// print a sorted list of zone ids
// ZoneId.getAvailableZoneIds().stream().sorted().
// forEach(System.out::println);
}
}
getAvailableZoneIds
返回字符串的Set
集合。 可以使用Collections.sort()
或更優雅地通過調用它的stream
方法對Set
進行排序。 可以編寫此代碼對區域標識符進行排序。
ZoneId.getAvailableZoneIds().stream().sorted()
.forEach(System.out::println);
getAvailableZoneIds
返回586個區域標識符的Set
集合。 以下是上述代碼中的一部分區域標識符。
Africa/Cairo
Africa/Johannesburg
America/Chicago
America/Los_Angeles
America/Mexico_City
America/New_York
America/Toronto
Antarctica/South_Pole
Asia/Hong_Kong
Asia/Shanghai
Asia/Tokyo
Australia/Melbourne
Australia/Sydney
Canada/Atlantic
Europe/Amsterdam
Europe/London
Europe/Paris
US/Central
US/Eastern
US/Pacific
ZonedDateTime
ZonedDateTime
類以一個時區為日期時間的構建。例如,以下是一個時區的日期時間:
2015-12-31T10:59:59+01:00 Europe/Paris
ZonedDateTime
始終是不可變的,時間分量的存儲精度為納秒。
ZonedDateTIme
中一些重要方法的使用與LocalDateTime
類似,只是多了一個時區的概念。可自行查閱API。
像LocalDateTime
一樣,ZonedDateTime
類現在提供靜態now
和of
方法,並構造一個ZonedDateTime
實例。 now
方法創建一個ZonedDateTime
代表執行的日期和時間。 無參now
方法會使用計算機的默認時區創建ZonedDateTime
。
ZonedDateTime now = ZonedDateTime.now();
now
的另一個重載方法允許傳遞區域標識符:
ZonedDateTime parisTime =
ZonedDateTime.now(ZoneId.of("Europe/Paris"));
of
方法也有好幾個重載的方法。在所有情況下,都需要傳遞區域標識符。 第一個重載方法允許傳遞時區日期時間的每個部分,從年份到納秒。
public static ZonedDateTime of(int year, int month, int dayOfMonth,
int hour, int minute, int second, int nanosecond,
ZoneId zone)
of
方法的第二個重載方法需要LocalDate
,LocalTime
和ZoneId
參數:
public static ZonedDateTime of(LocalDate date, LocalTime time,
ZoneId zone)
of
方法的最后一個重載方法需要LocalDateTime
和ZoneId
參數。
public static ZonedDateTime of(LocalDateTime datetime, ZoneId zone)
像LocalDate
和LocalDateTime
一樣,ZonedDateTime
提供了使用plusXXX
,minusXXX
和withXXX
方法創建實例拷貝的方法。
例如,下面代碼行創建一個帶默認時區的ZonedDateTime
,並調用它的minusDays
方法以在三天前創建相同的ZonedDateTime
。
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime threeDaysEarlier = now.minusDays(3);
Duration
Duration
類是基於時間的持續時間的構建。 它與Period
類似,不同之處在於Duration
的時間分量為納秒精度,並考慮了ZonedDateTime
實例之間的時區。 下表顯示了Duration
中重要的方法。
方法 | 描述 |
---|---|
between | 在兩個時差的對象之間創建一個Duration實例,例如在兩個LocalDateTime或兩個ZonedDateTime之間。 |
ofYears, ofMonths, ofWeeks, ofDays, ofHours, ofMinutes, ofSeconds, ofNano | 創建給定年數/月/周/天/小時/分鍾/秒/納秒的Duration實例 |
of | 根據指定數量的時間單位創建Duration實例 |
toDays, toHours, toMinutes | 以int形式返回此Duration的天數/小時/分鍾數 |
isNegative | 如果此Duration為負,則返回true。 否則返回false。 |
isZero | 如果此Duration長度為零,則返回true。 否則,返回false |
plusDays, minusDays | 在此Duration內添加或減去指定的天數。 |
plusMonths, minusMonths | 在此Duration內添加或減去指定的月數。 |
plusYears, minusYears | 在Duration內添加或減去指定的年數 |
withSeconds | 以指定的秒數返回此Duration的拷貝。 |
可以通過調用靜態方法between
或of
來創建Duration
。 下面的代碼會在2015年1月26日11:10至2015年1月26日12:40之間創建兩個LocalDateTime
的Duration
。
import java.time.Duration;
import java.time.LocalDateTime;
public class DurationDemo1 {
public static void main(String[] args) {
LocalDateTime dateTimeA = LocalDateTime
.of(2015, 1, 26, 8, 10, 0, 0);
LocalDateTime dateTimeB = LocalDateTime
.of(2015, 1, 26, 11, 40, 0, 0);
Duration duration = Duration.between(
dateTimeA, dateTimeB);
System.out.printf("There are %d hours and %d minutes.%n",
duration.toHours(),
duration.toMinutes() % 60);
}
}
運行DurationDemo1
類的結果是這樣的。
There are 3 hours and 30 minutes.
下面的代碼在兩個ZoneDateTime
之間創建一個Duration
,具有相同的日期和時間,但時區不同。
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class DurationDemo2 {
public static void main(String[] args) {
ZonedDateTime zdt1 = ZonedDateTime.of(
LocalDateTime.of(2015, Month.JANUARY, 1,
8, 0),
ZoneId.of("America/Denver"));
ZonedDateTime zdt2 = ZonedDateTime.of(
LocalDateTime.of(2015, Month.JANUARY, 1,
8, 0),
ZoneId.of("America/Toronto"));
Duration duration = Duration.between(zdt1, zdt2);
System.out.printf("There are %d hours and %d minutes.%n",
duration.toHours(),
duration.toMinutes() % 60);
}
}
運行DurationDemo2
類在控制台上打印如下結果。
There are -2 hours and 0 minutes.
這是預料之中的,因為時區America/Denver
和America/Toronto
之間有兩個小時的差異。
作為一個更復雜的例子,下面的代碼顯示了一個公交車旅行時間計算器。 它有一個方法calculateTravelTime
,它需要一個離開的ZonedDateTime
實例和一個到達的ZonedDateTime
實例。 該代碼調用calculateTravelTime
方法兩次。 這兩次公交車都在丹佛早上8點從科羅拉多州丹佛出發,並於多倫多時間第二天早上8點抵達多倫多。 公交車首次於2014年3月8日啟程,第二次於2014年3月18日啟程。
兩種情況下的旅行時間是多少?
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class TravelTimeCalculator {
public Duration calculateTravelTime(
ZonedDateTime departure, ZonedDateTime arrival) {
return Duration.between(departure, arrival);
}
public static void main(String[] args) {
TravelTimeCalculator calculator =
new TravelTimeCalculator();
ZonedDateTime departure1 = ZonedDateTime.of(
LocalDateTime.of(2014, Month.MARCH, 8,
8, 0),
ZoneId.of("America/Denver"));
ZonedDateTime arrival1 = ZonedDateTime.of(
LocalDateTime.of(2014, Month.MARCH, 9,
8, 0),
ZoneId.of("America/Toronto"));
Duration travelTime1 = calculator
.calculateTravelTime(departure1, arrival1);
System.out.println("Travel time 1: "
+ travelTime1.toHours() + " hours");
ZonedDateTime departure2 = ZonedDateTime.of(
LocalDateTime.of(2014, Month.MARCH, 18,
8, 0),
ZoneId.of("America/Denver"));
ZonedDateTime arrival2 = ZonedDateTime.of(
LocalDateTime.of(2014, Month.MARCH, 19,
8, 0),
ZoneId.of("America/Toronto"));
Duration travelTime2 = calculator
.calculateTravelTime(departure2, arrival2);
System.out.println("Travel time 2: "
+ travelTime2.toHours() + " hours");
}
}
運行結果為:
Travel time 1: 21 hours
Travel time 2: 22 hours
為什么有這個區別? 因為2014年的夏令時從3月9日星期日凌晨2點開始。 因此,在2014年3月8日至2014年3月9日之間“失去”了一小時。
Formatting A Date-Time
可以使用java.time.format.DateTimeFormatter
格式化本地或時區日期時間。LocalDate
,LocalDateTime
,LocalTime
和ZoneDateTime
類提供具有以下簽名的格式方法。
public java.lang.String format(java.time.format.DateTimeFormatter
formatter)
很明顯,要格式化日期或時間,必須首先創建DateTimeFormatter
實例。
下面的代碼使用兩個格式化實例格式化當前日期。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class DateTimeFormatterDemo1 {
public static void main(String[] args) {
DateTimeFormatter formatter1 = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.MEDIUM);
LocalDateTime example = LocalDateTime.of(
2000, 3, 19, 10, 56, 59);
System.out.println("Format 1: " + example
.format(formatter1));
DateTimeFormatter formatter2 = DateTimeFormatter
.ofPattern("MMMM dd, yyyy HH:mm:ss");
System.out.println("Format 2: " +
example.format(formatter2));
}
}
運行結果如下:(第一個結果取決於你的區域設置)。
Format 1: 19-Mar-2000 10:56:59 AM
Format 2: March 19, 2000 10:56:59
Parsing A Date-Time
在Java Date和Time API的許多類中有兩種parse
方法。第一個需要格式化實例,第二個則不需要。后一個方法會根據默認模式解析日期時間。要使用自己的格式化模式,請使用DateTimeFormatter
。如果傳遞的字符串不能被解析,那么解析方法將拋出一個DateTimeParseException
。
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Scanner;
public class AgeCalculator {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-d");
public Period calculateAge(LocalDate birthday) {
LocalDate today = LocalDate.now();
return Period.between(birthday, today);
}
public LocalDate getBirthday() {
Scanner scanner = new Scanner(System.in);
LocalDate birthday;
while (true) {
System.out.println("Please enter your birthday "
+ "in yyyy-MM-dd format (e.g. 1980-9-28): ");
String input = scanner.nextLine();
try {
birthday = LocalDate.parse(input, formatter);
return birthday;
} catch(DateTimeParseException e) {
System.out.println("Error! Please try again");
}
}
}
public static void main(String[] args) {
AgeCalculator ageCalculator = new AgeCalculator();
LocalDate birthday = ageCalculator.getBirthday();
Period age = ageCalculator.calculateAge(birthday);
System.out.printf("Today you are %d years, %d months"
+ " and %d days old%n",
age.getYears(), age.getMonths(), age.getDays());
}
}
AgeCalculator
類有兩個方法,getBirthday
和calculateAge
。 getBirthday
方法使用Scanner
類來讀取用戶輸入,並使用DateTimeFormatter
類將輸入解析到LocalDate
中。 getBirthday
方法一直請求一個日期,直到用戶輸入正確格式的日期,在這種情況下,方法返回。 calculateAge
方法需要一個生日,並在生日和今天的日期之間創建一個Period
實例。
如果運行這個例子,會在控制台上看到這個。
Please enter your birthday in yyyy-MM-dd format (e.g. 1980-9-28):
如果以正確的格式輸入日期,則程序將打印計算的年齡,如下所示。
Today you are 79 years, 0 months and 15 days old
Summary
Java 8帶來了全新的Date-Time API來替代以java.util.Date
類為中心的舊的API。 通過本篇文章,學習如何使用新API中的核心類,如Instant
,LocalDate
,LocalDateTime
,ZonedDateTime
,Period
和Duration
,以及學習如何格式化和解析日期時間。