java 的 ZoneOffset 与 ZoneId

关于时区常见的问题:如何在java8及更高版本中获取默认的ZoneOffset?

tl;dr

OffsetDateTime.now().getOffset()

但是,建议使用时区(ZoneId) 而不是UTC的偏移量 (ZoneOffset)。

偏移量 VS 时区

一个UTC偏移量 offset-from-UTC 仅仅只记录了时分秒而已,除此之外没有任何其他信息。举个例子 ,+08:00的意思时超前于UTC八个小时,而 -05:45 意思是落后于UTC五小时四十五分钟。

而时区对于特定地区的人来说是过去,现在,未来的偏移量的历史集合。像夏令时 这样的异常会导致特定时间段内的偏移量会随时间变化,无论是过去已经发生的还是 未来政客们宣布计划的改变。

所以,当你了解时最好还是使用时区。

很多地区的偏移量都会随时间变化。比如,美国的夏令时 会导致约半年左右的时间内都会再原来的基础上偏移一个小时,然后再下半年再调整回这一个小时的偏移量。时区的意义就是记录这些所有的会造成偏移的情况。

因此,没有结合日期计算的偏移量没有意义的。举个例子,法国时间 (Europe/Paris),在一年的一小部分时间内偏移量为+01:00,而由于夏令时的缘故,大部分时间(三月末到十月末)的偏移量都是+02:00

// 指定区域,构造时间: 法国巴黎时间的当前时间,五月有DST,偏移量+2
LocalDateTime now = LocalDateTime.of(LocalDate.of(2020, 5, 31), LocalTime.now())
ZonedDateTime zonedDateTime = ZonedDateTime.of(now, ZoneId.of("Europe/Paris"));
zonedDateTime.getOffset(); // +02:00

// 手动调整的二月份,此时没有DST,偏移量是+1
ZonedDateTime thirdMonth = zonedDateTime.minusMonths(3);
thirdMonth.getOffset(); // +01:00

OffsetDateTime

让我们来定义一个OffsetDateTime,然后获取其中的 ZoneOffset.

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

odt.toString(); // 2020-05-31T23:59:24.753+08:00
zoneOffset.toString(); // +08:00

now方法其实隐含应用了jvm当前的默认时区。我的建议是 应该永远显式的指定你需要的时区,即便你就是要获取当前的默认时区。这么做的目的就是明确代码的意图,从而消除模棱两可的不确定性,比如代码到底是要获取默认时区还是写代码的人忘记考虑了时区的因素,这种情况在代码里是经常发生的。默认时区可以通过调用 ZoneId.systemDefault获取。

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

ZoneId.systemDefault().toString(); // Asia/Shanghai
odt.toString(); // 2020-05-31T23:59:24.753+08:00
zoneOffset.toString(); // +08:00

注意,如果代码依赖了jvm的默认时区则需要小心,因为jvm中的任何一个线程都可以随时更改时区。如果时区对于代码十分重要,建议保存用户的时区到Session或者持久化存储,以便代码行为的统一性。

同时,你也可以通过偏移量获取以秒为单位的偏移秒数。

int offsetSeconds = zoneOffset.getTotalSeconds();
offsetSeconds; // 28800

ZonedDateTime

另外一个例子:也许你想知道今年圣诞节的时候的Québec(魁北克)偏移量是多少。定义一个America/Montreal的时区,获取ZonedDateTime,然后通过ZonedDateTime获取对应的UTC偏移对象ZoneOffset.

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

zdtXmas.toString(); // 2020-12-25T00:00-05:00[America/Montreal]
zoneOffsetXmas.toString(); // -05:00
zoneOffsetXmas.getTotalSeconds(); // -18000

ZoneId

正如yanys在评论中所提到的,你可以通过给ZoneId传递一个时刻Instant来获取对应的偏移量。 Instant 类代表UTC的时间轴上的一个时刻,可以精确到纳秒级别(nanoseconds,可以达到小数点后九位)。

这是获取偏移量的另外一种方式。就像在OffsetDateTimeZonedDateTime中讨论的一样,我们可以定义一个时区,然后通过一个时刻来获取偏移量。

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

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

点击查看完整的演示: code live at IdeOne.com.

ZoneOffset.systemDefault – Bug还是特性?

ZoneOffset 类, 是 ZoneId的一个子类, 通过继承,拥有父类的 systemDefault 方法。然而,实际上它并不能按照预期工作.

 // 编译失败
ZoneOffset zoneOffset = ZoneOffset.systemDefault();

error: incompatible types: ZoneId cannot be converted to ZoneOffset

不确定这个编译失败是bug还是特性。如同上文所提到的,脱离日期时间获取默认的偏移量没有意义。所以也许 ZoneOffset.systemDefault 确实需要失败,但是也应该在文档中提到或者提供详细的解释。

我尝试提交一个关于这个问题的bug,最后还是放弃了,因为我不知道在哪里如何提交这样一个bug报告。

本文中的第一人称"我"指的是原作者,也就是原贴的回答。点击查看原帖:How to get default ZoneOffset in java8?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269