一、背景
jdk 1.8
之前, Java
時間使用java.util.Date
和 java.util.Calendar
類。
Date today = new Date();
System.out.println(today);
// 轉為字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String todayStr = sdf.format(today);
System.out.println(todayStr);
Date
的幾個問題:
- 如果不格式化,
Date
打印出的日期可讀性差; - 可以使用
SimpleDateFormat
對時間進行格式化,但SimpleDateFormat
是線程不安全的(阿里巴巴開發手冊中禁用static
修飾SimpleDateFormat
); Date
對時間處理比較麻煩,比如想獲取某年、某月、某星期,以及n
天以后的時間,如果用Date
來處理的話真是太難了,並且Date
類的getYear()
、getMonth()
這些方法都被棄用了;
二、JDK 1.8
新的日期時間類型
Java8
引入的新的一系列API
,對時間日期的處理提供了更好的支持,清楚的定義了時間日期的一些概念,比如說,瞬時時間(Instant
),持續時間(duration
),日期(date
),時間(time
),時區(time-zone
)以及時間段(Period
)。
LocalDate
:不包含時間的日期,比如2019-10-14
。可以用來存儲生日,周年紀念日,入職日期等。LocalTime
:與LocalDate
想對照,它是不包含日期的時間。LocalDateTime
:包含了日期及時間,沒有偏移信息(時區)。ZonedDateTime
:包含時區的完整的日期時間,偏移量是以UTC
/格林威治時間為基准的。Instant
:時間戳,與System.currentTimeMillis()
類似。Duration
:表示一個時間段。Period
:用來表示以年月日來衡量一個時間段。DateTimeFormatter
:新的日期解析格式化類。
2.1 LocalDate
LocalDate
類內只包含日期,不包含具體時間。只需要表示日期而不包含時間,就可以使用它。
public static void localDate() {
//獲取當前年月日
LocalDate today = LocalDate.now();
System.out.println("當前年月日:" + today);
// 獲取年的兩種方式
int thisYear = today.getYear();
int thisYearAnother = today.get(ChronoField.YEAR);
System.out.println("今年是" + thisYear + "年");
System.out.println("今年是" + thisYearAnother + "年");
// 獲取月
Month thisMonth = today.getMonth();
System.out.println(thisMonth.toString());
// 這是今年的第幾個月(兩種寫法)
int monthOfYear = today.getMonthValue();
// int monthOfYear = today.get(ChronoField.MONTH_OF_YEAR);
System.out.println("這個月是今年的第" + monthOfYear + "個月");
// 月份的天數
int length = today.lengthOfMonth();
System.out.println("這個月有" + length + "天");
// 獲取日的兩種方式
int thisDay = today.getDayOfMonth();
int thisDayAnother = today.get(ChronoField.DAY_OF_MONTH);
System.out.println("今天是這個月的第" + thisDay + "天");
System.out.println("今天是這個月的第" + thisDayAnother + "天");
// 獲取星期
DayOfWeek thisDayOfWeek = today.getDayOfWeek();
System.out.println(thisDayOfWeek.toString());
// 今天是這周的第幾天
int dayOfWeek = today.get(ChronoField.DAY_OF_WEEK);
System.out.println("今天是這周的第" + dayOfWeek + "天");
// 是否為閏年
boolean leapYear = today.isLeapYear();
System.out.println("今年是閏年:" + leapYear);
//構造指定的年月日
LocalDate anotherDay = LocalDate.of(2008, 8, 8);
System.out.println("指定年月日:" + anotherDay);
}
2.2 LocalTime
LocalTime
只會獲取時間,不獲取日期。LocalTime
和LocalDate
類似,區別在於LocalDate不包含具體時間,而LocalTime
不包含具體日期。
public static void localTime() {
// 獲取當前時間
LocalTime nowTime = LocalTime.now();
System.out.println("當前時間:" + nowTime);
//獲取小時的兩種方式
int hour = nowTime.getHour();
int thisHour = nowTime.get(ChronoField.HOUR_OF_DAY);
System.out.println("當前時:" + hour);
System.out.println("當前時:" + thisHour);
//獲取分的兩種方式
int minute = nowTime.getMinute();
int thisMinute = nowTime.get(ChronoField.MINUTE_OF_HOUR);
System.out.println("當前分:" + minute);
System.out.println("當前分:" + thisMinute);
//獲取秒的兩種方式
int second = nowTime.getSecond();
int thisSecond = nowTime.get(ChronoField.SECOND_OF_MINUTE);
System.out.println("當前秒:" + second);
System.out.println("當前秒:" + thisSecond);
// 構造指定時間(最多可到納秒)
LocalTime anotherTime = LocalTime.of(20, 8, 8);
System.out.println("構造指定時間:" + anotherTime);
}
2.3 LocalDateTime
LocalDateTime
表示日期和時間組合。可以通過of()方法直接創建,也可以調用LocalDate
的atTime()
方法或LocalTime
的atDate()
方法將LocalDate
或LocalTime
合並成一個LocalDateTime
。
public static void localDateTime() {
// 當前日期和時間
LocalDateTime today = LocalDateTime.now();
System.out.println("現在是:" + today);
// 創建指定日期和時間
LocalDateTime anotherDay = LocalDateTime.of(2008, Month.AUGUST, 8, 8, 8, 8);
System.out.println("創建的指定時間是:" + anotherDay);
// 拼接日期和時間
// 使用當前日期,指定時間生成的 LocalDateTime
LocalDateTime thisTime = LocalTime.now().atDate(LocalDate.of(2008, 8, 8));
System.out.println("拼接的日期是:" + thisTime);
// 使用當前日期,指定時間生成的 LocalDateTime
LocalDateTime thisDay = LocalDate.now().atTime(LocalTime.of(12, 24, 12));
System.out.println("拼接的日期是:" + thisDay);
// 指定日期和時間生成 LocalDateTime
LocalDateTime thisDayAndTime = LocalDateTime.of(LocalDate.of(2008, 8, 8), LocalTime.of(12, 24, 12));
System.out.println("拼接的日期是:" + thisDayAndTime);
// 獲取LocalDate
LocalDate todayDate = today.toLocalDate();
System.out.println("今天日期是:" + todayDate);
// 獲取LocalTime
LocalTime todayTime = today.toLocalTime();
System.out.println("現在時間是:" + todayTime);
}
2.4 Instant
Instant
用於一個獲取時間戳,與System.currentTimeMillis()
類似,但Instant
可以精確到納秒。
public class InstantDemo {
public static void main(String[] args) {
// 創建Instant對象
Instant instant = Instant.now();
// 通過ofEpochSecond方法創建(第一個參數表示秒,第二個參數表示納秒)
Instant another = Instant.ofEpochSecond(365 * 24 * 60, 100);
// 獲取到秒數
long currentSecond = instant.getEpochSecond();
System.out.println("獲取到秒數:" + currentSecond);
// 獲取到毫秒數
long currentMilli = instant.toEpochMilli();
System.out.println("獲取到毫秒數:" + currentMilli);
}
}
2.5 Duration
Duration
的內部實現與Instant
類似,但Duration
表示時間段,通過between
方法創建,還可以通過of()
方法創建。
public static void duration() {
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
// 通過between()方法創建
Duration duration = Duration.between(from, to);
// 通過of()方法創建,該方法參數為時間段長度和時間單位。
// 7天
Duration duration1 = Duration.of(7, ChronoUnit.DAYS);
// 60秒
Duration duration2 = Duration.of(60, ChronoUnit.SECONDS);
}
2.5 Period
Period
與Duration
類似,獲取一個時間段,只不過單位為年月日,也可以通過of
方法和between
方法創建,between
方法接收的參數為LocalDate
。
private static void period() {
// 通過of方法
Period period = Period.of(2012, 12, 24);
// 通過between方法
Period period1 = Period.between(LocalDate.now(), LocalDate.of(2020,12,31));
}
三、時間操作
3.1 時間比較
isBefore()
和isAfter()
判斷給定的時間或日期是在另一個時間/日期之前還是之后。
以LocalDate
為例,LocalDateTime
/LocalTime
同理。
public static void compare() {
LocalDate thisDay = LocalDate.of(2008, 8, 8);
LocalDate otherDay = LocalDate.of(2018, 8, 8);
// 晚於
boolean isAfter = thisDay.isAfter(otherDay);
System.out.println(isAfter);
// 早於
boolean isBefore = thisDay.isBefore(otherDay);
System.out.println(isBefore);
}
3.2 增加/減少年數、月數、天數
以
LocalDateTime
為例,LocalDate
/LocalTime
同理。
public static void plusAndMinus() {
// 增加
LocalDateTime today = LocalDateTime.now();
LocalDateTime nextYearDay = today.plusYears(1);
System.out.println("下一年的今天是:" + nextYearDay);
LocalDateTime nextMonthDay = today.plus(1, ChronoUnit.MONTHS);
System.out.println("下一個月的今天是:" + nextMonthDay);
//減少
LocalDateTime lastMonthDay = today.minusMonths(1);
LocalDateTime lastYearDay = today.minus(1, ChronoUnit.YEARS);
System.out.println("一個月前是:" + lastMonthDay);
System.out.println("一年前是:" + lastYearDay);
}
3.3 時間修改
通過
with
修改時間
public static void edit() {
LocalDateTime today = LocalDateTime.now();
// 修改年為2012年
LocalDateTime thisYearDay = today.withYear(2012);
System.out.println("修改年后的時間為:" + thisYearDay);
// 修改為12月
LocalDateTime thisMonthDay = today.with(ChronoField.MONTH_OF_YEAR, 12);
System.out.println("修改月后的時間為:" + thisMonthDay);
}
3.4 時間計算
通過
TemporalAdjusters
的靜態方法 和Duration
計算時間
public static void compute() {
// TemporalAdjusters 的靜態方法
LocalDate today = LocalDate.now();
// 獲取今年的第一天
LocalDate date = today.with(firstDayOfYear());
System.out.println("今年的第一天是:" + date);
// Duration 計算
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusMonths(1);
Duration duration = Duration.between(from, to);
// 區間統計換算
// 總天數
long days = duration.toDays();
System.out.println("相隔" + days + "天");
// 小時數
long hours = duration.toHours();
System.out.println("相隔" + hours + "小時");
// 分鍾數
long minutes = duration.toMinutes();
System.out.println("相隔" + minutes + "分鍾");
}
TemporalAdjusters
的更多方法
方法名稱 | 描述 |
---|---|
dayOfWeekInMonth() |
返回同一個月中每周的第幾天 |
firstDayOfMonth() |
返回當月的第一天 |
firstDayOfNextMonth() |
返回下月的第一天 |
firstDayOfNextYear() |
返回下一年的第一天 |
firstDayOfYear() |
返回本年的第一天 |
firstInMonth() |
返回同一個月中第一個星期幾 |
lastDayOfMonth() |
返回當月的最后一天 |
lastDayOfNextMonth() |
返回下月的最后一天 |
lastDayOfNextYear() |
返回下一年的最后一天 |
lastDayOfYear() |
返回本年的最后一天 |
lastInMonth() |
返回同一個月中最后一個星期幾 |
next() / previous() |
返回后一個/前一個給定的星期幾 |
nextOrSame() / previousOrSame() |
返回后一個/前一個給定的星期幾,如果這個值滿足條件,直接返回 |
四、時間日期格式化
4.1 格式化時間
DateTimeFormatter
默認提供了多種格式化方式,如果默認提供的不能滿足要求,可以通過DateTimeFormatter
的ofPattern
方法創建自定義格式化方式。
public static void format() {
LocalDate today = LocalDate.now();
// 兩種默認格式化時間方式
String todayStr1 = today.format(DateTimeFormatter.BASIC_ISO_DATE);
String todayStr2 = today.format(DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println("格式化時間:" + todayStr1);
System.out.println("格式化時間:" + todayStr2);
//自定義格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String todayStr3 = today.format(dateTimeFormatter);
System.out.println("自定義格式化時間:" + todayStr3);
}
4.2 解析時間
4.1 中以何種方式格式化,這里需以同樣方式解析。
public static void parse() {
LocalDate date1 = LocalDate.parse("20080808", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2008-08-08", DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println(date1);
System.out.println(date2);
}
五、總結
相較於
Date
的優勢
Instant
的精確度更高,可以精確到納秒級;Duration
可以便捷得到時間段內的天數、小時數等;LocalDateTime
能夠快速地獲取年、月、日、下一月等;TemporalAdjusters
類中包含許多常用的靜態方法,避免自己編寫工具類;- 與
Date
的格式化方式SimpleDateFormat
相比,DateTimeFormatter
是線程安全的。