如何在 java8 中获取默认 ZoneOffset?

How to get default ZoneOffset in java8?

使用java8我们知道使用ZoneId.default()可以获得系统默认ZoneId,但是如何获得默认ZoneOffset

我看到 ZoneId 有一些 "rules" 并且每个规则都有一个 ZoneOffset,这是否意味着 ZoneId 可能有多个 ZoneOffset?

tl;博士

OffsetDateTime.now().getOffset()

但您可能应该使用时区,而不仅仅是与 UTC 的偏移量。

ZoneId.systemDefault() 

偏移量与时区

一个offset-from-UTC is merely a number of hour, minutes, and seconds — nothing more. For example, -08:00 means eight hours behind the UTC, and +05:45 means five hours and forty-five minutes ahead of UTC.

一个 time zone is a history of past, present, and future changes to the offset used by the people of a particular region. Anomalies such as Daylight Saving Time (DST) 在特定时间段内引起偏移量的变化会随着时间的推移进行跟踪,在过去发生时,以及在政客宣布计划变更时的未来。

所以最好使用已知的区域

任何区域的偏移量都会随时间变化。例如,DST in the United States 将大约半年的偏移量偏移一个小时,然后在另一半年将该小时恢复到偏移量。时区的全部目的是记录这些偏移量的变化。

所以在没有日期时间的情况下要求偏移量确实毫无意义。例如,在 America/Los_Angeles 中,今年的部分时间偏移量为 -08:00,但在一年中的另一部分时间,它在 DST 期间为 -07:00

OffsetDateTime

所以让我们将时刻指定为 OffsetDateTime, and then extract the ZoneOffset

OffsetDateTime odt = OffsetDateTime.now ();
ZoneOffset zoneOffset = odt.getOffset ();

odt.toString(): 2017-01-02T15:19:47.162-08:00

zoneOffset.toString(): -08:00

now 方法实际上隐式应用了 JVM 当前的默认时区。我建议您始终通过指定 desired/expected 时区来明确说明。即使您想要当前的默认区域,也要明确说明以明确您的意图。消除关于您是否打算使用默认时区或未能考虑时区的歧义,因为程序员经常发生这种情况。调用 ZoneId.systemDefault.

OffsetDateTime odt = OffsetDateTime.now ( ZoneId.systemDefault () );
ZoneOffset zoneOffset = odt.getOffset ();

ZoneId.systemDefault().toString(): America/Los_Angeles

odt: 2017-01-02T15:19:47.162-08:00

zoneOffsetOfOdt: -08:00

关于依赖于默认区域的注意事项:这个默认值可以随时被 JVM 内任何线程中的任何代码更改。如果重要,请询问用户他们想要的时区。

您可以向偏移量询问其总秒数的时间量。

int offsetSeconds = zoneOffset.getTotalSeconds ();

offsetSeconds: -28800

ZonedDateTime

另一个例子:也许您想知道今年圣诞节在魁北克的偏移量是多少。指定时区America/Montreal,得到一个ZonedDateTime,要求它的偏移量作为一个ZoneOffset对象。

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate ld = LocalDate.of( 2017 , 12 , 25 );
ZonedDateTime zdtXmas = ld.atStartOfDay( z );
ZoneOffset zoneOffsetXmas = zdtXmas.getOffset();

zdtXmas.toString(): 2017-12-25T00:00-05:00[America/Montreal]

zoneOffsetXmas.toString(): -05:00

zoneOffsetXmas.getTotalSeconds(): -18000

ZoneId

正如 yanys 在评论中所建议的那样,您可以查询 ZoneId for a particular ZoneOffset by passing a moment as an Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds(最多九 (9) 位小数)。

这只是通往同一目的地的另一条路线。就像上面讨论的 OffsetDateTimeZonedDateTime 一样,我们指定 (a) 时区和 (b) 时刻。

Instant instant = zdtXmas.toInstant();
ZoneOffset zo = z.getRules().getOffset( instant );

For ZoneId: America/Montreal at instant: 2017-12-25T05:00:00Z the ZoneOffset is: -05:00

查看所有这些示例' code live at IdeOne.com

ZoneOffset.systemDefault – 错误或功能?

ZoneOffset class,ZoneId 的子class,被记录为继承了 systemDefault 方法。然而,这实际上不起作用。

ZoneOffset zoneOffset = ZoneOffset.systemDefault() ;  // Fails to compile.

error: incompatible types: ZoneId cannot be converted to ZoneOffset

不确定这种编译失败是错误还是功能。正如上面所讨论的,对我来说,要求带有日期时间的默认偏移量似乎没有意义,所以也许 ZoneOffset.systemDefault 确实应该失败。但是文档应该这么说,并有解释。

我试图针对文档未能解决此问题提交错误,但放弃了,无法确定在哪里以及如何提交此类错误报告。

太阳时与政治时

关于偏移量和时区的更多信息……

Solar Time has been used since pre-history, tracking each day by noting when the sun is directly overhead. Poke a stick in the ground, and watch its shadow. When the shadow is shortest, when the shadow begins to grow rather than shrink, then you know it is now noon. Formalize that with a sundial 跟踪时间流逝。

按照太阳时,当您从一个城镇向西移动到另一个城镇时ward,中午会晚一点。向东ward,中午来得有点早。所以每个城镇都有自己的中午,只与同经度的南北城镇共享。

太阳时在现代基本上被废弃了。随着火车、电报和电话的出现,临时协调的需求也随之而来。因此,选择了一个接近太阳时间的中午点,并宣布向西和向东数英里的大片土地在时钟上共享相同的 12:00,即 他们更大的地区在 Greenwich Prime Meridian line. So began the tradition of every train stop displaying prominently a clock to let the town know of the standard time 之前或之后偏移相同的小时数,而不是他们自己城镇的太阳时。通常,该时区西部边缘的城镇会看到他们的火车站时钟 12:00 在太阳升起之前 稍微 。该地区东部边缘城镇的时钟在太阳升起后12:00稍微

世界各地的政客都表现出改变其管辖权偏移量的倾向。原因各不相同,诸如外交,war和占领,以及Daylight Saving Time (DST). The reasons vary, but their changes come with surprising frequency. A time zone is a name given to a region to track its history of such changes. So an offset-from-UTC的愚蠢只是在本初子午线之前或之后的几个小时-分钟-秒时区远不止于此:历史 特定区域偏移量的过去、现在和未来的变化。虽然两个相邻地区今天可能共享相同的 UTC 偏移量,但在过去或未来,它们可能会有所不同,具体取决于政治家的不同想法或逻辑。

这意味着政治家定义的现代时间跟踪与地理关系不大。例如,今天印度这个幅员辽阔的国家只有一个时区(与 UTC 的偏移量为 +05:30)。因此,在广阔的次大陆的不同地方,太阳正午(太阳直射你的头顶)相隔数小时。印度政客决定这样做是为了帮助统一他们多元化的民主制度。在世界各地的其他例子中,我们看到地区使用他们的时区作为国际关系的象征,例如与他们的邻国不同,或者选择与邻国相同的时区作为最近在朝鲜看到的关系解冻变化以匹配韩国。所以,现在,太阳时只是时间跟踪中的几个考虑因素之一。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

您可以直接与您的数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes。 Hibernate 5 和 JPA 2.2 支持 java.time.

在哪里获取 java.time classes?

根据您的 objective,您或许可以完全绕过 ZoneOffset

假设您只需要 ZoneOffset 例如LocalDateTime.ofEpochSecond(),你可以替换

ZoneOffset offset = OffsetDateTime.now().getOffset();
LocalDateTime dt1 = LocalDateTime.ofEpochSecond(seconds, 0, offset);

LocalDateTime dt2 = LocalDateTime.ofInstant(
    Instant.ofEpochSecond(seconds), 
    ZoneId.systemDefault());

其中 dt1.equals(dt2)true