最近工作遇到一个问题,发现java里面的Date类型在一切场景下很难用,正好想到之前java8里面的java.time包中提供了新的时间和日期API,就去先简单了解了一下他们的使用方法。
- 同一个时刻在计算机中存储的本质上只是一个整数,我们称它为Epoch Time,是计算从1970年1月1日零点(格林威治时区/GMT+00:00)到现在所经历的秒数,也被成为时间戳,可以用System.currentTimeMillis()方法获得。
- 以前的Java中使用Date和Calendar来表述时间。Calendar和Date相比,它提供了时区转换的功能。时区用TimeZone对象表示。Calendar只有一种方式获取,即Calendar.getInstance(),而且一获取到就是当前时间。如果我们想给它设置成特定的一个日期和时间,就必须先清除所有字段(Calendar.getInstance().clear();)Date对象可以用SimpleDateFormat(线程不安全)来format成想要的格式。
而Java8中提供了下面几种类型:
- 本地日期和时间:LocalDateTime,LocalDate,LocalTime;
- 带时区的日期和时间:ZonedDateTime;
- 时刻:Instant;
- 时区:ZoneId,ZoneOffset;
- 时间间隔:Duration。
- LocalDate表示日期,LocalTime表示时间,都严格按照ISO 8601的格式。可以直接输出,也可以直接把满足条件的字符串转化为对应类型。当然也可以使用DateTimeFormatter将不同格式的string转化过来。
LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, 注意11=11月
LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);
LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
LocalDate d = LocalDate.parse("2019-11-19");
LocalTime t = LocalTime.parse("15:16:17");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime dt1 = LocalDateTime.parse("2019/11/30 15:16:17", dtf);
LocalDateTime也可以使用with方法来单独调整时间参数(withYear(),withMonth(),withDayOfMonth(),withHour(),withMinute(),withSecond()),还可以通过minus/plusDay/Month/Hours等方法做运算。
- Duration表示两个时刻之间的时间间隔。另一个类似的Period表示两个日期之间的天数。
注意到两个LocalDateTime之间的差值使用Duration表示,类似PT1235H10M30S,表示1235小时10分钟30秒。而两个LocalDate之间的差值用Period表示,类似P1M21D,表示1个月21天。
Duration和Period的表示方法也符合ISO 8601的格式,它以P...T...的形式表示,P...T之间表示日期间隔,T后面表示时间间隔。如果是PT...的格式表示仅有时间间隔。利用ofXxx()或者parse()方法也可以直接创建Duration。
- ZonedDateTime可以理解成LocalDateTime加ZoneId(时区)
时区转化
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
- Instant是时间戳,内部只有两个核心字段:
public final class Instant implements ... {
private final long seconds;
private final int nanos;
}
一个是以秒为单位的时间戳,一个是更精确的纳秒精度。它和System.currentTimeMillis()返回的long相比,只是多了更高精度的纳秒。
时间类型转换
7.新老API转化
- 如果要把旧式的Date或Calendar转换为新API对象,可以通过toInstant()方法转换为Instant对象,再继续转换为ZonedDateTime
Instant ins1 = new Date().toInstant();
// Calendar -> Instant -> ZonedDateTime:
Calendar calendar = Calendar.getInstance();
Instant ins2 = calendar.toInstant();
ZonedDateTime zdt = ins2.atZone(calendar.getTimeZone().toZoneId());
- 如果要把新的ZonedDateTime转换为旧的API对象,只能借助long型时间戳做一个“中转”
// ZonedDateTime -> long:
ZonedDateTime zdt = ZonedDateTime.now();
long ts = zdt.toEpochSecond() * 1000;
// long -> Date:
Date date = new Date(ts);
// long -> Calendar:
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.setTimeZone(TimeZone.getTimeZone(zdt.getZone().getId()));
calendar.setTimeInMillis(zdt.toEpochSecond() * 1000);
暂时先写这么多,如果只是想要使用,这样应该够用了,等有空的时候再看看他们的源码。
参考资料
网友评论