获得时间范围在午夜和当前时间JDK 8之间

时间:2017-08-20 10:19:01

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

我有这种方法来计算midnigt和当前时间的长值:

/**
 * 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;
}

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

2 个答案:

答案 0 :(得分:7)

你也可以这样做:

ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);

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

我想不出更简单的方法。

LocalDateTime vs ZonedDateTime

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

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

当我这样做时:

Europe/London

它会在JVM的默认时区中创建一个LocalDateTime nowLdt = LocalDateTime.now(); ,其中包含当前日期和时间。在这种情况下,它将获得圣保罗时区的当前日期和时间(2017年8月20日 th 17:56 ):

  

2017-08-20T的 17时56 :05.159

当我调用LocalDateTime方法时,它会在指定区域中创建与日期和时间相对应的atZone

ZonedDateTime

ZoneId zoneId = ZoneId.of("Europe/London"); ZonedDateTime nowAtZone = nowLdt.atZone(zoneId); 变量将是:

  

2017-08-20T的 17时56 :05.159 + 01:00 [欧洲/伦敦]

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

nowAtZone

它将是:

  

1503248165159

现在,如果我不使用System.out.println(nowAtZone.toInstant().toEpochMilli()); 而直接使用LocalDateTime代替:

ZonedDateTime

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

  

2017-08-20T的 21时56 :05.170 + 01:00 [欧洲/伦敦]

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

ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);

值为:

  

1503262565170

请注意,它与使用System.out.println(nowZdt.toInstant().toEpochMilli()); 的第一种情况不同(即使您忽略毫秒值的差异,因为小时不同)。如果您希望在指定时区显示当前日期和时间,则必须使用LocalDateTime

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

夏令时

只是提醒DST(夏令时)问题导致的角落案件。我将以我所居住的时区为例(TimeZone.setDefault())。

在圣保罗,DST于2016年10月16日 th 开始:在午夜,时钟从午夜向凌晨1点移动1小时前进(偏移量从{{{ 1}}到America/Sao_Paulo)。因此,在这个时区中,00:00到00:59之间的所有当地时间都不存在(你也可以认为时钟从23:59:59.999999999直接变为01:00)。如果我在此时间间隔内创建了本地日期,则会将其调整为下一个有效时刻:

-03:00

当夏令时结束时:2017年2月19日 th 午夜时钟,返回 1小时,从午夜到晚上23点 18 th < / sup> (偏移量从-02: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] )。因此,从23:00到23:59的所有当地时间都存在两次(在两个偏移中:-02:00-03:00),您必须决定您想要哪一个。 默认情况下,它使用DST结束前的偏移量,但您可以使用-03:00方法在DST结束后获取偏移量:

-02:00

请注意,DST结束前后的日期具有不同的偏移量(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] )。这会影响epochMilli的价值。

如果使用-02:00方法调整时间,也可能发生上述情况。

答案 1 :(得分:1)

修改后,代码现在变得更加简单:

/**
 * 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;
}