注:大家还记得java的Date类吗?它默认就是使用ISO-8601表示的。
第二节:JDK8之前:时区/偏移量TimeZone
在JDK8之前,我们一直用java.util.TimeZone来表示和处理时区和偏移量。
TimeZone.getDefault() 获得当前JVM所运行的时区,那它是怎么获取默认时区的呢,之前有写过分析文章,有兴趣的可以了解下,这里就不再重复了。
有时候需要做时区的时间转换,比如一个时间要用北京时间和纽约时间显示。实现:
这里没有到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/lib
或JRE_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/lib
或JRE_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()方法实现上是调用了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"));
@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("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);
}
...
}
由源码可知:
- zoneId长度小于等于1位,或者以“+”或“-”开头的,创建的是ZoneOffset实例
- 以“UTC”,“UT”或“GMT”开头的,创建的是ZoneRegion实例
- 不符合以上两种的,创建的是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();
}
推荐相关文章
hutool日期时间系列文章
2DateUtil(时间工具类)-常用的时间类型Date,DateTime,Calendar和TemporalAccessor(LocalDateTime)转换
9LocalDateTimeUtil(JDK8+中的{@link LocalDateTime} 工具类封装)
10TemporalAccessorUtil{@link TemporalAccessor} 工具类封装
其他
java的SimpleDateFormat线程不安全出问题了,虚竹教你多种解决方案
如何自学黑客&网络安全
黑客零基础入门学习路线&规划
初级黑客
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、超级黑客
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。
网络安全工程师企业级学习路线
如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的
视频配套资料&国内外网安书籍、文档&工具
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。
一些笔者自己买的、其他平台白嫖不到的视频教程。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!