• LocalDateTime与时间戳


    众所周知,如果想把 LocalDateTime 转为时间戳,需要先指定时区,然后才能转为时间戳,例如:

    LocalDateTime localDateTime = LocalDateTime.now();  
    ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());  
    long second = zonedDateTime.toEpochSecond();
    
    • 1
    • 2
    • 3

    但是既然 LocalDateTime(本地时间)已经确定当前时间,为什么不能直接转为时间戳?

    因为时间戳指的是自 1970 年 1 月 1 日(00:00:00 UTC/GMT)以来的秒数,所以无论在哪个时区,同一时间获取的都是相同时间戳,可以用于跨时区。但是我们现实生活用到的本地时间是跟时区挂钩的,中国所在的时区是东八区,会比 UTC 时间快 8 个小时。时间戳是从 UTC 时间得来的,所以时间戳与本地时间的相互转换,需要根据时区来转换。

    通过查看 LocalDateTime.now() 方法源码,也能看出会先获取系统默认时区,然后再时间戳和时区获得本地时间。

    public static LocalDateTime now() {  
        return now(Clock.systemDefaultZone());  //先获取系统默认时区
    }
    
    • 1
    • 2
    • 3

    Clock.systemDefaultZone() 方法一直往下走,到达获取系统默认时区的重要方法。首先获取 JVM 参数中的时区信息,如果不存在则获取操作系统的时区信息,否则默认使用 GMT。

    //java.util.TimeZone#setDefaultZone
    private static synchronized TimeZone setDefaultZone() {  
        TimeZone tz;  
        // get the time zone ID from the system properties  
        Properties props = GetPropertyAction.privilegedGetProperties();  
        String zoneID = props.getProperty("user.timezone");  //通过JVM属性获取
      
        // if the time zone ID is not set (yet), perform the  
        // platform to Java time zone ID mapping.   
        if (zoneID == null || zoneID.isEmpty()) {  
            String javaHome = StaticProperty.javaHome();  
            try {  
                zoneID = getSystemTimeZoneID(javaHome);  //获取操作系统时区
                if (zoneID == null) {  
                    zoneID = GMT_ID;  //默认使用GMT
                }  
            } catch (NullPointerException e) {  
                zoneID = GMT_ID;  
            }  
        }  
      
        // Get the time zone for zoneID. But not fall back to  
        // "GMT" here.   
        tz = getTimeZone(zoneID, false);   //从默认的时区配置和java根目录下的/lib/tzdb.dat中根据时区id获取时区信息。
      
        if (tz == null) {  
            // If the given zone ID is unknown in Java, try to  
            // get the GMT-offset-based time zone ID,        
            // a.k.a. custom time zone ID (e.g., "GMT-08:00").        
            String gmtOffsetID = getSystemGMTOffsetID();  
            if (gmtOffsetID != null) {  
                zoneID = gmtOffsetID;  
            }  
            tz = getTimeZone(zoneID, true);  
        }  
        assert tz != null;  
      
        final String id = zoneID;  
        props.setProperty("user.timezone", id);  
      
        defaultTimeZone = tz;  
        return tz;  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    getSystemTimeZoneID() 是一个 native 方法,根据不同操作系统,获取的方法不同。对于 window 系统,获取的是注册表里的时区信息。

    //java.util.TimeZone#getSystemTimeZoneID
    private static native String getSystemTimeZoneID(String javaHome);
    
    • 1
    • 2

    获取到系统默认时区后,通过获取 1970 年 1 月 1 日午夜至今的秒数和时区偏移量,计算出本地时间的秒数,借此创建 LocalDateTime 实例。

    //java.time.LocalDateTime#now(java.time.Clock)
    public static LocalDateTime now(Clock clock) {  
        Objects.requireNonNull(clock, "clock");  
        final Instant now = clock.instant();  // called once   //获取1970 年 1 月 1 日午夜至今的秒数
        ZoneOffset offset = clock.getZone().getRules().getOffset(now);  //获取时区偏移量
        return ofEpochSecond(now.getEpochSecond(), now.getNano(), offset);  
    }
    
    //java.time.LocalDateTime#ofEpochSecond
    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  //将UTC时间下的秒数和时区偏移的秒数相加,获得本地时间的秒数
        long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY);  //将本地时间的秒数除以每天的秒数,得到天数
        int secsOfDay = 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);  //通过LocalDate和LocalTime创建LocalDateTime实例
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    既然本地时间是根据 1970 年 1 月 1 日午夜至今的秒数加上时区偏移的秒数得到的,那么从 LocalDateTime 转换为时间戳也自然需要减去时区偏移的秒数。

    //java.time.chrono.ChronoZonedDateTime#toEpochSecond
    default long toEpochSecond() {  
        long epochDay = toLocalDate().toEpochDay();  
        long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();  //获取本地时间的秒数
        secs -= getOffset().getTotalSeconds();  //减去时区偏移的秒数
        return secs;  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    unittest框架
    GeoServer运行报错503,……Unmapped relationship: 7
    nestjs 结合LibreOffice 实现word转pdf在线预览
    基于Java毕业设计房产客户信息管理系统源码+系统+mysql+lw文档+部署软件
    Flutter组件--Align和AnimatedAlign
    蒸散发与植被总初级生产力的区域数据下载、处理与显示
    LeetCode_动态规划_中等_368.最大整除子集
    Jedis入门及对Redis数据结构的操作
    MindSpore社区群组介绍系列之三——WG- Molecular Modeling
    2核4G游戏服务器推荐(阿里云/腾讯云/华为云)
  • 原文地址:https://blog.csdn.net/weixin_54227075/article/details/133849342