确定日期夏令时的算法?

Posted

技术标签:

【中文标题】确定日期夏令时的算法?【英文标题】:Algorithm to determine Daylight Saving Time of a date? 【发布时间】:2010-09-09 09:58:44 【问题描述】:

最初我在 Actionscript 中寻找解决方案。这个问题的重点是算法,当时钟必须切换夏令时时,它会检测精确的分钟。

因此,例如在 10 月 25 日到 31 日之间,我们必须检查,如果实际日期是星期天,是在 2 点之前还是之后...

【问题讨论】:

【参考方案1】:

没有真正的算法来处理夏令时。基本上每个国家都可以自行决定 DST 何时开始和结束。作为开发人员,我们唯一能做的就是使用某种表格来查找它。大多数计算机语言都在语言中集成了这样的表格。

在 Java 中,您可以使用 TimeZone 类的 inDaylightTime 方法。如果您想知道某年 DST 开始或结束的确切日期和时间,我建议使用Joda Time。我看不到仅使用标准库来找出这一点的干净方法。

以下程序是一个示例:(请注意,如果某个时区在某年没有 DST,它可能会产生意想不到的结果)

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

public class App 
    public static void main(String[] args) 
        DateTimeZone dtz = DateTimeZone.forID("Europe/Amsterdam");

        System.out.println(startDST(dtz, 2008));
        System.out.println(endDST(dtz, 2008));
    

    public static DateTime startDST(DateTimeZone zone, int year) 
        return new DateTime(zone.nextTransition(new DateTime(year, 1, 1, 0, 0, 0, 0, zone).getMillis()));
    

    public static DateTime endDST(DateTimeZone zone, int year) 
        return new DateTime(zone.previousTransition(new DateTime(year + 1, 1, 1, 0, 0, 0, 0, zone).getMillis()));
    

【讨论】:

所以在 joda 中没有内置方法可以使用“DateTime”和“DateTimeZone”并告诉我日期时间是否为夏令时? @russau (A) 是的,russau,Joda-Time 2.3 中有这样的内置方法。关于另一个问题,请参阅my answer。 (B) 您在这里提出的问题与此原始海报不同。【参考方案2】:

Answer by Richters 是正确的,应该被接受。

正如 Richters 所说,Daylight Saving Time (DST) 或其他异常没有逻辑。政客们武断地重新定义了他们time zones中使用的offset-from-UTC。他们在做出这些更改时通常几乎没有预先警告,甚至像几周前的North Korea did 那样根本没有警告。

java.time

这里有一些进一步的想法,以及使用现代 java.time 类的示例代码,这些类继承了他的答案中显示的 Joda-Time 类。

这些更改在 ICANN 维护的列表中进行跟踪,该列表称为 tzdata,以前称为 Olson 数据库。您的 Java 实现、主机操作系统和数据库系统可能都有自己的数据副本,当您关心的区域模式发生变化时,必须根据需要进行替换。这些更改没有逻辑,因此无法以编程方式预测更改。您的代码必须调用 tzdata 的新副本。

例如在 10 月 25 日到 31 日之间,我们必须检查,如果实际日期是星期天,是在 2 点之前还是之后...

实际上,您无需确定切换点。一个好的日期时间库会自动为您处理。

Java 拥有最好的此类库,业界领先的 java.time 类。当您询问某个地区(时区)某个日期的某个时间时,如果该时间无效,则会自动进行调整。阅读ZonedDateTime 的文档以了解该调整中使用的算法。

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate ld = LocalDate.of( 2018 , Month.MARCH , 11 );  // 2018-03-11.
LocalTime lt = LocalTime.of( 2 , 0 );  // 2 AM.
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z );

请注意,结果是凌晨 3 点,而不是请求的凌晨 2 点。那个区域的那个日期没有凌晨 2 点。因此,java.time 调整为凌晨 3 点,因为时钟“提前”一小时。

zdt.toString(): 2018-03-11T03:00-04:00[美国/蒙特利尔]

如果您觉得需要调查为时区定义的规则,请使用ZoneRules 类。

获取当前使用的 DST 偏移量。

Duration d = z.getRules().getDaylightSavings​( Instant.now() ) ;

获取下一个计划更改,表示为 ZoneOffsetTransition 对象。

ZoneId z = ZoneId.of( "America/Montreal" );
ZoneOffsetTransition t = z.getRules().nextTransition( Instant.now() );
String output = "For zone: " + z + ", on " + t.getDateTimeBefore() + " duration change: " + t.getDuration() + " to " + t.getDateTimeAfter();

对于区域:美国/蒙特利尔,2018-11-04T02:00 持续时间更改:PT-1H 至 2018-11-04T01:00

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

Java SE 8Java SE 9Java SE 10 及更高版本 内置。 标准 Java API 的一部分,带有捆绑实现。 Java 9 添加了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能都在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuarter 和more。

【讨论】:

以上是关于确定日期夏令时的算法?的主要内容,如果未能解决你的问题,请参考以下文章

如何确定给定日期是不是在 .NET 2.0 中给定时区的夏令时?

您如何确定 VBA 中的夏令时?

没有夏令时的日期计算

如何确定夏令时是不是在 C 中处于活动状态?

在 Javascript 中使用夏令时检查日期范围

如何在 Dynamics CRM 中获取时区的夏令时开始和结束?