前言
对于只负责国内业务 的公司来说,我们在业务中想要获取当前时间 ,往往只需要利用Java
自带的工具类new Date()、LocalDateTime.now()
等 方式来获取,但对于一些跨境业务 的公司来说,因为涉及到时区问题,往往需要将当前北京时间转换成其国外的当地时间
国际化时间转换,这就是本文想带大家了解的内容~
基本概念
在进行实际案例操作之前,我们需要先了解一下基本概念~
UTC
UTC(Coodinated Universal Time),协调世界时,又称世界统一时间、世界标准时间、国际协调时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
UTC 是现在全球通用的时间标准,全球各地都同意将各自的时间进行同步协调。UTC 时间是经过平均太阳时(以格林威治时间GMT为准)、地轴运动修正后的新时标以及以秒为单位的国际原子时所综合精算而成。
上面两句话是我从网上找的,从整句话上来理解UTC
感觉还是比较迷茫的,特别是还扯到地理上了,hh
但真正其核心点在于我加粗的文字: 世界标准时间、全球通用的时间标准
UTC就好比0这个数字,就是个标准,大于0为正数,小于0位负数
时区也是如此,比如北京时区就是UTC+8
又或者洛杉矶时区现在是 UTC-8
那么洛杉矶现在 跟北京的时间差就是8 - (-8)
就是16
个小时了,这么理解起来我感觉还是比较容易的~
需要注意的是,我说的是现在 ,为什么用这个词呢 ,因为接下来就引出了下一个概念: 冬令时、夏令时
冬令时、夏令时
冬令时和夏令时是用于节省日光和能源的时间制度,它们通过在一年中的不同时间调整标准时间来实现。
- 夏令时 :
- 在夏令时期间,美西时间(Pacific Daylight Time, PDT)比协调世界时(UTC)慢7小时(UTC-7)。
- 夏令时 通常从每年的三月第二个星期日开始,直到十一月第一个星期日结束。
- 冬令时 :
- 在冬令时期间,美西时间(Pacific Standard Time, PST)比协调世界时慢8小时(UTC-8)。
- 冬令时 从每年的十一月第一个星期日开始,持续到次年的三月第二个星期日
所以我说洛杉矶时区现在是 UTC-8
,因为随着时间变化,时区也会变成UTC-7
案例
基本概念大致了解后,我们需要通过案例来真正知道到底需要通过怎样的编码来实现国际化时间转换
拿到不同时区的当前时间
首先来一行LocalDateTime.now()
拿到当前时间: 2023-11-16T22:26:36.863636
java
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
}
跟一下源码可见,默认拿的系统时间,即上海时区 ,没啥问题,因为我是中国人(手动狗头)
此外我们还能拿到一个结论,那就是**LocalDateTime.now()
本身没有时区概念,最终是需要根据时区来拿到当前时间的**,也没啥问题,都不给时区,人家怎么知道你当前是什么时间,hh
既然如此,我想肯定可以手动传入时区,然后拿到目前时区的当前时间
诺,不出所料,API
还是很全面的,支持传入Clock 和 ZoneId
那么我们现在想如果拿到洛杉矶的当前时间,那么传入当前洛杉矶的时区-8
即可
java
public static void main(String[] args) {
System.out.println(LocalDateTime.now(ZoneId.of("-8")));
System.out.println(LocalDateTime.now(Clock.system(ZoneId.of("-8"))));
}
拿到不同时区的当前时间的时间戳
在Java
中,我们如果想拿到当前时间的时间戳,最常见的代码就是System.currentTimeMillis()
java
public static void main(String[] args) {
System.out.println(System.currentTimeMillis());
}
那么除了这种方式,大家还知道别的方式吗?比如LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()
我们都知道时间戳是没有时区概念的 ,在这一前提下,相信LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()
这段代码就很好理解啦
- 通过
LocalDateTime.now()
拿到当前时间,即默认使用系统时区即上海时区+8
- 然后
toInstant(ZoneOffset.of("+8"))
,传入+8
,因为拿到的当前时间存在+8
时区概念,所以我们需要传入+8
来抵消时区 - 最后
toEpochMilli
,拿到不存在时区概念的时间戳
不同时区时间互转
在之前我们都是使用的LocalDateTime
传入目标时区 ,来获取目标时区的当地时间、时间戳~
而接下来如果想要进行不同时区互相转换,我们则需要ZonedDateTime
可以简单地把ZonedDateTime
理解成LocalDateTime
加ZoneId
,即带时区的LocalDateTime
那么我们把当前北京时间转换成对应洛杉矶的时区,编码如下:
java
public static void main(String[] args) {
ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("-8"));
System.out.println(zonedDateTime.toLocalDateTime());
}
解读一下
LocalDateTime.now()
还是拿到当前系统时区,即上海时区的当前时间atZone(ZoneId.systemDefault())
,这里也可以理解为atZone(ZoneId.of("+8"))
, 表明我们LocalDateTime.now()
拿到的是时间时区是+8
.withZoneSameInstant(ZoneId.of("-8")
, 表明转换成-8
时区- 最后
toLocalDateTime
,拿到转换后的时间
根据时间戳转换成各个时区的时间
时间戳没有时间概念,所以首先想到看看Instant
有没有相关api
,如图可见,支持通过毫秒级、秒级时间戳进行转换
我们用毫秒级时间戳转换为例,转换成Instant
后,通过atZone
表明时区
我们表明为+8
时区,因为我们通过System.currentTimeMillis()
拿到的也是当前系统的时间戳,最后toLocalDateTime
,并与LocalDateTime.now()
比较一下,可以看到是基本一致的
再来个案例,我们表明当前拿到时间的时间戳是-8
时区的,那么在转换后,我们可见其时间与LocalDateTime.now()
相差16
个小时,符合预期~
总结
本文从概念到案例由浅入深的带大家了解了在国际化时间转换的一些业务常见的解决方案,如有不足,请大家补充~
我是 Code皮皮虾 ,会在以后的日子里跟大家一起学习,一起进步! 觉得文章不错的话,可以在 掘金 关注我,这样就不会错过很多技术干货啦~