确定日期夏令时的算法?

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

因此,例如在10月25日和31日之间,我们必须检查,如果实际日期是星期日,则是在2点之前或之后……

没有真正的算法来处理夏令时。 基本上每个国家都可以在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())); } } 

Richters的答案是正确的,应该被接受。

Richters指出, 夏令时(DST)或其他exception没有逻辑。 政客们任意重新定义他们所在时区 的UTC偏移量 。 他们经常几乎没有预警就做出这些改变,甚至几周前朝鲜没有发出任何警告。

java.time

下面是一些进一步的想法,以及使用现代java.time类的示例代码, 这些类inheritance了他的答案中显示的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(); 

对于区域:America / Montreal,2018-11-04T02:00持续时间更改:PT-1H到2018-11-04T01:00

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


关于java.time

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

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

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

从哪里获取java.time类?

  • Java SE 8Java SE 9Java SE 10及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26), ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。