【问题标题】:Java DST Change issueJava DST 更改问题
【发布时间】:2017-07-09 20:46:40
【问题描述】:

我发现了一个关于 Java DST 更改的问题。 当 DST(夏令时)发生的确切时刻,该特定时间可能会出现两次。

例如,对于时区 America/Sao_Paulo,它从 2016 年 2 月 21 日星期日 00:00:00 向后更改一小时到 2016 年 2 月 20 日星期六 23:00:00。 当天 23:00 时间出现两次。

在我的用例中,我想进行第二次。例如,当我想要这一天的数据时,我实际上是指完整的这一天(即使它可能包含 25 小时)。

但在我看来,Java 似乎占据了这一刻的第一次。

例子:

@Test
public void testDSTChange() throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

    TimeZone tz = TimeZone.getTimeZone("America/Sao_Paulo");

    long dateInput = sdf.parse("2016-02-20T23:00:00").getTime();
    long dateUTC = dateInput - tz.getOffset(dateInput);

    long dateInput2 = sdf.parse("2016-02-21T02:00:00").getTime();
    long dateUTC2 = dateInput2 - tz.getOffset(dateInput2);

    System.out.println("Difference: " + (dateUTC2 - dateUTC) / 3600 / 1000);

}

在这个例子中,它显示这两个日期之间的差异是 4 小时,而不是我预期的 3 小时。所以我想知道是否还有其他方法可以做到这一点?

【问题讨论】:

  • 我不太清楚你的问题。您希望当天的时间不受小时变化的影响吗?
  • 不,我想要 DST 更改后的时间。在我的示例中, 2016-02-20T23:00:00 实际上当天发生了两次,我想采取第二次,即 DST 更改后的一次。
  • 你能用 Joda Time 或 Java 8 代替 java.util.Datejava.util.Calendar 吗?
  • Java 8 发生了什么变化?

标签: java timezone


【解决方案1】:

好吧,当 DST 向后更改时,DST 更改的时间会发生两次,没有办法绕过它。可能还有另一种方法,但让我提出一个建议,内部使用 UTC 时间 (GMT),它内部没有 DST,仅出于显示目的在输出上转换为本地时间(挂钟时间)。你再也不用担心它了。

【讨论】:

  • 经常给出“使用 UTC 处理一切”的建议,但过于笼统。这对一些 的东西很好,但对其他的则不然。例如,如果您想要从当地时间下午 3 点开始的每周会议,您真的不想将其存储为“当地时间下午 3 点转换为 UTC” - 因为那样您会丢失 真实 信息。所以不,采用这种方法当然并不意味着“你再也不用担心它了”。
【解决方案2】:

在我的用例中,我想进行第二次。例如,当我想要这一天的数据时,我实际上是指完整的这一天(即使它可能包含 25 小时)。

从这句话中,我假设您实际上是在进行某种范围查询来收集一整天的数据。解决此问题的最佳方法是从一天的开始包含到下一天的开始不包含进行查询。换句话说:startOfDay <= dataPoint(s) < startOfNextDay

这在您的场景中效果很好,因为直到第二天开始,时钟才真正达到00:00。它像这样滴答作响:

23:58
23:59
23:00
23:01
...
23:58
23:59
00:00  <--- start of next day

所以使用半开间隔,它应该会奏效。

在春季过渡时要小心,因为一天的开始是01:00,而不是00:00。 :)

此外,Jon 关于使用 Java 8 的新时间 API 或 Joda-Time 的评论也很到位。它们都可以让您更好地控制这种情况。较旧的 Java API 没有。

【讨论】:

    【解决方案3】:
    LocalDateTime l1 = LocalDateTime.of(2016, Month.FEBRUARY, 20, 23, 0, 0);
    ZoneId id = ZoneId.of("America/Sao_Paulo");  
    ZoneOffset offset = ZoneOffset.of("-08:00");
    ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(l1, offset, id);
    long el1= zonedDateTime.toEpochSecond();
    
    
    
    LocalDateTime l2 = LocalDateTime.of(2016, Month.FEBRUARY, 21, 2, 0, 0);
    ZonedDateTime zonedDateTime1 = ZonedDateTime.ofInstant(l2, offset, id);;
    long el2= zonedDateTime1.toEpochSecond();
    
    
    System.out.println("Difference: " + (el2-el1 ) / 3600 );
    
        ====================================
        on console it prints 
        Difference: 3
    

    【讨论】:

    • 最好包含对代码的简要说明以及它解决问题的原因,而不是只发布源代码。
    猜你喜欢
    • 2015-01-11
    • 1970-01-01
    • 2017-04-02
    • 2011-08-23
    • 1970-01-01
    • 1970-01-01
    • 2019-04-06
    • 2014-01-17
    • 2015-06-07
    相关资源
    最近更新 更多