Date 获取午夜和当前时间JDK 8之间的时间范围

Date 获取午夜和当前时间JDK 8之间的时间范围,date,datetime,java-8,java-time,zoneddatetime,Date,Datetime,Java 8,Java Time,Zoneddatetime,我使用此方法计算中间时间和当前时间作为长值: /** * 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 lon

我使用此方法计算中间时间和当前时间作为长值:

/**
 * 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;
}
也许有更简单的方法可以做到这一点?

您也可以:

ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);

ZonedDateTime todayAtMidnightZdt = nowZdt.with(LocalTime.MIDNIGHT);
我想不出比这更简单的方法了


LocalDateTime与ZonedDateTime
LocalDateTime.now().atZone(zoneId)
zonedatetime.now(zoneId)
之间有一个(微妙的)区别

对于下面的代码,我使用的是一个JVM,默认时区
美国/圣保罗
,并将尝试获取另一个时区(
欧洲/伦敦
)中的当前日期和时间。现在我运行这个代码,时间是2017年8月20日,但在圣保罗时间是17:56,在伦敦时间是21:56

当我这样做时:

LocalDateTime nowLdt = LocalDateTime.now();
它使用JVM的默认时区中的当前日期和时间创建一个
LocalDateTime
。在本例中,它将获得圣保罗时区的当前日期和时间(即2017年8月20日17: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)相同。请注意,它不是伦敦的当前日期/时间。如果我得到同等的Epochmili:

System.out.println(nowAtZone.toInstant().toEpochMilli());
它将是:

1503248165159

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

ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);
它将获得伦敦的当前日期和时间,即:

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

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

System.out.println(nowZdt.toInstant().toEpochMilli());
该值将为:

150326565170

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

使用
LocalDateTime.now().atZone()
不仅会产生不同的结果,而且如果在不同的JVM中运行,或者JVM默认时区发生变化,结果也会发生变化(有人可能会对其进行错误配置,或者在同一VM中运行的另一个应用程序调用
timezone.setDefault()


夏时制 只需提醒一下由于DST(夏令时)问题而导致的角落案例。我将以我居住的时区为例(
美国/圣保罗

在圣保罗,DST于2016年10月16日开始:午夜,时钟从午夜向前移动1小时至凌晨1点(偏移量从
-03:00
更改为
-02:00
)。因此,00:00到00:59之间的所有本地时间在此时区中都不存在(您也可以认为时钟从23:59:59.99999999直接更改为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日午夜,时钟从2017年2月18日午夜移回1小时(偏移量从
-02:00
变为
-03:00
)。因此,从23:00到23:59的所有本地时间都存在两次(在两个偏移量中:
-03:00
-02:00
),您必须决定要哪一次。 默认情况下,它使用DST结束前的偏移量,但您可以使用
WithLaterOffsetToVerlap()
方法获取DST结束后的偏移量:

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


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

修改后,现在的代码要简单得多:

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

方法
.with(LocalTime.MIDNIGHT)
有帮助。
LocalDateTime.now().atZone(zoneId)
ZonedDateTime.now(zoneId)
-我已经用细节更新了答案。