天天看點

Java.time時間處理

作者:張記代碼鋪

目錄

Java.time時間處理

背景

在1.8以前, 我們通常使用 Date / Calendar 等util類來進行時間的描述與操作, 到目前為止Date還是被廣泛使用中, 但是Date類有幾點不足:

  1. 粒度不夠細: 最小次元為毫秒, 不支援納秒級别
  2. 無時區: Date固定采用TimeZone中給定的預設時區
  3. 不夠便捷: 必須通過Calendar類進行時間操作
  4. 變量歧義: Calendar中, 描述月份不是從112而是從011; 描述星期時第1天是周日而不是周一;
  5. 不夠抽象: 一個Date為特定的時刻, 無法單純的描述某一年或某一天
  6. ...

是以, jdk1.8版本在java.time包下新增了一系列的類, 試圖解決以上不足

包結構

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. 擷取目前時間

中原標準時間:

Java.time時間處理
/*
 * 底層實作 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);
}           

繼續閱讀