Java API 获得一年的夏令时边界

Posted

技术标签:

【中文标题】Java API 获得一年的夏令时边界【英文标题】:Java API to get daylight saving boundaries for a year 【发布时间】:2017-11-19 01:05:43 【问题描述】:

我有一个日期范围(开始日期和结束日期),并且需要知道这是否属于夏令时更改范围内。

是否有任何 Java API 可用于检查这一点或任何 Java 代码来实现这一点?

【问题讨论】:

请参考:***.com/questions/2532729/… 类似但不重复:Determine Whether Daylight Savings Time (DST) is Active in Java for a Specified Date 【参考方案1】:

每个国家/地区的夏令时更改发生在不同的日期,因此首先要知道您要检查的时区的名称。

我正在使用 Joda-Time 和新的 Java 日期/时间 API 编写此答案,并且都使用 IANA's list of timezone names(格式为 Continent/City)。这两个 API 也避免使用 3-letter names,因为它们是 ambiguous and not standard。

对于下面的代码,我将使用America/Sao_Paulo(我居住的时区,每年都有 DST 变化),但您可以将其替换为您想要的时区。

下面的代码向您展示了如何检查日期是否在 DST 中并找到下一个 DST 更改发生的日期。因此,如果您有开始日期和结束日期并且想知道两者是否都在 DST 更改中,您可以检查两者是否都在 DST 中,还可以找到下一个和上一个 DST 更改(并检查日期是否介于这些更改 - 我不清楚您应该如何进行检查)。


另外请注意,Joda-Time 处于维护模式,正在被新的 API 取代,因此我不建议使用它来启动新项目。即使在joda's website 中,它也说:“请注意,Joda-Time 被认为是一个基本上“完成”的项目。没有计划进行重大改进。如果使用 Java SE 8,请迁移到 java.time (JSR-310 )。”


乔达时间

您可以使用org.joda.time.DateTimeZone 类。要了解所有可用时区,请致电DateTimeZone.getAvailableIDs()

下面的代码检查日期是否在 DST 中,并找到下一个 DST 更改发生的日期:

// create timezone object
DateTimeZone zone = DateTimeZone.forID("America/Sao_Paulo");

// check if a date is in DST
DateTime inDst = new DateTime(2017, 1, 1, 10, 0, zone);
// isStandardOffset returns false (it's in DST)
System.out.println(zone.isStandardOffset(inDst.getMillis()));
// check when it'll be the next DST change
DateTime nextDstChange = new DateTime(zone.nextTransition(inDst.getMillis()), zone);
System.out.println(nextDstChange); // 2017-02-18T23:00:00.000-03:00

// check if a date is in DST
DateTime noDst = new DateTime(2017, 6, 18, 10, 0, zone);
// isStandardOffset returns true (it's not in DST)
System.out.println(zone.isStandardOffset(noDst.getMillis()));
// check when it'll be the next DST change
nextDstChange = new DateTime(zone.nextTransition(noDst.getMillis()), zone);
System.out.println(nextDstChange); // 2017-10-15T01:00:00.000-02:00

如果您想查找上一个 DST 更改(而不是下一个),请调用 previousTransition() 而不是 nextTransition()


Java 新的日期/时间 API

如果您使用的是 Java 8,new java.time API 已经自带了。

如果您使用的是 Java ,则可以使用 ThreeTen Backport,这是 Java 8 新日期/时间类的一个很好的向后移植。对于Android,还有ThreeTenABP(更多关于如何使用它here)。

下面的代码适用于两者。 唯一的区别是包名(在 Java 8 中是 java.time,在 ThreeTen Backport(或 android 的 ThreeTenABP)中是 org.threeten.bp),但类和方法 names 是相同的。

代码与 Joda-Time 的版本非常相似。主要区别:

虽然 Joda-Time 有 isStandardOffset() 来检查日期是否在 DST 中不是,但新 API 有 isDaylightSavings() 来检查日期是否在 DST 中。 Joda-Time 直接在 DateTimeZone 类中提供方法,但新 API 有一个专门用于其 DST 规则的类 (java.time.zone.ZoneRules) 下一个和上一个转换的方法返回 java.time.zone.ZoneOffsetTransition 而不是直接返回日期(此对象提供有关 DST 更改的更多信息,如下所示)。

尽管存在这些差异,但想法非常相似:

// create timezone object
ZoneId zone = ZoneId.of("America/Sao_Paulo");
// get the timezone's rules
ZoneRules rules = zone.getRules();

// check if a date is in DST
ZonedDateTime inDST = ZonedDateTime.of(2017, 1, 1, 10, 0, 0, 0, zone);
// isDaylightSavings returns true (it's in DST)
System.out.println(rules.isDaylightSavings(inDST.toInstant()));
// check when it'll be the next DST change
ZoneOffsetTransition nextTransition = rules.nextTransition(inDST.toInstant());
// getInstant() returns the UTC instant; atZone converts to the specified timezone
System.out.println(nextTransition.getInstant().atZone(zone)); // 2017-02-18T23:00-03:00[America/Sao_Paulo]

// you can also check the date/time and offset before and after the DST change
// in this case, at 19/02/2017, the clock is moved 1 hour back (from midnight to 11 PM)
ZonedDateTime beforeDST = ZonedDateTime.of(nextTransition.getDateTimeBefore(), nextTransition.getOffsetBefore());
System.out.println(beforeDST); // 2017-02-19T00:00-02:00
ZonedDateTime afterDST = ZonedDateTime.of(nextTransition.getDateTimeAfter(), nextTransition.getOffsetAfter());
System.out.println(afterDST); // 2017-02-18T23:00-03:00

// check if a date is in DST
ZonedDateTime noDST = ZonedDateTime.of(2017, 6, 1, 10, 0, 0, 0, zone);
// isDaylightSavings returns false (it's not in DST)
System.out.println(rules.isDaylightSavings(noDST.toInstant()));
// check when it'll be the next DST change
nextTransition = rules.nextTransition(noDST.toInstant());
// getInstant() returns the UTC instant; atZone converts to the specified timezone
System.out.println(nextTransition.getInstant().atZone(zone)); // 2017-10-15T01:00-02:00[America/Sao_Paulo]

// you can also check the date/time and offset before and after the DST change
// in this case, at 15/10/2017, the clock is moved 1 hour forward (from midnight to 1 AM)
beforeDST = ZonedDateTime.of(nextTransition.getDateTimeBefore(), nextTransition.getOffsetBefore());
System.out.println(beforeDST); // 2017-10-15T00:00-03:00
afterDST = ZonedDateTime.of(nextTransition.getDateTimeAfter(), nextTransition.getOffsetAfter());
System.out.println(afterDST); // 2017-10-15T01:00-02:00

如果您想查找上一个 DST 更改而不是下一个,可以调用 rules.previousTransition() 而不是 rules.nextTransition()

【讨论】:

非常感谢。它帮助了我。【参考方案2】:

当然有。还有不止一个。要使用的标准 API 是 java.time

很明显,您首先需要确定您想要的时区。

您标记了您的问题 gmt,这很简单:GMT 没有夏令时(夏令时),因此您的范围内永远不会发生转换。如果这是您的意思,您无需再阅读。

夏令时转换日期在北美和欧盟并不相同,而在南半球则完全不同。此外,许多时区根本不适用 DST。因此,从ZoneId.of() 获取您想要的时区,以continent/city 的形式提供一个字符串,例如Europe/Stockholm。它接受许多城市,我认为每个时区至少有一个,每个国家/地区至少有一个。使用ZoneId.getRules() 获取ZoneRules 对象。请检查the documentation 了解您可以使用此对象执行的所有操作。我想我会尝试 nextTransistion() 传递你的开始日期。如果我收到null 回复,则范围内不能进行转换(可能该区域不应用 DST)。如果我收到 ZoneOffsetTransition 回复,请使用其 getInstant() 并检查 Instant 是否在您的结束日期之前。

java.time 在 JSR-310 中进行了描述。它内置于 Java 8 及更高版本中。如果您尚未使用 Java 8,请使用 ThreeTen Backport。

您标记了您的问题 jodatime,是的,Joda-Time 也应该是一个选项。

请注意,Joda-Time 被认为是一个基本上“已完成”的项目。 没有计划进行重大改进。如果使用 Java SE 8,请迁移 到java.time (JSR-310)。

引用自the Joda-Time homepage。

【讨论】:

以上是关于Java API 获得一年的夏令时边界的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 的 ZoneOffset 是不是考虑了夏令时?

WordPress 从哪里获得夏令时信息

将UTC时间转换为PST以获得夏令时?

有没有啥简单的方法可以在 C/C++ 中获得 Linux 下的夏令时转换时间

Google 日历 API 问题 - 夏令时

中国有夏令时和冬令时吗?