Java 日历没有给出结束时间

Java Calendar not giving end of hour

基于纪元秒,我将其转换为小时的开始和结束。

    long epochSeconds = 1589374800L;
    Instant instant = Instant.ofEpochSecond(epochSeconds);
    Calendar now = Calendar.getInstance(TimeZone.getTimeZone(ZoneId.of("Asia/Kolkata")));
    now.setTimeInMillis(instant.toEpochMilli());

    System.out.println(now.getTime()); // Correct --> Wed May 13 06:00:00 PDT 2020

    Calendar endOfHour = (Calendar)now.clone();
    endOfHour.set(Calendar.MINUTE, 59);
    endOfHour.set(Calendar.SECOND, 59);
    endOfHour.set(Calendar.MILLISECOND, 999);

    System.out.println(endOfHour.getTime()); // Wrong ---> Wed May 13 06:29:59 PDT 2020

小时的开始似乎是正确的,但小时的结束却不正确,而不是最多 59 分 59 秒 999 毫秒,它只给出了半小时的差异。

您正在混合使用 java.timejava.util.Calendar 类型。不要那样做。一方面,您将丢失在克隆时指定的 TimeZone。基本上,Calendar一团糟。但是你在这里不需要它,比如

long epochSeconds = 1589374800L;
LocalDateTime date = Instant.ofEpochSecond(epochSeconds) //
        .atZone(ZoneId.of("Asia/Kolkata")) //
        .toLocalDateTime();
System.out.println(date);
LocalDateTime endOfHour = date.withMinute(59) //
        .withSecond(59) //
        .with(ChronoField.MILLI_OF_SECOND, 999);
System.out.println(endOfHour);

应该能满足您的需求。这里输出

2020-05-13T18:30
2020-05-13T18:59:59.999

两点(最后还有一点):

  1. 只是重复已经说过的话:不要混淆旧的和现代的日期时间 类。只使用现代的。忘记旧的。无论如何,它们总是设计得很糟糕。
  2. 您观察到的结果是正确的,符合预期。

您正在为您的(过时的)Calendar 对象使用 Asia/Kolkata 时区。 Asia/Kolkata 时区的特殊之处在于它不像大多数时区那样从 UTC 偏移整小时数,而是 +05:30,五个半小时。让我们先看看你在这个时区的时间。您的纪元秒值(又名 Unix 时间戳)等于 Asia/Kolkata 中的 2020-05-13T18:30:00+05:30。 Asia/Kolkata 那个小时的结束时间是 2020-05-13T18:59:59.999。这就是您得到的结果。

看来您是 运行 您在不同时区的 JVM 上的程序(可能是 America/Vancouver 或 America/Los_Angeles)。该时区与 UTC 相差整整几个小时,因此印度的 18:59:59.999 等于 06:29:59 PDT(您的时区)。

半开: 我答应过你第三点。将小时的结束表示为整小时,这里是 19:00 而不是 18:59:59 和一些 9。哲学论证:一个小时不会在下一个小时开始前一毫秒结束,所以这是不正确的。实际论据:它使您无需决定需要多少个 9。 java.time 具有纳秒级的精度,因此能够表示从一小时结束到下一小时开始之间的近一百万个时间点。您冒着达到这样一个点并将其分配给错误时间的风险。在比较中,只需确保检查一个时间点是否 strictly 在整小时结束之前。

如果您确实需要 Calendar 个对象 作为遗产 API:

    ZoneId zone = ZoneId.of("Asia/Kolkata");

    long epochSeconds = 1589374800L;
    ZonedDateTime now = Instant.ofEpochSecond(epochSeconds).atZone(zone);
    Calendar nowAsOldfashionedCalendar = GregorianCalendar.from(now);

    ZonedDateTime endOfHour
            = now.plusHours(1).truncatedTo(ChronoUnit.HOURS).minusNanos(1);
    Calendar endOfHourAsOldfashionedCalendar = GregorianCalendar.from(endOfHour);

我包含了 .minusNanos(1) 以获得前一小时的最后纳秒,但正如我所说,如果可以的话,您应该更愿意省略它。转换为 GregorianCalendar 将截断为毫秒,并为您提供与问题代码相同的结果。