4

我有这种方法来计算 midnigt 和当前时间作为 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;
}

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

4

2 回答 2

9

你也可以这样做:

ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);

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

我想不出更简单的方法来做到这一点。


LocalDateTime 与 ZonedDateTime

LocalDateTime.now().atZone(zoneId)和之间有一个(棘手的)区别ZonedDateTime.now(zoneId)

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

当我做:

LocalDateTime nowLdt = LocalDateTime.now();

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

2017-08-20T 17: 56 :05.159

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

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

nowAtZone变量将是:

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

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

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

这将是:

1503248165159

现在,如果我不使用LocalDateTimeand 直接使用 theZonedDateTime代替:

ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);

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

2017-08-20T 21: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 中运行的另一个应用程序调用TimeZone.setDefault()),它也会发生变化。


夏令时

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

在圣保罗,夏令时于 2016 年 10 月 16开始:在午夜,时钟从午夜向前移动 1 小时到凌晨 1 点(偏移从-03:00变为-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午夜,时钟向后移动1 小时,从午夜到18下午 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如果您使用方法调整时间,也会发生上述情况。

于 2017-08-20T10:37:21.710 回答
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;
}
于 2017-08-20T16:52:17.333 回答