十五万字《JDK源码分析》之《JSR-310(java8 新日期时间API)》总结(JAVA 小虚竹,建议收藏)

Posted 小虚竹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十五万字《JDK源码分析》之《JSR-310(java8 新日期时间API)》总结(JAVA 小虚竹,建议收藏)相关的知识,希望对你有一定的参考价值。

❤️作者简介:大家好,我是小虚竹。Java领域优质创作者🏆,CSDN博客专家🏆
❤️技术活,该赏
❤️点赞 👍 收藏 ⭐再看,养成习惯

什么是JSR规范

来自百度百科的解释:

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

JCP又是什么神秘的组织

JCP(Java Community Process) 是一个开放的国际组织,主要由Java开发者以及被授权者组成

所以JAVA的标准化规范,都是这个组织审核发布的。感觉真的是太厉害了。

那JCP的组织成员有哪些,八卦之火熊熊燃起

从官网获取的信息:

对于2020年度选举,将消除两个批准和一个选举席位,从而将EC减少到18名成员
2020年年级选举后,将有11个批准席位,4个选举席位,2个准席位(给个人的),Oracle America持有的常任席位。成员任期 2 年,任期交错,因此 17 个席位中的 8 或 9 个通常每年需要批准/选举。

我们来看看2020年获批准和选举的名单

以下划线的,就是2020年获批准和选举入选的。

Alibaba: Alibaba入选,国内企业入选JCP,厉害了。

BellSoft : BellSoft 发布并支持免费且安全的 Unified Java Runtime,Liberica JDK。它可用于大多数平台和当今的架构,包括基于微服务的架构。

BNY Mellon: 全球拥有超过 11,000 名技术专家和开发人员,支持该银行作为全球金融市场清算、支付、托管和资产管理服务的领先供应商。

Java 社区参与

- JCP 执行委员会 (EC) 的积极批准成员
- 提议的 Java Collections 2.0
- 通过工作组致力于 Java 教育
- 领导 Java 用户组
- 通过演示、博客、代码研讨会与社区互动
- 开源参与

JetBrains: IDEA用过吧,这产品就是JetBrains公司研发的。

MicroDoc: MicroDoc 是一家位于德国的面向技术的软件开发企业,为全球客户群提供嵌入式 Java 虚拟机。MicroDoc 是开放标准和商业上可用的开源软件的支持者。它是 OpenJDK 的积极贡献者。自 2014 年以来,MicroDoc 在 JCP EC 中占有一席之地,并决定参加下一个选举期以促进 Java 的发展,重点是汽车、医疗保健和智能环境中的嵌入式应用程序。

SAP SE: SAP 拥有超过 250,000 家企业客户,是全球领先的商业软件供应商。这些 SAP 客户中有很大一部分运行基于 Java 的 SAP。SAP 自 2001 年以来一直参与 JCP,并在 50 多个 JSR 中进行了合作。SAP 自 2012 年开始为 OpenJDK 做出贡献。它目前参与 OpenJDK 11 更新项目并提供 SapMachine,这是 OpenJDK 的免费构建版本。SAP 致力于 Java 的持续成功。

London Java Community: 伦敦 Java 社区, 代表全球 JUG(伦敦 Java 用户组) 社区(40 万+ 开发人员)。我们运行“采用 JSR”、“采用 OpenJDK”来提高开发人员对 Java 标准的日常参与。

Eclipse Foundation: Eclipse 基金会自 2007 年以来一直参与 JCP 执行委员会。作为 Eclipse Java IDE、Jakarta EE、MicroProfile、Adoptium、Eclipse Java 编译器和 OpenJ9 的所在地,我们非常致力于 Java 生态系统。我们对 JCP EC 的主要贡献是代表开源社区的利益以及 Java 规范的独立实现。

Ken Fogel: 我是蒙特利尔道森学院计算机科学技术课程的教授。该计划的任务是培训学生,主要是高中毕业后,成为准备工作的软件开发人员。2019 年,我成为 Java Champion。五年前,我在蒙特利尔开始了一个名为 DawsCon 的免费为期一天的会议。我吸引了一些最优秀的演讲者与学生和当地开发者社区交谈。在过去的六年里,我也在 JavaOne/CodeOne 上发表过演讲。

好!八卦结束,回归正题。

JSR规范内容

包含在三个 Java 版本的标准集合:JAVA SE、JAVA EE和 JAVA ME。

某些JSR包含在一个或多个JAVA平台中。

以下是对应的官网地址:

Java SE

Java EE

Java ME

点击JAVA SE进来看看

5JSR的编号,同一行的内容为此JSR编号对应的名称
Description:描述此JSR是用来干嘛的
Status:当前此JSR是什么状态
Latest Stage:最新阶段
Spec Lead:规范的负责人

点击JSR编号对应的名称,会跳转到此JSR内容的详情页。

Status状态:

StatusWhat it means
Active在过去 12 个月内发布了里程碑的 JSR。
Final一年多前产生了最终发布里程碑的 JSR。
Maintenance上次产生维护审查或维护发布里程碑的 JSR 是在一年多以前
Inactive尚未生成最终版本或维护版本且在去年未发布里程碑的 JSR
Withdrawn在最终发布之前由规范负责人从 JCP 中撤回的 JSR。
Rejected在该 JSR 的投票阶段之一中未得到执行委员会批准的 JSR
Dormant被执行委员会投票为“休眠”的 JSR,或已达到其自然寿命终点的 JSR

现在大家对JSR有一定的基本认识了吧。

JSR-310介绍

博主最近在研究JAVA 日期时间的源码,以JSR-310为切入口,给大家介绍下JSR

第一部分:时间线

JSR-310从开始到发布,历时7年左右的时间。以小见大可知,每个JSR的发布是经过细细打磨的精品。

第二部分:团队

规范负责人和专家组名单

第三部分:发展时间线明细

JSR-310 的发展时间线:

该API(JSR-310)是Stephen Colebourne (斯蒂芬·科尔本)基于Joda-Time 项目进行实现的。

Stephen Colebourne (斯蒂芬·科尔本)创立并领导了 Joda-Time 项目,并且是 Apache 软件基金会的成员,主要致力于 Jakarta-Commons。Michael Nascimento Santos 创立并领导了 Genesis 项目,并为 NetBeans、AspectWerkz 和 Thinlet 做出了贡献。Michael 还拥有 5 个以前的 JSR 的经验。

第四部分:明细

JSR-310规范的描述(官方文档直接翻译)

这个 JSR 将为 Java 提供一个新的和改进的日期和时间 API。主要目标是借鉴从 Java SE 前两个 API(日期和日历)中吸取的经验教训,为日期和时间操作提供更高级和更全面的模型。

新 API 将针对所有需要日期和时间数据模型的应用程序。该模型将超越类来替换日期和日历,包括表示没有时间的日期、没有日期的时间、持续时间和间隔。这将提高应用程序代码的质量。例如,日期和时间模型将提供一个明确定义它的类,而不是使用 int 来存储持续时间,并使用 javadoc 将其描述为天数。

新 API 还将解决相关的日期和时间问题。这些包括格式化和解析,考虑到 ISO8601 标准及其实现,例如 XML。此外,还将考虑序列化和持久性领域。

新 API 的最终目标是简单易用。API 将需要包含一些强大的功能,但不能让这些功能模糊标准用例。易于使用的一部分包括与现有 Date 和 Calendar 类的交互,这将是专家组的重点。

目标Java平台

Java SE(Java EE 也适用)

提议的规范将满足 Java 社区的哪些需求(官方文档直接翻译)

  1. 目前 Java SE 有两个独立的日期和时间 API - java.util.Date 和 java.util.Calendar。Java 开发人员在博客和论坛上一致认为这两种 API 难以使用。值得注意的是,两者都使用了月份的从0开始索引,这是导致许多错误的原因。多年来,日历也遭受了许多错误和性能问题,主要是由于在内部以两种不同的方式存储其状态。
  2. 一个经典错误 (4639407) 阻止在 Calendar 对象中创建某些日期。可以编写一系列代码,可以在某些年份创建日期,但不能在其他年份创建,从而防止某些用户输入正确的出生日期。这是由于 Calendar 类只允许在夏季增加 1 小时的夏令时时间,而在历史上,它是在第二次世界大战前后增加 2 小时。虽然此错误现已修复,但如果在未来某个时间某个国家/地区选择在夏季引入 3 小时的夏令时增益,那么 Calendar 类将再次被破坏。
  3. 当前的 Java SE API 也受到多线程环境的影响。众所周知,不可变类本质上是线程安全的,因为它们的状态不能改变。但是,Date 和 Calendar 都是可变的,这需要程序员明确考虑克隆和线程化。此外,DateTimeFormat 中缺乏线程安全性并不广为人知,并且已成为许多难以追踪的线程问题的原因。
  4. 除了 Java SE 的 datetime 类的问题外,它没有用于建模其他概念的类。非时区日期或时间、持续时间、周期和间隔在 Java SE 中没有类表示。因此,开发人员经常使用 int 来表示持续时间,并使用 javadoc 指定单位。
  5. 缺乏全面的日期和时间模型也导致许多常见操作比应有的更棘手。例如,计算两个日期之间的天数是目前特别困难的问题。
  6. 该 JSR 将解决完整日期和时间模型的问题,包括日期和时间(有和没有时区)、持续时间和时间段、间隔、格式和解析。

第五部分:下载API和分析

JSR-310包含的核心代码有哪些呢

下载API文档:如图点击:

选择下载:

打开index页面

看到这个目录包结构,很熟悉是不是

java.time 包里放的是java8 新的日期和时间API

java.time包含基于ISO-8601标准的主API。这里定义的类表示主要的日期时间概念,包括瞬间、持续时间、日期、时间、时区和时段。它们以ISO日历系统为基础,这是遵循公历规则的事实上的世界日历。所有的类都是不可变的和线程安全的。

博主最近刚写了篇关于java新旧日期和时间的API对比文章《万字博文教你搞懂java源码的日期和时间相关用法

有兴趣可以了解一下,可以快速了解两套API的差异和新API的好用之处。

(一)JSR-310:ZoneId 时区和偏移量

地理知识回顾

时区

由于世界各国家与地区经度不同,地方时也有所不同,因此会划分为不同的时区。
正式的时区划分包括24个时区,每一时区由一个英文字母表示,每隔经度15°划分一个时区。
为了克服时间上的混乱,1884年在华盛顿召开的一次国际经度会议(又称国际子午线会议)上,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1—12区,西1—12区。每个时区横跨经度15度,时间正好是1小时。最后的东、西第12区各跨经度7.5度,以东、西经180度为界。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时,相邻两个时区的时间相差1小时。
例如,中国东8区的时间总比泰国东7区的时间早1小时,而比日本东9区的时间晚1小时。
–引用自百度百科

时区经度分布如列表所示:

时区时区经度范围时区中心线
UTC(0时区)7.5°W~7.5°E
UTC+1(东1区)7.5°E~22.5°E15°E
UTC+2(东2区)22.5°E~37.5°E30°E
UTC+3(东3区)37.5°E~52.5°E45°E
UTC+4(东4区)52.5°E~67.5°E60°E
UTC+5(东5区)67.5°E~82.5°E75°E
UTC+6(东6区)82.5°E~97.5°E90°E
UTC+7(东7区)97.5°E~112.5°E105°E
UTC+8(东8区)112.5°E~127.5°E120°E
UTC+9(东9区)127.5°E~142.5°E135°E
UTC+10(东10区)142.5°E~157.5°E150°E
UTC+11(东11区)157.5°E~172.5°E165°E
UTC12(东、西12区)172.5°E~172.5°W180°
UTC-11(西11区)172.5°W~157.5°W165°W
UTC-10(西10区)157.5°W~142.5°W150°W
UTC-9(西9区)142.5°W~127.5°W135°W
UTC-8(西8区)127.5°W~112.5°W120°W
UTC-7(西7区)112.5°W~97.5°W105°W
UTC-6(西6区)97.5°W~82.5°W90°W
UTC-5(西5区)82.5°W~67.5°W75°W
UTC-4(西4区)67.5°W~52.5°W60°W
UTC-3(西3区)52.5°W~37.5°W45°W
UTC-2(西2区)37.5°W~22.5°W30°W
UTC-1(西1区)22.5°W~7.5°W15°W

实际上,常常1个国家或1个省份同时跨着2个或更多时区,为了照顾到行政上的方便,常将1个国家或1个省份划在一起。例如,中国幅员宽广,差不多跨5个时区,但为了使用方便简单,实际上在只用东八时区的标准时即北京时间为准。

UTC

协调世界时,又称世界统一时间、世界标准时间、国际协调时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
协调世界时是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。
国际原子时的准确度为每日数纳秒,而世界时的准确度为每日数毫秒。许多应用部门要求时间系统接近世界时UT,对于这种情况,一种称为协调世界时的折中时标于1972年面世。为确保协调世界时与世界时相差不会超过0.9秒,在有需要的情况下会在协调世界时内加上正或负闰秒。因此协调世界时与国际原子时之间会出现若干整数秒的差别,两者之差逐年积累,便采用跳秒(闰秒)的方法使协调时与世界时的时刻相接近,其差不超过1s。它既保持时间尺度的均匀性,又能近似地反映地球自转的变化。
–引用自百度百科

协调世界时跟地区位置没有相关,不代表当前时刻某个地方的时间,所以在说某个地方时间时要加上时区。例如:中国就是UTC+8。

UTC是时间标准,这个标准把世界分成UTC-12到UTC+12共24个时区。

GMT

GMT(Greenwich Mean Time)别名:格林尼治时间(有时候翻译也叫格林威治),中文名:世界时。

GMT是指格林尼治所在地的标准时间,也是表示地球自转速率的一种形式。以地球自转为基础的时间计量系统。地球自转的角度可用地方子午线相对于地球上的基本参考点的运动来度量。为了测量地球自转,人们在地球上选取了两个基本参考点:春分点(见分至点)和平太阳点,由此确定的时间分别称为恒星时和平太阳时。
–引用自百度百科

GMT并不等于UTC,只是格林尼治刚好在0时区上。所以GMT = UTC+0才是对的。

CST

CST可视为美国、澳大利亚、古巴或中国的标准时间
美国中部时间:Central Standard Time (USA) UT-6:00
澳大利亚中部时间:Central Standard Time (Australia) UT+9:30
中国标准时间:China Standard Time UT+8:00
古巴标准时间:Cuba Standard Time UT-4:00

–引用自百度百科

所以在换算CST时间时,要注意对应的时区。这是一个坑。

美国中部时间:CST=UTC/GMT-6;

中国标准时间:CST=UTC/GMT+8;

DST

DST(Daylight Saving Time)中文名:夏令时。

表示为了节约能源,人为规定时间的意思。也叫夏时制,夏令时(Daylight Saving Time:DST),又称“日光节约时制”和“夏令时间”,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。全世界有近110个国家每年要实行夏令时。
–引用自百度百科

中国实现DST时间范围:1986年至1991年。

ISO-8601

国际标准化组织的国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前最新为第三版ISO8601:2004,第一版为ISO8601:1988,第二版为ISO8601:2000
–引用自百度百科

年由4位数字组成YYYY,或者带正负号的四或五位数字表示±YYYYY。以公历公元1年为0001年,以公元前1年为0000年,公元前2年为-0001年。

月、日用两位数字表示:MM、DD。

只使用数字为基本格式。使用短横线"-"间隔开年、月、日为扩展格式。

小时、分和秒都用2位数表示,对UTC时间最后加一个大写字母Z,其他时区用实际时间加时差表示。如UTC时间下午2点30分5秒表示为14:30:05Z或143005Z,当时的北京时间表示为22:30:05+08:00或223005+0800,也可以简化成223005+08。

注:大家还记得java的Date类吗?它默认就是使用ISO-8601表示的。

JDK8之前:时区/偏移量TimeZone

在JDK8之前,我们一直用java.util.TimeZone来表示和处理时区和偏移量。

**TimeZone.getDefault()**获得当前JVM所运行的时区,那它是怎么获取默认时区的呢,之前有写过分析文章,有兴趣的可以了解下,这里就不再重复了。

JDK获取默认时区的风险和最佳实践

有时候需要做时区的时间转换,比如一个时间要用北京时间和纽约时间显示。实现:

这里没有到SimpleDateFormat 来格式化时间是因为它是线程不安全的。选用线程安全的FastDateFormat,

Apache Commons Lang包支持。

有兴趣可以了解下FastDateFormat 的源码分析:java的SimpleDateFormat线程不安全出问题了,虚竹教你多种解决方案

		String patternStr = "yyyy-MM-dd HH:mm:ss";
		// 北京时间(new出来就是默认时区的时间)
		Date bjDate = new Date();
		// 得到纽约的时区
		TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York");
		// 根据此时区 将北京时间转换为纽约的Date
		FastDateFormat fastDateFormat = FastDateFormat.getInstance(patternStr,newYorkTimeZone);
		System.out.println("这是北京时间:" + FastDateFormat.getInstance(patternStr).format(bjDate));
		System.out.println("这是纽约时间:" + fastDateFormat.format(bjDate));

19-7=12 北京时间比纽约时间快12小时。

JDK8开始支持:时区/偏移量 ZoneId/ZoneOffset

JDK8中ZoneId表示时区的ID,ZoneOffset表示Greenwich/UTC的偏移量。

ZoneId 是用来替换java.util.TimeZone 的。

我们来研究下ZoneId ,ZoneId代表一个时区的ID,它是确定的。但是时区ID是有对应的规则,规则变化为java.time.zone.ZoneRules 决定。像夏令时规则是由各国政府定的,可能会变化,不同的年还不一样,这个就交给JDK底层机制来保持同步,我们调用者不需要关心(不!要关心!当技术不再是黑盒时,才能做到心里有底! )。

时区的规则发生变化时,如何同步时区

TZUpdater 工具介绍

​ 提供的 TZUpdater 工具 允许您使用更新的时区数据更新已安装的 Java 开发工具包 (JDK) 和 Java 运行时环境 (JRE) 软件,以适应不同国家/地区的夏令时 (DST) 更改。Oracle 依赖于通过 IANA 的时区数据库公开提供的时区数据。

如果您无法使用 Oracle 最新的 JDK 或 JRE 更新版本,或者如果最新版本上的时区数据不是最新可用的,TZUpdater 工具提供了一种更新时区数据的方法,同时保持其他系统配置和依赖项不变.

TZUpdater 工具用法

TZUpdater 工具用于执行该工具的 JDK/JRE 软件实例。每次执行都会修改 JDK/JRE 软件。要将工具管理到 JDK/JRE 软件的多个实例。

在安装的 JDK/JRE 软件上运行 TZUpdater 工具之前,您必须停止操作系统上的 JDK/JRE 软件的任何正在运行的服务。

使用以下命令运行 TZUpdater 工具:

java -jar tzupdater.jar options

要成功更新时区数据,您应该确保您有足够的权限来修改JDK_HOME /jre/libJRE_HOME /lib目录。

如果未指定任何选项,则会显示用法消息。要更新时区数据,请使用-l-f选项。

选项描述
-h, --help
将用法打印到stdout并退出。如果指定此选项,则其他选项将被忽略。
-V, --version打印工具版本、JRE 中的 tzdata 版本以及工具将更新到的 tzdata 版本,然后退出。
-l, --location url-link-to-archive-file从提供的tzdata.tar.gz包中编译、测试和更新 JRE 时区数据,例如-l https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz. 支持的 URL 协议:http://、https://、file://。如果未提供 URL 链接,该工具将使用位于 的最新 IANA tzdata 包https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz
-f, --force强制 tzdata 更新。如果更新到较旧的 tzdata 版本,请使用此选项。
-v, --verbose向 显示详细消息stdout

手动升级

注意:

1、在安装的 JDK/JRE 软件上运行 TZUpdater 工具之前,您必须停止操作系统上的 JDK/JRE 软件的任何正在运行的服务。

2、要成功更新时区数据,您应该确保您有足够的权限来修改JDK_HOME /jre/libJRE_HOME /lib目录。(linux系统:JRE目录要有写权限;windows系统:用管理员身份运行cmd)

3、如果系统上有多个JDK/JRE ,需要将该工具用于每个JDK/JRE中(每个JDK/JRE都要操作一遍)

4、更新成功后,要重新启动此 JDK/JRE 实例上的应用程序服务(如果还没更新,重启下服务器试试)

操作步骤:

1、下载Oracle官方提供的tzupdater.jar包;下载地址

https://www.oracle.com/java/technologies/javase-tzupdater-downloads.html

把tzupdater.jar放到java目录bin目录下,比如

“C:\\Program Files\\JAVA\\java-1.8.0-openjdk-1.8.0.201\\bin\\tzupdater.jar”;

2、查看当前时区数据库版本,以windows为例,用管理员身份运行cmd,切换到tzupdater.jar对应的目录:

java -jar tzupdater.jar -V

3、在线更新,以windows为例,用管理员身份运行cmd,切换到tzupdater.jar对应的目录:(第3种和第4种更新方式任选一种)

java -jar tzupdater.jar -l https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz

如图所示,已经更新成功到了tzdata2021a版本了。

更新后的文件是放在jre/lib/tzdb.dat ,如图所示,它有备份历史的版本。

4、离线更新:要先下载最新的时区数据,下载地址:

https://data.iana.org/time-zones/releases/

以windows为例,用管理员身份运行cmd。切换到tzupdater.jar对应的目录:

java -jar tzupdater.jar -l file:///[path]/tzdata.tar.gz 

注:

windows建议放在C盘根目录下,路径目录也不要有中文;

用管理员身份运行cmd(需要写权限);

如上面的命令所示,file后面的/是3个

5、以上执行完后,用第2步的查看当前时区数据库版本命令,查看是否更新成功。

服务自动化升级

思路步骤:

1、设置定时任务(操作系统配置就行),执行tzupdater 更新时区的命令脚本;

2、新开一个时区服务,用来对外提供时区和夏令时规则读取服务,独立部署;

3、在时区服务中,写个同步按钮,用来执行tzupdater 更新时区的命令脚本;

4、在时区服务中,将timeZone数据定时写到自定义的时区表中。提供维护功能,可以自定义新增修改删除timeZone数据。

此思路的好处:

1、其他服务不需要停止服务来更新时间,直接通过调用时区服务的数据,可保证获取到最新的时区数据;

2、自动化的好处,避免了手动维护时区的繁琐,人工介入有引发问题的风险;

3、时区服务和其他业务服务是拆分的,方便未来的扩展。

系统默认的ZoneId

	@Test
	public void timeZoneTest2()
		System.out.println("JDK 8之前做法:"+TimeZone.getDefault());

		System.out.println("JDK 8之后做法:"+ZoneId.systemDefault());
	

ZoneId.systemDefault()方法实现上是调用了TimeZonepublic static ZoneId systemDefault() 
        return TimeZone.getDefault().toZoneId();
    

所以两个的结果是一样的(Asia/Shanghai),这个很正常。

TimeZone.toZoneId() 是java8 后加的方法。

   /**
     * Converts this @code TimeZone object to a @code ZoneId.
     *
     * @return a @code ZoneId representing the same time zone as this
     *         @code TimeZone
     * @since 1.8
     */
    public ZoneId toZoneId() 
        String id = getID();
        if (ZoneInfoFile.useOldMapping() && id.length() == 3) 
            if ("EST".equals(id))
                return ZoneId.of("America/New_York");
            if ("MST".equals(id))
                return ZoneId.of("America/Denver");
            if ("HST".equals(id))
                return ZoneId.of("America/Honolulu");
        
        return ZoneId.of(id, ZoneId.SHORT_IDS);
    

指定字符串得到ZoneId和获取所有的zoneIds

		System.out.println(ZoneId.of("America/New_York"));

		System.out.println(ZoneId.of("Asia/Shanghai"));

	@Test
	public void ZoneIdTest2()
		Set<String> zoneIds = ZoneId.getAvailableZoneIds();
		System.out.println("zoneIds长度:"+zoneIds.size());
		for(String zoneId : zoneIds)
			System.out.println(zoneId);
		
	

指定的字符串不能乱写,不然会报错,要在ZoneId.getAvailableZoneIds() 的集合范围里。

从日期中获取时区

System.out.println(ZoneId.from(ZonedDateTime.now()));
			
System.out.println(ZoneId.from(ZoneOffset.of("+8")));

从日期中获取时区只支持带有时区的TemporalAccessor ,像LocalDateTime,LocalDate是不可以的,会报错。

		try 
			System.out.println(ZoneId.from(LocalDateTime.now()));
		catch (Exception e)
			e.printStackTrace();
		
		try 
			System.out.println(ZoneId.from(LocalDate.now()));
		catch (Exception e)
			e.printStackTrace();
		

ZoneId是抽象类,它有两个继承实现类:

  • ZoneOffset:时区偏移量
  • ZoneRegion:地理区域

ZoneOffset(时区偏移量)

时区偏移量是时区与Greenwich/UTC之间的时间差,一般是固定的小时数和分钟数。

最小/最大偏移量

	@Test
	public void ZoneIdTest5()
		System.out.println("最小偏移量:" + ZoneOffset.MIN);
		System.out.println("最小偏移量:" + ZoneOffset.MAX);
		System.out.println("中心偏移量:" + ZoneOffset.UTC);
		// 超出最大范围
		System.out.println(ZoneOffset.of("+100"));
	

超出最大范围会报错

时分秒构造偏移量

	@Test
	public void ZoneIdTest6()
		System.out.println(ZoneOffset.ofHours(10));
		System.out.println(ZoneOffset.ofHoursMinutes(10, 10));
		System.out.println(ZoneOffset.ofHoursMinutesSeconds(10, 10, 10));

		System.out.println(ZoneOffset.ofHours(-10));
	

挺方便的,也简单好理解。偏移量可以精确到秒级。

ZoneRegion(地理区域)

ZoneRegion表示地理区域,格式是:洲(州、国家)/城市。最常见的区域分类是时区数据库(TZDB)。

which defines regions such as ‘Europe/Paris’ and ‘Asia/Tokyo’.(TZDB使用“Europe/Paris”和“Asia/Tokyo”来区分地区。)

final class ZoneRegion extends ZoneId implements Serializable 
	...

由源码可知,地理区域ZoneRegion是ZoneId的继承实现类。

但是我们发现这个不是对外使用的,ZoneRegion的修饰符是default(只能由同包下的类调用)。只能通过ZoneId来操作。

	@Test
	public void ZoneIdTest7()
		ZoneId zoneId = ZoneId.systemDefault();
		System.out.println(zoneId);
	

博主在厦门,所以默认获取的时区ID是Asia/Shanghai

ZoneId的实例是ZoneOffset或ZoneRegion

ZoneId of(String zoneId, boolean checkAvailable) 源码分析:

/**
     * Parses the ID, taking a flag to indicate whether @code ZoneRulesException
     * should be thrown or not, used in deserialization.
     *
     * @param zoneId  the time-zone ID, not null
     * @param checkAvailable  whether to check if the zone ID is available
     * @return the zone ID, not null
     * @throws DateTimeException if the ID format is invalid
     * @throws ZoneRulesException if checking availability and the ID cannot be found
     */
    static ZoneId of(String zoneId, boolean checkAvailable) 
        Objects.requireNonNull(zoneId, "zoneId");
        if (zoneId.length() <= 1 || zoneId.startsWith("+") || zoneId.startsWith("-")) 
            return ZoneOffset.of(zoneId);
         else if (zoneId.startsWith("UTC") || zoneId.startsWith("GMT")) 
            return ofWithPrefix(zoneId, 3, checkAvailable);
         else if (zoneId.startsWith(&

以上是关于十五万字《JDK源码分析》之《JSR-310(java8 新日期时间API)》总结(JAVA 小虚竹,建议收藏)的主要内容,如果未能解决你的问题,请参考以下文章

❤️高级JAVA开发必备技能❤️java8 新日期时间API(JSR-310:实战+源码分析),5万字详解(JAVA 小虚竹,建议收藏)

高级JAVA开发必备技能:java8 新日期时间API(JSR-310:实战+源码分析)(JAVA 小虚竹)

高级JAVA开发必须掌握技能java8 新日期时间API(JSR-310:ZoneId 时区和偏移量),2万字详解(全程干货,建议收藏)

高级JAVA开发必须掌握技能java8 新日期时间API(JSR-310:常用的日期时间API),4万字详解(全程干货,建议收藏)

❤️高级JAVA开发必备技能❤️java8 新日期时间API(JSR-310:格式化和解析),2万字详解(JAVA 小虚竹,建议收藏)

❤️高级JAVA开发必备技能❤️java8 新日期时间API(JSR-310:常用计算工具),2万字详解(JAVA 小虚竹,建议收藏)