如何在Java 8中获取默认的ZoneOffset?tl;博士偏移量与时区太阳时与政治时间关于 java.time

2022-08-31 09:16:55

使用Java 8,我们知道使用可以得到系统默认值,但是如何获得默认值?ZoneId.default()ZoneIdZoneOffset

我看到 a 有一些“规则”,每个规则都有一个 ,这是否意味着 a 可能有多个?ZoneIdZoneOffsetZoneIdZoneOffset


答案 1

tl;博士

OffsetDateTime.now().getOffset()

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

ZoneId.systemDefault()

偏移量与时区

与 UTC 的偏移量只是小时、分钟和秒数—仅此而已。例如,表示比 UTC 晚 8 小时,表示比 UTC 早 5 小时 45 分钟。-08:00+05:45

时区是特定地区人民使用的偏移量的过去、现在和未来变化的历史记录。诸如夏令时 (DST) 之类的异常导致特定时间段内偏移量的偏移量变化,会随着时间的推移、过去发生的情况以及将来政治家宣布计划的更改时进行跟踪。

因此,最好在已知时使用区域。

任何区域的偏移量随时间而变化。例如,美国的 DST 在大约半年内将偏移量偏移量移动一小时,然后将该小时恢复到另一半年的偏移量。时区的全部目的是记录这些偏移量的变化。

因此,要求在没有日期时间的情况下进行偏移实际上毫无意义例如,在美国/Los_Angeles,偏移量是今年部分时间,但在一年中的另一部分是在 DST 期间。-08:00-07:00

OffsetDateTime

因此,让我们将一个时刻指定为 OffsetDateTime,然后提取 ZoneOffset

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

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

zoneOffset.toString(): -08:00

该方法实际上隐式应用了 JVM 的当前默认时区。我建议您始终通过指定所需的/预期的时区来明确说明这一点。即使您想要当前的默认区域,也请明确说明以明确您的意图。消除关于您是打算默认还是未能考虑时区的歧义,就像程序员经常发生的那样。调用 ZoneId.systemDefault.now

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

区域OffsetOfOdt: -08:00

关于取决于缺省区域的注意事项:JVM 中任何线程中的任何代码都可以随时更改此缺省值。如果重要,请向用户询问其预期的时区。

您可以以总秒数的形式询问偏移量的时间量。

int offsetSeconds = zoneOffset.getTotalSeconds ();

偏移次: -28800

ZonedDateTime

另一个例子:也许你想知道今年魁北克圣诞节的偏移量是多少。指定时区 ,获取 ZonedDateTime,请求将其偏移量作为对象。America/MontrealZoneOffset

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[美国/蒙特利尔]

zoneOffsetXmas.toString(): -05:00

zoneOffsetXmas.getTotalSeconds(): -18000

Table of date-time types in Java, both modern and legacy.

ZoneId

正如 yanys 在注释中所建议的那样,您可以通过将某个时刻作为 .Instant 类以 UTC 格式表示时间轴上的某个时刻,分辨率为纳秒(最多九 (9) 位小数)。ZoneOffsetInstant

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

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

对于 ZoneId: 美国/蒙特利尔 在即时: 2017-12-25T05:00:00Z 区域偏移为: -05:00

IdeOne.com 实时查看所有这些示例的代码。

ZoneOffset.systemDefault– 错误或功能?

该类是 的子类,记录为继承 systemDefault 方法。但是,这实际上不起作用。ZoneOffsetZoneId

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

错误:不兼容的类型:ZoneId 无法转换为 ZoneOffset

不确定这种编译失败是错误还是功能。如上所述,对我来说,要求使用日期时间进行默认偏移似乎没有意义,因此也许确实应该失败。但是文档应该这样说,并附上解释。ZoneOffset.systemDefault

我试图在文档失败时提交一个错误来解决这个问题,但放弃了,无法确定在哪里以及如何提交这样的错误报告。

太阳时与政治时间

有关偏移量和时区的更多信息...

太阳时自史前以来就被使用,通过记录太阳何时直接在头顶上来跟踪每一天。在地上戳一根棍子,看着它的影子。当阴影最短时,当阴影开始增长而不是缩小时,那么你知道现在是中午。用日晷将其正式化,以跟踪时间流逝。

随着太阳时的到来,当你从一个城镇到另一个城镇向西移动时,中午会晚一点到达。向东移动,中午来得有点早。因此,每个城镇都有自己的中午,仅与同一经度的北部和南部城镇共享。

太阳时在现代在很大程度上被遗弃了。随着火车、电报和电话的到来,临时协调的需要也随之而来。因此,在接近正午的太阳时时时选择了一个点,并且向西和向东数英里的大片土地被宣布共享相同的时钟12:00,格林威治本初子午线之前或后面偏移的小时数相同。因此,开始了每个火车站都突出显示时钟的传统,让城镇知道他们更大地区的标准时间,而不是他们自己城镇的太阳时。通常,该时区西部边缘的城镇会在太阳从头顶升起之前看到他们的火车站时钟读数为12:00。该地区东部边缘城镇的时钟在太阳在头顶一点点读取12:00。

世界各地的政治家都表现出改变其管辖权的抵消的倾向。原因各不相同,例如外交,战争和占领,以及夏令时(DST)的愚蠢。原因各不相同,但它们的变化频率惊人。时区是为区域指定的名称,用于跟踪其此类更改的历史记录。因此,与 UTC 的偏移量只是本初子午线前面或后面的小时-分-秒数。时区远不止于此:特定区域偏移量的过去、现在和将来更改的历史记录。虽然两个邻近地区今天可能共享相同的UTC偏移量,但在过去或将来,它们可能会有所不同,具体取决于其政治家的不同异想天开或逻辑。

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


关于 java.time

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

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

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

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

从哪里获取 java.time 类?

Table of which java.time library to use with which version of Java or Android


答案 2

根据您的目标,您可以完全绕过区域偏移

假设您只需要一个 for 例如,您可以替换ZoneOffsetLocalDateTime.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


推荐