Java8 日期时间

发布时间 2023-11-23 20:51:16作者: YangDanMua

概念

彻底弄懂GMT、UTC、时区和夏令时
java中的时间与时区:LocalDateTime和Date

UTC

public static Instant java.time.Instant#now() {
    return Clock.systemUTC().instant();
}

UTC(Coodinated Universal Time),协调世界时,又称世界统一时间、世界标准时间、国际协调时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
在军事中,协调世界时会使用“Z”来表示。又由于Z在无线电联络中使用“Zulu”作代称,协调世界时也会被称为"Zulu time"。

GMT
GMT(Greenwich Mean Time), 格林威治平时(也称格林威治时间)。
它规定太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午12点。

1972年之前,格林威治时间(GMT)一直是世界时间的标准。1972年之后,GMT 不再是一个时间标准了。

T 和 Z
2023-10-23T09:04:59.435228400Z
T表示分隔符(日期和时间分隔符),Z表示的是UTC。
UTC:世界标准时间,在标准时间上加上8小时,即东八区时间,也就是北京时间。

Instant 的 toString 就是 UTC 时间
LocalDateTime 的 toString 无论哪个时区,都不会有 Z

// 2023-10-23T09:06:14.321339500Z
// 2023-10-23T17:06:14.383132800
System.out.println(Instant.now());
System.out.println(LocalDateTime.now());


public String java.time.LocalDateTime#toString() {
    return date.toString() + 'T' + time.toString();
}

Instant

Instant 类返回的值计算从 1970 年 1 月 1 日(1970-01-01T00:00:0Z)第一秒开始的时间, 也称为 EPOCH。 发生在时期之前的瞬间具有负值,并且发生在时期后的瞬间具有正值。

  • now
  • getEpochSecond:秒数
  • getNano:adjustment,在 getEpochSecond 基础上便宜的纳秒数,也即 getEpochSecond 转纳秒 + getNano 才是真正的纳秒

System.currentTimeMillis()

输出

8:22:41 GMT
...16:22:41.193561500
public class Demo04 {
    public static void main(String[] args) {
        printNow();
        System.out.println(LocalDateTime.now());
    }


    private static void printNow() {
        //获得系统的时间,单位为毫秒,转换为妙
        long totalMilliSeconds = System.currentTimeMillis();
        long totalSeconds = totalMilliSeconds / 1000;

        //求出现在的秒
        long currentSecond = totalSeconds % 60;

        //求出现在的分
        long totalMinutes = totalSeconds / 60;
        long currentMinute = totalMinutes % 60;

        //求出现在的小时
        long totalHour = totalMinutes / 60;
        long currentHour = totalHour % 24;

        //显示时间
        System.out.println(currentHour + ":" + currentMinute + ":" + currentSecond + " GMT");
    }
}

System.currentTimeMillis() 无论是哪个时区在同一时刻得到的结果都是相同的,printNow 得到的也是 UTC 时间,相对北京时间慢 8h。

Instant.now()

public static Instant java.time.Instant#now() {
    return Clock.systemUTC().instant();
}

public static Clock java.time.Clock#systemUTC {
    return SystemClock.UTC;
}

static final class SystemClock extends Clock implements Serializable {
    private static final long OFFSET_SEED =
            System.currentTimeMillis() / 1000 - 1024; // initial offest
    static final SystemClock UTC = new SystemClock(ZoneOffset.UTC);

    private final ZoneId zone;

    // 注意这个是 SystemClock.OFFSET_SEED, static final 的
    private transient long offset;

    SystemClock(ZoneId zone) {
        this.zone = zone;
        this.offset = OFFSET_SEED;
    }

    // 新的时区, 新的实例
    public Clock withZone(ZoneId zone) {
        if (zone.equals(this.zone)) {  // intentional NPE
            return this;
        }
        return new SystemClock(zone);
    }

    // 毫秒直接使用 System.currentTimeMillis
    public long millis() {
        return System.currentTimeMillis();
    }

    // 转 instant
    public Instant instant() {
        // 注意这个是 SystemClock.OFFSET_SEED, static final 的
        long localOffset = offset;
        // 偏移, 相对于 localOffset 这个时间点的偏移
        long adjustment = VM.getNanoTimeAdjustment(localOffset);

        // 说明相对 OFFSET_SEED 时间太远, 重新获取
        if (adjustment == -1) {
            localOffset = System.currentTimeMillis()/1000 - 1024;

            // retry
            adjustment = VM.getNanoTimeAdjustment(localOffset);

            if (adjustment == -1) {
                throw new InternalError("Offset " + localOffset + " is not in range");
            } else {
                offset = localOffset;
            }
        }
        // 转 Instant
        return Instant.ofEpochSecond(localOffset, adjustment);
    }
}

Instant 转 LocalDateTime

会加上时区偏移对应的秒数来最终获取时间,也即 UTC 时间 + 时区相差的时间

LocalDateTime.ofInstant(Instant.now(), ZoneOffset.ofHours(8));
LocalDate.ofInstant(Instant.now(), ZoneOffset.ofHours(8));
LocalTime.ofInstant(Instant.now(), ZoneOffset.ofHours(8));

LocalDateTime 是不带时区的(LocalDate、LocalTime也不带)。

ZonedDateTime、OffsetDateTime 是带时区的。

System.out.println(ZonedDateTime.now());
System.out.println(OffsetDateTime.now());

时区当地时间 + 相对 UTC 时间偏移 + 时区

2023-10-23T17:14:08.198466100+08:00[Asia/Shanghai]
2023-10-23T17:14:08.198466100+08:00

Instant 转

System.out.println(ZonedDateTime.ofInstant(Instant.now(), ZoneOffset.ofHours(8)));
System.out.println(OffsetDateTime.ofInstant(Instant.now(), ZoneOffset.ofHours(8)));
2023-10-23T17:15:14.939259900+08:00
2023-10-23T17:15:14.942248300+08:00

转 Instant

  • LocalDateTime.toInstant:需要时区参数,从而能转成 UTC 的毫秒数
  • ZonedDateTime、OffsetDateTime 能直接 toInstant

Duration/Period 时间差

  • Duration:用于计算 2 个时间(LocalTime,时分秒)的差值
  • Period:用于计算 2 个 日期(LocalDate,年月日)的差值

between 方法

Date、LocalDate 转换

主要是通过中间 Instant 转换,因为时区不同,但是同一时刻 Instant 是相同的