Java 为啥 Calendar.get(Calendar.DST_OFFSET) 在夏令时给出 0?

Posted

技术标签:

【中文标题】Java 为啥 Calendar.get(Calendar.DST_OFFSET) 在夏令时给出 0?【英文标题】:Java why does Calendar.get(Calendar.DST_OFFSET) give 0 during daylight savings time?Java 为什么 Calendar.get(Calendar.DST_OFFSET) 在夏令时给出 0? 【发布时间】:2021-09-16 22:46:46 【问题描述】:

我有一个日历对象,对应于 2021-07-05T18:00:00.000-04:00(东部夏令时间)。然而 Calendar.get(DST_OFFSET) 和 Calendar.getTimeZone().getDSTSavings() 都给出 0。它应该是 1 小时。我错过了什么或我出了什么问题?我使用的所有其他方法都返回预期值。

我正在使用 setTimeInMillis() 和 TimeZone 使用偏移量创建日历。这是它不起作用的原因吗?显示的民间时代总是正确的......

尽管我想使用新的 Java 时间,但我正在使用 Java for android。最后我只检查了最新版本的 Android 是否支持新的 Java 时间。他们最终可能会增加对旧版本的支持。

【问题讨论】:

那个老旧过时的API是原因,使用java.time... 确定您的 TZ 是 EDT 吗? technojeeves.com/index.php/aliasjava1/16-javasystemproperties 正如 deHaar 所说,不要使用Calendar,它已经过时了。 java.time 包中提供的较新的 Java 日期和时间 API 可以正确执行这些操作。 您是否检查过coreLibraryDesugaring 是否会让您回到那些版本? 真正的问题是什么,如果有的话? EDT 不是时区,您的 Android 设备肯定不能将其时区设置为 EDT? 【参考方案1】:

一个问题是输入定义了与 UTC 的偏移量,但不是具有特定规则的实时时区(例如 如果完全应用 DST,如果是,DST 何时生效应用)。

Calendar 显然无法处理这些规则,该类(可能还有整个 API)并不是设计的。

这就是在 Java 8 中引入 java.time 的原因之一。

这是在像您这样的情况下使用 java.time 的一些示例:

public static void main(String[] args) 
    // example String in ISO format
    String dateString = "2021-07-05T18:00:00.000-04:00";
    // define your time zone
    ZoneId americaNewYork = ZoneId.of("America/New_York");
    // parse the (zone-less) String and add the time zone
    ZonedDateTime odt = OffsetDateTime.parse(dateString)
                                      .atZoneSameInstant(americaNewYork);
    // then get the rules of that zone
    long hours = americaNewYork.getRules()
                               // then get the daylight savings of the datetime
                               .getDaylightSavings(odt.toInstant())
                               // and get the full hours of the dst offset
                               .toHoursPart();
    
    // use a formatter to format the output (nearly) as desired
    System.out.println(odt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
                        + " has a daylight saving offset of "
                        + hours);

打印出来

2021-07-05T18:00:00-04:00[America/New_York] has a daylight saving offset of 1

编辑:

您的评论让我提供了一个使用long 作为输入的类似版本:

public static void main(String[] args) 
    // example String in ISO format
    long input = 1625522400000L;
    // create an Instant from the input
    Instant instant = Instant.ofEpochMilli(input);
    // define your time zone
    ZoneId americaNewYork = ZoneId.of("America/New_York");
    // then get the rules of that zone
    long hours = americaNewYork.getRules()
                               // then get the daylight savings of the Instant
                               .getDaylightSavings(instant)
                               // and get the full hours of the dst offset
                               .toHoursPart();
    
    // use a formatter to format the output (nearly) as desired
    System.out.println(ZonedDateTime.ofInstant(instant, americaNewYork)
                                    .format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
                        + " has a daylight saving offset of "
                        + hours);

输出与上例相同。

【讨论】:

我认为这可能是正确的答案。我没有使用从实例创建的日历,它可能知道时区。但我正在清除该实例,并从 unix 纪元创建日历,并从偏移量创建 TimeZone。但是日历不知道时区 - 只是与 UTC 的偏移量。不过我不确定。 @BrianReinhold 如果你的输入是一个纪元毫秒或秒的long,你可以直接从它创建一个Instant。好的,也许 Calendar 可以知道一个时区,但它不能可靠地为您提供正确的偏移量,尤其是在夏令时起作用时。 我认为这是正确的答案,因为它确定了我做错了什么。我没有指定您在上面所做的特定时区,而只是提供了偏移量和纪元。偏移量中包含 DST 班次(给定我的来源)。所以日历当然不知道 DST 班次。例如,我将不得不进一步指定这是纽约时区(就像您在示例中所做的那样)。我没有那样做。【参考方案2】:

在一个 java 错误tracker 中你会发现你的问题。

在“回退”期间,日历不支持消歧,给定的本地时间被解释为标准时间。 为避免 DST 到标准时间的意外更改,请调用 add() 重置该值。

你可以通过替换 set() 来解决它

cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));

【讨论】:

但是 OP 的 2021-07-05 日期离“回退”期还很远,所以这个问题不适用 另外,不推荐使用 3 个字母的 TZ。如果您以java -Duser.timezone=America/New_York ... 运行代码,您可能会发现不同的结果

以上是关于Java 为啥 Calendar.get(Calendar.DST_OFFSET) 在夏令时给出 0?的主要内容,如果未能解决你的问题,请参考以下文章

java.util.Calendar.get* 和 java.util.Calendar.set* 被阻塞

关于JAVA中calendar.get(Calendar.Year)的问题

Java 时间操作

JAVA日期查询:季度月份星期等时间信息

java笔记------日期API

java按照时间查询,获取近1月时间信息。时间如何加减?简单易懂,谢谢了。