目錄
背景
在1.8以前, 我們通常使用 Date / Calendar 等util類來進行時間的描述與操作, 到目前為止Date還是被廣泛使用中, 但是Date類有幾點不足:
- 粒度不夠細: 最小次元為毫秒, 不支援納秒級别
- 無時區: Date固定采用TimeZone中給定的預設時區
- 不夠便捷: 必須通過Calendar類進行時間操作
- 變量歧義: Calendar中, 描述月份不是從112而是從011; 描述星期時第1天是周日而不是周一;
- 不夠抽象: 一個Date為特定的時刻, 無法單純的描述某一年或某一天
- ...
是以, jdk1.8版本在java.time包下新增了一系列的類, 試圖解決以上不足
包結構
java.time包
常用時間處理類包
時間點
類 | 說明 |
Clock | 抽象時鐘類 |
├ SystemClock | 系統時鐘類,類似System.currentTimeMillis() |
├ FixedClock | 固定時間時鐘類,包含時區資訊 |
├ OffsetClock | 時差時鐘類 |
└ TickClock | 自定義步長時鐘類,不足步長時按步長舍入 |
Instant | 時刻類,用于描述某一個納秒級别精度瞬間,不包含時區資訊 |
時間段
類 | 說明 |
Duration | 秒級别精度持續時間 |
Period | 日級别精度持續時間 |
日期
類 | 說明 |
LocalDate | 本地日期類,描述一個固定的日期,不可變類,包含年/月/日 |
LocalTime | 本地時間類,描述一個時間點, 包含時/分/秒/納秒 |
LocalDateTime | 本地日期時間類, LocalDate與LocalTime的組合 |
OffsetTime | 本地時區時間, 包含時區資訊(ZoneOffset)的LocalTime |
OffsetDateTime | 本地時區日期時間, 包含時區資訊(ZoneOffset)的LocalDateTime |
Year | 年分類, 專門用來描述某一年 |
YearMonth | 年月類, 專門用來描述某一年的某一個月 |
MonthDay | 月日類, 專門用來描述某一個月的某一天 |
Month | 1到12月的枚舉類, 映射的數值分别是1~12 |
DayOfWeek | 周一到周日的枚舉值, 映射的數值分别是1~7 |
時區
類 | 說明 |
ZonedDateTime | 區域時間類, 它用來精準的描述某一個Zone區域的時間 |
ZoneId | 抽象時區ID類, 用于辨別用于在Instant和LocalDateTime之間轉換的規則 |
├ ZoneOffset | 時區偏移量, 用于描述某一個時區相對UTC時間的偏移量, 比如東八區的id為: +8 |
└ ZoneRegion | 地理區域類, 配合ZoneRulesProvider類擷取該地區的時間規則(ZoneRules) |
java.time.format包
時間格式化/解析工具包
類 | 說明 |
DateTimeFormatter | 用于列印和解析日期時間對象的格式化程式 |
DateTimeFormatterBuilder | DateTimeFormatter構造器 |
DecimalStyle | 日期和時間格式中使用的數字樣式,包括正符号/負符号/零/小數點的樣式 |
FormatStyle | 枚舉類, 描述以何種模式來格式化日期或時間 |
常用操作
1. 擷取目前時間
中原標準時間:
/*
* 底層實作 Clock.systemUTC().instant()
* 傳回自1970/1/1 UTC以來的秒數,結尾Z表示UTC
*/
System.out.println(Instant.now());
System.out.println(LocalDateTime.now());
System.out.println(OffsetDateTime.now());
System.out.println(ZonedDateTime.now());
System.out.println(LocalDate.now());
System.out.println(LocalTime.now());
結果:
2023-05-22T10:47:30.842Z
2023-05-22T18:47:30.913
2023-05-22T18:47:30.913+08:00
2023-05-22T18:47:30.914+08:00[Asia/Shanghai]
2023-05-22
18:47:30.914
2. 字元串轉時間
String dateTime = "2023-05-22 13:20:12";
LocalDateTime localDateTime = LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
3. 格式化時間
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.toFormatter();
System.out.println(dateTime.format(dtf));
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
結果:
2023-05-22T12:16:45.725
2023-05-22 12:16:45
4. 擷取某個時間域
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
int monthValue = now.getMonthValue();
int dayOfMonth = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nano = now.getNano();
System.out.println(String.format("%d-%d-%d %d:%d:%d.%d", year, monthValue, dayOfMonth, hour, minute, second, nano));
結果:
2023-5-22 17:43:10.730000000
Month month = now.getMonth();
String monthName1 = month.getDisplayName(TextStyle.SHORT, Locale.CHINA);
String monthName2 = month.getDisplayName(TextStyle.SHORT, Locale.PRC);
String monthName3 = month.getDisplayName(TextStyle.SHORT, Locale.CHINESE);
String monthName4 = month.getDisplayName(TextStyle.SHORT, Locale.SIMPLIFIED_CHINESE);
System.out.println(String.format("%s %s %s %s", monthName1, monthName2, monthName3, monthName4));
結果:
五月 五月 五月 五月
Month month = Month.JUNE;
String monthName1 = month.getDisplayName(TextStyle.SHORT, Locale.ENGLISH);
String monthName2 = month.getDisplayName(TextStyle.SHORT_STANDALONE, Locale.ENGLISH);
String monthName3 = month.getDisplayName(TextStyle.NARROW, Locale.ENGLISH);
String monthName4 = month.getDisplayName(TextStyle.NARROW_STANDALONE, Locale.ENGLISH);
String monthName5 = month.getDisplayName(TextStyle.FULL, Locale.ENGLISH);
String monthName6 = month.getDisplayName(TextStyle.FULL_STANDALONE, Locale.ENGLISH);
System.out.println(String.format("%s %s %s %s %s %s", monthName1, monthName2, monthName3, monthName4, monthName5, monthName6));
DayOfWeek dayOfWeek = now.getDayOfWeek();
String dayOfWeekName = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.CHINA);
System.out.println(dayOfWeekName);
結果:
Jun Jun J J June June
星期一
5. 時間比較
LocalDateTime dateTime1 = LocalDateTime.parse("2023-05-01 15:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime dateTime2 = LocalDateTime.now();
System.out.println(dateTime1.isBefore(dateTime2));
結果:
true
6. 時間計算
增減時間
// 增減分秒
LocalDateTime dateTime = LocalDateTime.parse("2023-05-01 15:30:00.100", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.n"));
System.out.println(dateTime);
dateTime = dateTime.plusYears(1);
dateTime = dateTime.minusMonths(1);
dateTime = dateTime.plusDays(1);
dateTime = dateTime.plusHours(1);
dateTime = dateTime.plusMinutes(1);
dateTime = dateTime.plusSeconds(1);
dateTime = dateTime.plusNanos(300);
System.out.println(dateTime);
結果:
2023-05-01T15:30:00.000000100
2024-04-02T16:31:01.000000400
計算時間差
LocalDateTime dateTime1 = LocalDateTime.now();
// 設定時間
dateTime1 = dateTime1
.withYear(2023)
.withMonth(5)
.withDayOfMonth(1)
.withHour(0)
.withMinute(0)
.withSecond(0);
LocalDateTime dateTime2 = LocalDateTime.parse("2023-05-02 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
/*
* 計算 dateTime1 和 dateTime2 時間差
* 2023-05-01 00:00:00.000000400
* 2023-05-02 00:00:00
* 相差不足一天
*/
long days = dateTime1.until(dateTime2, ChronoUnit.DAYS);
System.out.println(String.format("%s - %s = %d days", dateTime1, dateTime2, days));
dateTime1 = dateTime1.withNano(0);
days = dateTime1.until(dateTime2, ChronoUnit.DAYS);
System.out.println(String.format("%s - %s = %d days", dateTime1, dateTime2, days));
結果:
2023-05-01T00:00:00.000000400 - 2023-05-02T00:00 = 0 days
2023-05-01T00:00 - 2023-05-02T00:00 = 1 days
LocalDate date1 = LocalDate.of(2022, 5, 20);
LocalDate date2 = LocalDate.of(2023, 6, 1);
Period between = Period.between(date1, date2);
System.out.println(String.format("diff: %d years, %d months, %d days", between.getYears(), between.getMonths(), between.getDays()));
LocalDateTime time1 = LocalDateTime.of(2023, 5, 20, 8, 30, 20, 100);
LocalDateTime time2 = LocalDateTime.of(2023, 5, 20, 8, 30, 25);
Duration duration = Duration.between(time1, time2);
System.out.println(String.format("diff: %d seconds, %d nanos", duration.getSeconds(), duration.getNano()));
結果:
diff: 1 years, 0 months, 12 days
diff: 4 seconds, 999999900 nanos
時區轉換
private final static DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
/**
* 把fromZone時區的時間轉換成toZone時區的時間
*/
public static String zoneChange(String time, String fromZone, String toZone) {
return LocalDateTime.parse(time, DATETIME_FORMATTER)
.atZone(ZoneId.of(fromZone))
.withZoneSameInstant(ZoneId.of(toZone))
.format(DATETIME_FORMATTER);
}