Java 8 的日期和時間API

Java 8 以前的日期和時間 API 設計有些不足,Java 8 引入了一套新的API,位于 java.time 包下。

Instant

Instant 表示時刻,不直接對應年月日信息,需要通過時區轉換。

Instant now = Instant.now();

可以根據 Epoch Time(紀元時)創建 Instant。

Instant now = Instant.ofEpochMilli(System.currentTimeMillis());

Instant 和 Date 可以通過紀元時相互轉換:

  • Date 轉 Instant
public static Instant toInstant(Date date) {
    return Instant.ofEpochMilli(date.getTime());
}
  • Instant 轉 Date
public static Date toDate(Instant instant) {
    return new Date(instant.toEpochMilli());
}     

給定一個時刻,使用不同時區解讀,日歷信息是不同的,默認時區是 ZoneId.systemDefault()。

public ZonedDateTime atZone(ZoneId zone)

LocalDateTime

LocalDateTime 表示與時區無關的日期和時間,不直接對應時刻,需要通過時區轉換。

獲取系統默認時區的當前日期和時間:

LocalDateTime dateTime = LocalDateTime.now();

直接用年月日等信息構建 LocalDateTime:

LocalDateTime ldt = LocalDateTime.of(2017, 7, 11, 20, 45, 5);

獲取日歷信息:

public int getYear()

public int getMonthValue()

public int getDayOfMonth()

public int getHour()

public int getMinute()

public int getSecond()

public DayOfWeek getDayOfWeek()  

DayOfWeek 是一個枚舉,有 7 個取值,從 DayOfWeek.MONDAY 到 DayOfWeek.SUN-DAY。

ZoneOffset

LocalDateTime 不能直接轉為時刻 Instant,轉換需要一個參數 ZoneOffset。

ZoneOffset 表示相對于格林尼治的時區差,北京是 +08:00。

public static Instant toBeijingInstant(LocalDateTime ldt) {
  return ldt.toInstant(ZoneOffset.of("+08:00"));
}

LocalDate/LocalTime

可以認為 LocalDateTime 由兩部分組成,一部分是日期 LocalDate,另一部分是時間 LocalTime。

// 表示2017年7月11日
LocalDate ld = LocalDate.of(2017, 7, 11);
// 當前時刻按系統默認時區解讀的日期
LocalDate now = LocalDate.now();

// 表示21點10分34秒
LocalTime lt = LocalTime.of(21, 10, 34);
// 當前時刻按系統默認時區解讀的時間
LocalTime time = LocalTime.now();
LocalDateTime ldt = LocalDateTime.of(2017, 7, 11, 20, 45, 5);

LocalDate ld = ldt.toLocalDate(); // 2017-07-11
LocalTime lt = ldt.toLocalTime(); // 20:45:05

// LocalDate加上時間,結果為2017-07-11 21:18:39
LocalDateTime ldt2 = ld.atTime(21, 18, 39);

// LocalTime加上日期,結果為2016-03-24 20:45:05
LocalDateTime ldt3 = lt.atDate(LocalDate.of(2016, 3, 24));

ZonedDateTime

ZonedDateTime 表示特定時區的日期和時間,獲取系統默認時區的當前日期和時間,除了記錄日歷信息,還會記錄時區。

LocalDateTime.now() 也是獲取默認時區的當前日期和時間,但 LocalDateTime 只單純記錄年月日時分秒等信息。

// 根據Instant和時區構建ZonedDateTime
public static ZonedDateTime ofInstant(Instant instant, ZoneId zone)
  
// 根據LocalDate、LocalTime和ZoneId構造
public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)

ZonedDateTime 可以直接轉換為 Instant。

ZonedDateTime ldt = ZonedDateTime.now();
Instant now = ldt.toInstant();

DateTimeFormatter 格式化

java.time.format.DateTimeFormatter 是線程安全的,用法和 DateFormat 相差不大。

  • DateTimeFormatter.ofPattern(pattern) 實例化
  • format 格式化 TemporalAccessor 接口子類對象轉字符串
  • parse 按格式解析字符串轉為 TemporalAccessor 接口子類對象
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(formatter.format(dateTime));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = "2016-08-18 14:20:45";
LocalDateTime ldt = LocalDateTime.parse(str, formatter);

設置和修改時間

修改時期和時間有兩種方式,一種是直接設置絕對值,另一種是在現有值的基礎上進行相對增減操作。

Java 8的大部分日期和時間類都是不可變類,修改操作是通過創建并返回新對象來實現的,原對象本身不會變。

  • 直接設置

設置時間為下午 3 點 20 分:

LocalDateTime ldt = LocalDateTime.now();
ldt = ldt.withHour(15).withMinute(20).withSecond(0).withNano(0);
LocalDateTime ldt = LocalDateTime.now();
ldt = ldt.toLocalDate().atTime(15, 20);

設置時間為 0 點:

LocalDateTime ldt = LocalDateTime.now();
ldt = ldt.with(ChronoField.MILLI_OF_DAY, 0); // ChronoField 是一個枚舉
LocalDateTime ldt = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
LocalDateTime ldt = LocalDate.now().atTime(0, 0);
  • 增減操作

設置時間為 3 小時 5 分鐘后:

LocalDateTime ldt = LocalDateTime.now();
ldt = ldt.plusHours(3).plusMinutes(5);

設置時間為下一個周二上午 10 點 :

LocalDate ld = LocalDate.now();
if(! ld.getDayOfWeek().equals(DayOfWeek.MONDAY)){
  ld = ld.plusWeeks(1);
}
LocalDateTime ldt = ld.with(ChronoField.DAY_OF_WEEK, 2).atTime(10, 0);

Java 8 有一個專門的接口 TemporalAdjuster,這是一個函數式接口,對日期或時間進行調整。

Instant、LocalDateTime 和 LocalDate 等都實現了它,有一個專門的默認實現類 TemporalAdjusters。

public interface TemporalAdjuster {
  Temporal adjustInto(Temporal temporal);
}

設置時間為下一個周二上午 10 點 :

LocalDate ld = LocalDate.now();
LocalDateTime ldt = ld.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)).atTime(10, 0);

TemporalAdjusters 的 next 實現:

public static TemporalAdjuster next(DayOfWeek dayOfWeek) {
  int dowValue = dayOfWeek.getValue();
  return (temporal) -> {
    int calDow = temporal.get(DAY_OF_WEEK);
    int daysDiff = calDow - dowValue;
    return temporal.plus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS);
  };
}

時間段的計算

Java 8 中表示時間段的類主要有兩個:Period 和 Duration。

Period 表示日期之間的差,用年月日表示,不能表示時間;

Duration 表示時間差,用時分秒等表示,也可以用天表示,一天嚴格等于24小時,不能用年月表示。

LocalDate ld1 = LocalDate.of(2016, 3, 24);
LocalDate ld2 = LocalDate.of(2017, 7, 12);
Period period = Period.between(ld1, ld2);
System.out.println(period.getYears() + "年" + period.getMonths() + "月" + period.getDays() + "天");
long lateMinutes = Duration.between(LocalTime.of(9, 0), LocalTime.now()).toMinutes();

與 Date/Calendar 轉換

Date 可以與 Instant 通過毫秒數相互轉換,對于其他類型,可以通過毫秒數與 Instant 相互轉換。

LocalDateTime 轉換為 Date:

public static Date toDate(LocalDateTime ldt){
  return new Date(ldt.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
}

Date 按默認時區轉換為 LocalDateTime:

public static LocalDateTime toLocalDateTime(Date date) {
  return LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
}

ZonedDateTime 轉換為 Calendar:

public static Calendar toCalendar(ZonedDateTime zdt) {
  TimeZone tz = TimeZone.getTimeZone(zdt.getZone());
  Calendar calendar = Calendar.getInstance(tz);
  calendar.setTimeInMillis(zdt.toInstant().toEpochMilli());
  return calendar;
}

Calendar 轉換為 ZonedDateTime:

public static ZonedDateTime toZonedDateTime(Calendar calendar) {
  ZonedDateTime zdt = ZonedDateTime.ofInstant(
    Instant.ofEpochMilli(calendar.getTimeInMillis()),
    calendar.getTimeZone().toZoneId());
  return zdt;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容