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;
}
共有 0 条评论