基本概念
Java8
已经发布很长时间了,但是仍然有很多小伙伴不熟悉Java8
的日期时间API
。今天我们来一起学习一下。
首先了解几个概念,也就是日期时间在Java
中所体现的几种形式,这几种形式是可以相互转换的。:
- 时间戳:
long
长整型1604030178372
指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
- 日期时间对象:
Clock
:时钟1604039715148
Instant
:瞬时2020-10-30T06:35:15.148Z
LocalDate
:日期2020-10-30
LocalTime
:时间14:35:15.161
LocalDateTime
:日期时间2020-10-30T14:35:15.162
OffsetTime
:偏移时间14:35:15.162+08:00
(OffsetTime = LocalTime + ZoneOffset)
OffsetDateTime
:偏移日期时间2020-10-30T14:35:15.162+08:00
(OffsetDateTime= LocalDateTime + ZoneOffset)
ZonedDateTime
:时区日期时间2020-10-30T14:35:15.163+08:00[Asia/Shanghai]
(ZonedDateTime= LocalDateTime + ZoneId)
- 字符串:
String
:文本2020-10-30
、2020-10-30 14:35:15
- 时间间隔:
Duration
:时间间隔PT1H2M38S
PT
前缀,H
表示小时,M
表示分钟,S
表示秒。
时区:
ZoneId
:时区ID Asia/Shanghai
ZoneOffset
:时区偏移,继承ZoneId
。+08:00
ZoneId
和ZoneOffset
区别:如果按照时区划分没啥区别。但是呢,时区是固定的。也就是ZoneId
是固定的。ZoneOffset
对其进行了扩展,可以任意调整偏移。
public static ZoneOffset ofHoursMinutesSeconds(int hours, int minutes, int seconds)
时区导致每个地区的时间是不一样的,比如现在北京时间
10:00
点,那么东京时间就是11:00
点。相差一个小时。体现到代码上就是,同一个时间戳使用不同的时区进行转换得到的日期时间是有时差的。
源码分析
Instant
LocalDate
LocalTime
LocalDateTime
OffsetTime
OffsetDateTime
ZonedDateTime
这几个类都继承了Temporal
。- 其实一切的根源就在于一行代码,简直不要太熟悉。
System.currentTimeMillis();
接下来我们一步一步分析这一行代码是如何创建的Java8日期时间API。
Clock
public abstract class Clock {
// 1 静态方法创建Clock对象
public static Clock systemDefaultZone() {
return new SystemClock(ZoneId.systemDefault());
}
public long millis() {
return instant().toEpochMilli();
}
public abstract Instant instant();
// 静态内部类实现Clock
static final class SystemClock extends Clock implements Serializable {
// 时区
private final ZoneId zone;
SystemClock(ZoneId zone) {
this.zone = zone;
}
// 获取毫秒数
@Override
public long millis() {
return System.currentTimeMillis();
}
// 转换成Instant对象
@Override
public Instant instant() {
return Instant.ofEpochMilli(millis());
}
}
}
LocalDateTime
public final class LocalDateTime implements Temporal, TemporalAdjuster, ChronoLocalDateTime<LocalDate>, Serializable {
private final LocalDate date;
private final LocalTime time;
// 创建日期时间:Clock.systemDefaultZone()
public static LocalDateTime now() {
return now(Clock.systemDefaultZone());
}
public static LocalDateTime now(Clock clock) {
Objects.requireNonNull(clock, "clock");
final Instant now = clock.instant(); // called once
ZoneOffset offset = clock.getZone().getRules().getOffset(now);
return ofEpochSecond(now.getEpochSecond(), now.getNano(), offset);
}
// 通过秒、毫秒、时区偏移来创建LocalDateTime.
public static LocalDateTime ofEpochSecond(long epochSecond, int nanoOfSecond, ZoneOffset offset) {
Objects.requireNonNull(offset, "offset");
NANO_OF_SECOND.checkValidValue(nanoOfSecond);
long localSecond = epochSecond + offset.getTotalSeconds(); // overflow caught later
long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY);
int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
LocalDate date = LocalDate.ofEpochDay(localEpochDay);
LocalTime time = LocalTime.ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + nanoOfSecond);
return new LocalDateTime(date, time);
}
}
由此可以看出
Clock
每次获取都是新的毫秒数,而Temporal
子类创建之后如果不修改的话就代表时间线上某一时刻的时间。
写到这里不想写了,太费劲了,奈何文笔太差。。。。
以LocalDateTime
举例,说白了,一个日期时间能有啥功能,首先我们得创建一个LocalDateTime
对象吧。
创建完之后我怎么也得看看里边是啥吧:比如年、月、日、时、分、秒等。
一看发现某个字段值不对,可以通过如下方法重新设置。
然后呢,我还想跟别人创建的日期比较比较。
比较完之后感觉不行,比了半天输了,那就不服了。那咋办,比靠后输了那就加点时间呗,比靠前输了那就减点时间呗。反正得赢。
折腾半天可算赢了,不能白赢。得通知通知其他人庆祝一下呗。转成字符串,告诉String
小老弟。
转成其他日期时间对象。
常用功能案例
- 获取日期时间
LocalDateTime.now();
LocalDateTime.of(2020, 10, 1, 10, 10, 10);
LocalDateTime.parse("2020-10-01 10:10:10", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
- 日期时间转字符串
LocalDateTime dateTime = LocalDateTime.now();
dateTime.format(DateTimeFormatter.ISO_DATE_TIME);
dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
- 字符串转日期时间
String dateStr = "2020-01-01 11:11:11";
LocalDateTime dateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
- 日期时间转毫秒
// 1秒 = 1000毫秒
// 1毫秒 = 1000000纳秒
LocalDateTime dateTime = LocalDateTime.now();
long nano = dateTime.getNano();// 纳秒
long second = dateTime.toEpochSecond(ZoneOffset.of("+08:00"));// 秒
long milli = dateTime.toInstant(ZoneOffset.of("+08:00")).toEpochMilli();// 毫秒
- 毫秒转日期时间
long milli = 1604047456505l;
LocalDateTime.ofInstant(Instant.ofEpochMilli(milli), ZoneId.systemDefault());
- 当前时间是否在指定时间内
LocalDateTime start = LocalDateTime.of(2020, 10, 1, 10, 10);// 2020-10-1 10:10
LocalDateTime end = LocalDateTime.of(2020, 11, 2, 10, 10);// 2020-11-2 10:10
LocalDateTime currTime = LocalDateTime.now();
boolean isBetween = currTime.isAfter(start) && currTime.isBefore(end);
- 比较两个时间
LocalDateTime a = LocalDateTime.of(2020, 10, 1, 10, 10);// 2020-10-1 10:10
LocalDateTime b = LocalDateTime.of(2020, 11, 2, 10, 10);// 2020-11-2 10:10
boolean isAfter = a.isAfter(b);
boolean isBefore = a.isBefore(b);
boolean isEqual = a.isEqual(b);
int result = a.compareTo(b);// a<b=-1;a=b=0;a>b=1
- 获取两个时间的时间间隔
LocalDateTime a = LocalDateTime.of(2020, 10, 1, 10, 10);// 2020-10-1 10:10
LocalDateTime b = LocalDateTime.of(2020, 11, 2, 10, 10);// 2020-11-2 10:10
Duration duration = Duration.between(a, b);
long days = duration.toDays();
long hours = duration.toHours();
long minutes = duration.toMinutes();
long millis = duration.toMillis();
如果还有其他没有涉及到的常用功能,可以在评论区留言。。。。。