【问题标题】:One hour difference in DST夏令时差一小时
【发布时间】:2021-04-12 00:16:26
【问题描述】:

我正在尝试找出美国的夏令时开始时间,即 2021 年 3 月 14 日凌晨 2:00,但我的代码返回 2021 年 3 月 14 日凌晨 3:00。我无法理解这一小时的时差。我的代码如下:

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.zone.ZoneOffsetTransition;
import java.time.zone.ZoneRules; 
class DST{
public static void main(String[] args) {       
     ZoneId zoneId = ZoneId.of("America/New_York");
     ZoneRules rules = zoneId.getRules();
     ZoneOffsetTransition nextTransition = rules.nextTransition(Instant.now());
     Timestamp ts = Timestamp.valueOf(nextTransition.getInstant().atZone(zoneId).toLocalDateTime());
     SimpleDateFormat sdf = new SimpleDateFormat("MMMM d, yyyy 'at' h:mm a");
     String date = sdf.format(ts);
     system.out.println("DST time:"+ date );
    }
}

日期值是March 14, 2021 at 3:00 AM,但它应该是March 14, 2021 at 2:00 AM。一小时的时差我看不懂?

【问题讨论】:

  • 不确定,但您是否尝试过打印getInstant().atZone(zoneId) 返回的ZonedDateTime 而不是将其转换为LocalDateTime
  • @Hulk:它返回2021-03-14T03:00-04:00[America/New_York]
  • 为什么要将旧的日期和时间 API 与 java.time 包中的新 API 混合使用?
  • 但是当转换发生在凌晨 2 点时,这意味着时钟向前拨到了 3 点,所以在那个时间点 IS 是凌晨 3 点。你得到了正确的结果。

标签: java time dst


【解决方案1】:

我认为(不必要的)转换为时间戳是错误的原因:

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.zone.ZoneOffsetTransition;
import java.time.zone.ZoneRules;

public class DST {
    public static void main(String[] args) {
        ZoneId zoneId = ZoneId.of("America/New_York");
        ZoneRules rules = zoneId.getRules();
        ZoneOffsetTransition nextTransition = rules.nextTransition(Instant.now());
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern("MMMM d, yyyy 'at' h:mm a");
        System.out.println("DST time:" + nextTransition.getDateTimeBefore().format(sdf));
    }
}

给你(如预期): March 14, 2021 at 2:00 AM

此时时间已经发生了变化,所以确实是凌晨 3 点。这就是 API 设计者必须创建方法 getDateTimeBefore() 的原因。

【讨论】:

  • 谢谢@Benoit,问题是我使用的是nextTransition.getInstant().atZone(zoneId).toLocalDateTime(),但您使用的是nextTransition.getDateTimeBefore(),不确定两者的区别?你有什么想法吗?
  • nextTransition.getDateTimeBefore() 给出的是LocalDateTime,而不是ZondeDateTime
  • @SumitSood 问题来自TimeStampSimpleDateFormat 类的使用,而不是来自nextTransition.getInstant()
  • @SumitSood 好的,我明白了。嗯,这个时间点,时间已经发生了变化,所以确实是凌晨三点。这就是 API 设计人员必须创建此方法 getDateTimeBefore() 的原因。
  • 这肯定是getDateTimeBefore() 造成了影响。如果您想要过渡前的时间,那么您应该不会对这样命名的方法将其提供给您感到非常惊讶。
【解决方案2】:

https://www.timeanddate.com/time/change/usa/new-york查看以下信息

14 Mar 2021 - Daylight Saving Time Starts
When local standard time is about to reach
Sunday, 14 March 2021, 02:00:00 clocks are turned forward 1 hour to
Sunday, 14 March 2021, 03:00:00 local daylight time instead.

信息与以下代码的输出相匹配:

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.zone.ZoneOffsetTransition;
import java.time.zone.ZoneRules;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        ZoneId zoneId = ZoneId.of("America/New_York");

        ZoneRules rules = zoneId.getRules();
        ZonedDateTime zdt = ZonedDateTime.now(zoneId);
        ZoneOffsetTransition nextTransition = rules.nextTransition(zdt.toInstant());

        ZonedDateTime zdtAfterTransition = nextTransition.getInstant().atZone(zoneId);
        ZonedDateTime zdtBeforeTransition = nextTransition.getInstant().minus(1, ChronoUnit.DAYS).atZone(zoneId);
        System.out.println(zdtBeforeTransition);
        System.out.println(zdtAfterTransition);

        LocalDateTime ldtBeforeTransition = nextTransition.getDateTimeBefore();
        System.out.println(ldtBeforeTransition);

        // Convert LocalDateTime to java.sql.Timestamp
        Timestamp ts = new Timestamp(Date.from(ldtBeforeTransition.toInstant(ZoneOffset.UTC)).getTime());
        SimpleDateFormat sdf = new SimpleDateFormat("MMMM d, yyyy 'at' h:mm a");
        String date = sdf.format(ts);
        System.out.println("DST time:" + date);
    }
}

输出:

2021-03-13T02:00-05:00[America/New_York]
2021-03-14T03:00-04:00[America/New_York]
2021-03-14T02:00
DST time:March 14, 2021 at 2:00 am

java.util 的日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。 java.sql.Timestamp 类扩展了java.util.Date,因此它继承了相同的问题。建议完全停止使用,转用modern date-time API

Trail: Date Time 了解现代日期时间 API。

【讨论】:

    猜你喜欢
    • 2018-10-01
    • 2013-12-31
    • 2017-07-11
    • 2019-08-09
    • 2015-03-10
    • 2019-01-03
    • 2012-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多