【问题标题】:Getting time range between midnight and current time JDK 8获取午夜和当前时间JDK 8之间的时间范围
【发布时间】:2018-01-28 13:55:32
【问题描述】:

我有这种方法可以将午夜和当前时间计算为 long 值:

/**
 * Returns the time range between the midnight and current time in milliseconds.
 *
 * @param zoneId time zone ID.
 * @return a {@code long} array, where at index: 0 - midnight time; 1 - current time.
 */

public static long[] todayDateRange(ZoneId zoneId) {
    long[] toReturn = new long[2];

    LocalTime midnight = LocalTime.MIDNIGHT;
    LocalDate today = LocalDate.now(zoneId);
    LocalDateTime todayMidnight = LocalDateTime.of(today, midnight);
    ZonedDateTime todayMidnightZdt = todayMidnight.atZone(zoneId);
    toReturn[0] = todayMidnightZdt.toInstant().toEpochMilli();

    ZonedDateTime nowZdt = LocalDateTime.now().atZone(zoneId);
    toReturn[1] = nowZdt.toInstant().toEpochMilli();
    return toReturn;
}

也许有更简单的方法来做到这一点?

【问题讨论】:

    标签: date datetime java-8 java-time zoneddatetime


    【解决方案1】:

    你也可以这样做:

    ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);
    
    ZonedDateTime todayAtMidnightZdt = nowZdt.with(LocalTime.MIDNIGHT);
    

    我想不出更简单的方法。


    LocalDateTime 与 ZonedDateTime

    LocalDateTime.now().atZone(zoneId)ZonedDateTime.now(zoneId) 之间存在(棘手的)区别。

    对于下面的代码,我正在使用 默认时区America/Sao_Paulo 的 JVM,并将尝试在另一个时区 (Europe/London) 中获取当前日期和时间。在我运行此代码时,现在是 2017 年 8 月 20 日th,但在圣保罗,时间是 17:56,而在伦敦是 21:56强>。

    当我这样做时:

    LocalDateTime nowLdt = LocalDateTime.now();
    

    它创建一个LocalDateTime,其当前日期和时间在 JVM 的默认时区。在这种情况下,它将获取圣保罗时区的当前日期和时间(即 2017 年 8 月 20 日th17:56):

    2017-08-20T17:56:05.159

    当我调用atZone 方法时,它会创建一个ZonedDateTime 对应于指定区域中的this 日期和时间:

    ZoneId zoneId = ZoneId.of("Europe/London");
    ZonedDateTime nowAtZone = nowLdt.atZone(zoneId);
    

    nowAtZone 变量将是:

    2017-08-20T17:56:05.159+01:00[欧洲/伦敦]

    伦敦时区的日期(2017 年 8 月 20 日)和 时间 (17:56) 相同。请注意,它不是伦敦的当前日期/时间。如果我得到等效的 epochMilli:

    System.out.println(nowAtZone.toInstant().toEpochMilli());
    

    会是:

    1503248165159

    现在,如果我不使用LocalDateTime 而直接使用ZonedDateTime

    ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);
    

    它将获取伦敦的当前日期和时间,即:

    2017-08-20T21:56:05.170+01:00[欧洲/伦敦]

    请注意,时间已更改(现在是 21:56)。那是因为现在,此时此刻,那是伦敦的当前时间。如果我得到 epochMilli 值:

    System.out.println(nowZdt.toInstant().toEpochMilli());
    

    值将是:

    1503262565170

    请注意,它与使用LocalDateTime 的第一种情况不同(即使您忽略毫秒值的差异,因为小时 不同)。如果你想要指定时区的当前日期和时间,你必须使用ZonedDateTime.now(zoneId)

    使用LocalDateTime.now().atZone() 不仅会产生不同的结果,而且如果您在不同的 JVM 中运行,或者如果 JVM 默认时区更改(有人可能错误配置它,或者在同一 VM 中运行的另一个应用程序调用 @987654340),它也会改变@)。


    夏令时

    只需提醒由于 DST(夏令时)问题而导致的极端情况。我将以我居住的时区为例 (America/Sao_Paulo)。

    在圣保罗,DST 于 2016 年 10 月 16 日th 开始:在午夜,时钟从午夜到凌晨 1 点向前移动 1 小时(并且偏移量从 @987654342 更改@ 到 -02:00)。因此,该时区不存在 00:00 到 00:59 之间的所有当地时间(您也可以认为时钟从 23:59:59.999999999 直接更改为 01:00)。如果我在此间隔内创建本地日期,则会将其调整为下一个有效时刻:

    ZoneId zone = ZoneId.of("America/Sao_Paulo");
    
    // October 16th 2016 at midnight, DST started in Sao Paulo
    LocalDateTime d = LocalDateTime.of(2016, 10, 16, 0, 0, 0, 0);
    ZonedDateTime z = d.atZone(zone);
    System.out.println(z);// adjusted to 2017-10-15T01:00-02:00[America/Sao_Paulo]
    

    DST 结束时:2017 年 2 月 19 日th 午夜,时钟将向后 1 小时,从午夜到 18th 的下午 23 点(并且偏移量从-02:00 更改为-03:00)。因此,从 23:00 到 23:59 的所有当地时间都存在 两次(在两个偏移量中:-03:00-02:00),您必须决定要使用哪一个。 默认使用夏令时结束前的偏移量,但可以使用withLaterOffsetAtOverlap()方法获取夏令时结束后的偏移量:

    // February 19th 2017 at midnight, DST ends in Sao Paulo
    // local times from 23:00 to 23:59 at 18th exist twice
    LocalDateTime d = LocalDateTime.of(2017, 2, 18, 23, 0, 0, 0);
    // by default, it gets the offset before DST ends
    ZonedDateTime beforeDST = d.atZone(zone);
    System.out.println(beforeDST); // before DST end: 2018-02-17T23:00-02:00[America/Sao_Paulo]
    
    // get the offset after DST ends
    ZonedDateTime afterDST = beforeDST.withLaterOffsetAtOverlap();
    System.out.println(afterDST); // after DST end: 2018-02-17T23:00-03:00[America/Sao_Paulo]
    

    请注意,夏令时结束前后的日期有不同的偏移量(-02:00-03:00)。这会影响 epochMilli 的值。

    如果您使用with方法调整时间,也会发生上述情况。

    【讨论】:

    • 方法 .with(LocalTime.MIDNIGHT) 帮助。
    【解决方案2】:

    修改后的代码现在简单多了:

    /**
     * Returns the time range between the midnight and current time in milliseconds.
     *
     * @param zoneId time zone ID.
     * @return a {@code long} array, where at index: 0 - midnight time; 1 - current time.
     */
    public static long[] todayDateRange(ZoneId zoneId) {
        long[] toReturn = new long[2];
    
        //ZonedDateTime nowZdt = LocalDateTime.now().atZone(zoneId);
        ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);//As suggested by Hugo (tested). 
        ZonedDateTime midZdt = nowZdt.with(LocalTime.MIDNIGHT);
        toReturn[0] = midZdt.toInstant().toEpochMilli();
        toReturn[1] = nowZdt.toInstant().toEpochMilli();
        return toReturn;
    }
    

    【讨论】:

    • LocalDateTime.now().atZone(zoneId)ZonedDateTime.now(zoneId) 之间存在差异 - 我已经用详细信息更新了我的答案。
    猜你喜欢
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-21
    • 2013-04-05
    相关资源
    最近更新 更多