高级JAVA开发必备技能:java8 新日期时间API((一)JSR-310:ZoneId 时区和偏移量)(JAVA 小虚竹)_时区和偏移量的区别(1)

本文介绍了Java中Date类的ISO-8601表示法,以及JDK8前后处理时区和偏移量的不同方法,重点讲解了TimeZone、ZoneId和ZoneOffset的使用,包括FastDateFormat的线程安全性,以及JDK8后如何使用ZoneId进行时区转换和自动更新时区数据的TZUpdater工具。
摘要由CSDN通过智能技术生成

注:大家还记得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));

image-202108109417345

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

image-202108101999961

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

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

image-2021081095044291

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”;

image-20210811935505

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

java -jar tzupdater.jar -V

image-20210811933080

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

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

image-2021081193461

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

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

image-2021081198385

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

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

image-202108119194

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

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

注:

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

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

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

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

image-2021081193461

服务自动化升级
思路步骤:

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());
	}

image-202108109107753

ZoneId.systemDefault()方法实现上是调用了TimeZone:
 public 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"));

image-20210810990025341

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

image-20210810210652005

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

从日期中获取时区

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

image-202108109411428

从日期中获取时区只支持带有时区的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();
		}

image-202108109721346

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

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

image-20210810927677

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"));
	}

image-202108109251152

超出最大范围会报错

时分秒构造偏移量
	@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));
	}

image-202108109621080

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

image-202108109915102

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);
	}

image-2021081199155

博主在厦门,所以默认获取的时区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("UT")) {
            return ofWithPrefix(zoneId, 2, checkAvailable);
        }
        return ZoneRegion.ofId(zoneId, checkAvailable);
    }


   private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) {
        String prefix = zoneId.substring(0, prefixLength);
        if (zoneId.length() == prefixLength) {
            return ofOffset(prefix, ZoneOffset.UTC);
        }
       ...
    }

image-20210819856624

由源码可知:

  1. zoneId长度小于等于1位,或者以“+”或“-”开头的,创建的是ZoneOffset实例
  2. 以“UTC”,“UT”或“GMT”开头的,创建的是ZoneRegion实例
  3. 不符合以上两种的,创建的是ZoneRegion实例
	@Test
	public void ZoneIdTest8(){

		ZoneId zoneId1 = ZoneId.of("+8");
		ZoneId zoneId2 = ZoneId.of("+08:00");
		ZoneId zoneId3 = ZoneId.of("UT+8");
		ZoneId zoneId4 = ZoneId.of("Asia/Shanghai");
		System.out.println();
	}

image-2021081194068

推荐相关文章

hutool日期时间系列文章

1DateUtil(时间工具类)-当前时间和当前时间戳

2DateUtil(时间工具类)-常用的时间类型Date,DateTime,Calendar和TemporalAccessor(LocalDateTime)转换

3DateUtil(时间工具类)-获取日期的各种内容

4DateUtil(时间工具类)-格式化时间

5DateUtil(时间工具类)-解析被格式化的时间

6DateUtil(时间工具类)-时间偏移量获取

7DateUtil(时间工具类)-日期计算

8ChineseDate(农历日期工具类)

9LocalDateTimeUtil(JDK8+中的{@link LocalDateTime} 工具类封装)

10TemporalAccessorUtil{@link TemporalAccessor} 工具类封装

其他

要探索JDK的核心底层源码,那必须掌握native用法

万字博文教你搞懂java源码的日期和时间相关用法

java的SimpleDateFormat线程不安全出问题了,虚竹教你多种解决方案

源码分析:JDK获取默认时区的风险和最佳实践

如何自学黑客&网络安全

黑客零基础入门学习路线&规划

初级黑客
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)
恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k

到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?

如果你想要入坑黑客&网络安全,笔者给大家准备了一份:282G全网最全的网络安全资料包评论区留言即可领取!

7、脚本编程(初级/中级/高级)
在网络安全领域。是否具备编程能力是“脚本小子”和真正黑客的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力.

如果你零基础入门,笔者建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习;搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP, IDE强烈推荐Sublime;·Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完;·用Python编写漏洞的exp,然后写一个简单的网络爬虫;·PHP基本语法学习并书写一个简单的博客系统;熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选);·了解Bootstrap的布局或者CSS。

8、超级黑客
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。
img

网络安全工程师企业级学习路线

img
如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的

视频配套资料&国内外网安书籍、文档&工具

当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

img
一些笔者自己买的、其他平台白嫖不到的视频教程。
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值