【问题标题】:Converting Instant to LocalDate and back将 Instant 转换为 LocalDate 并返回
【发布时间】:2020-09-24 12:57:08
【问题描述】:

InstantLocalDate 之间转换日期时遇到问题。我需要将我的日期更改为当前一周的星期一(如果是星期三,那么我将更改为星期一):

public static Instant getStartDateAsMonday(Instant startTime) {
    LocalDate monday = startTime.atZone(ZoneId.systemDefault()).toLocalDate()
        .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
    startTime = monday.atStartOfDay(ZoneId.systemDefault()).toInstant();
    return startTime;
}

当我测试它时...

@Test
public void testGetStartDateAsMonday() {
    Instant instant = DateHelperService.getStartDateAsBeginningOnMonday(
        Instant.parse("2020-05-27T00:00:00Z"));
    assertThat(instant).isEqualTo("2020-05-25T00:00:00Z");
}

...测试没有通过并给出输出:

Expected : 2020-05-25T00:00:00Z
Actual   : 2020-05-24T22:00:00Z

我的系统默认时区是GMT+2。当我做atStartOfDay(ZoneId.of("UTC")) 时测试通过了,但我不明白为什么我不能在该转换中使用我的系统默认值。

【问题讨论】:

    标签: java datetime java-time datetime-conversion


    【解决方案1】:

    首先,调用ZoneId.systemDefault() 两次是不好的做法。 JVM 当前的默认时区可能在调用之间发生了变化。该 JVM 内任何应用程序的任何线程中的任何代码都可以立即更改当前默认时区。

    所以,捕获当前的默认值。

    ZoneId z = ZoneId.systemDefault() ;
    

    你说:

    我的系统默认时区是 GMT+2

    GMT+2 表示偏移量而不是时区。偏移量只是小时-分钟-秒数,正数或负数。时区更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。时区的名称格式为Continent/Region

    你说:

    预计:2020-05-25T00:00:00Z

    实际:2020-05-24T22:00:00Z

    您的预期不正确。如果您有一个代表一天中第一刻的时刻,例如 Europe/BrusselsAfrica/Cairo 的时间为 00:00,并且您知道该区域在该日期比 UTC 早两个小时运行,那么直觉上你知道 UTC 早了两个小时。如果布鲁塞尔或开罗的时钟在午夜敲响,您就知道在 UTC、格林威治皇家天文台或冰岛的时钟还不能敲响午夜。午夜不会再打两个小时。因此,UTC 中的日期仍然是“昨天”。而早于 00:00 的两个小时是 22:00。所以你的实际结果是正确的。

    【讨论】:

      【解决方案2】:

      让我们一个接一个地看:

      LocalDate monday = startTime // startTime is 2020-05-27 00:00:00 as an Instant.
      
          // 2020-05-27 02:00:00, but with GMT+2 information as it is a ZonedDateTime now.
          .atZone(ZoneId.systemDefault())
      
          // (1) 2020-05-27 02:00:00 without any zone information.
          // (2) 2020-05-27 00:00:00 because hour is dropped as it's a LocalDate here.
          .toLocalDate()
      
          // Changed to 2020-05-25 00:00:00, which is what you want.
          .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
      
      startTime = monday // monday is 2020-05-25 00:00:00 as LocalDate.
      
          // You say that this is at GMT+2 and you want the start of day.
          // You get a ZonedDateTime 2020-05-25 00:00:00 with information about GMT+2.
          .atStartOfDay(ZoneId.systemDefault())
      
          // Instant has no zone offset, so this is what the ZonedDateTime needs to consider.
          // 2020-05-25 00:00:00 becomes 2020-05-24 22:00:00 as the +2 offset is subtracted.
          .toInstant();
      

      当您使用ZoneId.of("UTC") 时,不会出现任何问题,因为ZoneId.of("UTC") 的偏移量为零(为简单起见,忽略夏令时的内容)。日期时间减去零小时是相同的日期时间。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-15
        • 2019-07-22
        • 1970-01-01
        • 2016-11-26
        • 2018-01-21
        • 2013-10-12
        • 2013-03-04
        相关资源
        最近更新 更多