java时区转换

假设我们以 epoch time 作为存储格式,现在拿到 2019-10-15 20:48:19 这样一个时间,要如何转换成相应的 epoch time 呢?注意,这个时间字符串是不带时区的!

1.java.sql.Timestamp.valueOf

例如 java.sql.Timestamp.valueOf 会认为解析的字符串就是 UTC 时间。Java 创建 Timestamp类型的初衷是对标 MySQL 的 TIMESTAMP 类型,两者在解析时都认为输入是 UTC 时间也就不足为奇了。

java 复制代码
String timeStr = "2019-10-15 10:10:10.001";
Timestamp timestamp = java.sql.Timestamp.valueOf(timeStr);
System.out.println(timestamp);
System.out.println(timestamp.getTime());

// 2019-10-15 10:10:10.001 # 北京时间下运行
// 1571105410001 # (2019-10-15 10:10:10.001 in GMT)

// 2019-10-15 10:10:10.001 # 东京时间下运行
// 1571101810001 # (2019-10-15 10:10:10.001 in GMT)

上例中将系统调成北京时间(CST)还是东京时间(JST),输出的内容不变。

2.java.util.Date以及对应的 java.text.DateFormat

java.util.Date 以及对应的 java.text.DateFormat 都允许指定时区,默认选取系统的时区进行解析。下面以 SimpleDateFormat 为例 :

java 复制代码
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String timeStr = "2019-10-15 10:10:10.001";
java.util.Date date = format.parse(timeStr);
System.out.println(date);
System.out.println(date.getTime());

// Tue Oct 15 10:10:10 CST 2019 # 北京时间下运行
// 1571105410001 # (2019-10-15 2:10:10.001 in GMT)

// Tue Oct 15 10:10:10 JST 2019 # 东京时间下运行
// 1571101810001 # (2019-10-15 1:10:10.001 in GMT)

由于使用了系统当前所在的时区,上面的代码在北京时间(CST)和东京时间(JST)下执行,得到的毫秒数是不同的。

要以当前的时区展示?还是以原时区展示?这是与业务相关的。

  • 如果要判断一笔交易在几点进行,则可能按发生地时区展示/计算更合理(例如认为凌晨发生的交易是欺诈的可能性高,则境外的交易就需要按境外的时区算几点)
  • 如果要展示一篇博客何时发布,以读者所在的时区展示可能更理想

正因为这个决定跟业务相关,系统的实现者只能为两种需求都提供对应的机制。显然以 epoch time 存储是不行的,因为它不带原始时区。MySQL 的存储也同样不行,虽然以字符串存储,但依旧不包含原始时区[4]。这里我们简单记录 Java 提供的处理机制。

3.ZonedDateTime

Java 8 的 java.time 包中提供了许多时间处理的类,让我们按需自取。如 LocalDateLocalTimeLocalDateTime 内部以年月日、时分秒的形式保存了日期和时间,不包含任何时区的信息。而 ZonedDateTime 则是 DateTime 加上时区,用于处理与时区相关的所有操作,包括时区间的时间转换。

Java 8 中的 ZonedDateTime 人如其名,内部提供了额外的字段保留时区:

java 复制代码
String timeStr = "2019-10-15T10:10:10.001+02:00[Europe/Paris]";
ZonedDateTime datetime = ZonedDateTime.parse(timeStr);
System.out.println(datetime);
System.out.println(datetime.toInstant().getEpochSecond());

// 2019-10-15T10:10:10.001+02:00[Europe/Paris] # 北京时间下运行
// 1571127010 # 2019-10-15 08:10:10 GMT

可以看到,尽管在北京时间下运行,输出里仍然保留了原始输入的时区:巴黎时间。

而如果希望将巴黎时间展示为当前的时区,则可以如下操作:

java 复制代码
String timeStr = "2019-10-15T10:10:10.001+02:00[Europe/Paris]";
ZonedDateTime parisTime = ZonedDateTime.parse(timeStr);
ZonedDateTime shanghaiTime = parisTime.withZoneSameInstant(ZoneId.systemDefault());
System.out.println(shanghaiTime);
System.out.println(parisTime.toInstant().getEpochSecond());

// 2019-10-15T16:10:10.001+08:00[Asia/Shanghai]
// 1571127010

可以看到,当前时区是上海,巴黎 10:10 时,上海是 16:10

注意:

  1. SimpleDateFormat 不是线程安全的,Java 8 之后尽量使用 java.time.DateTimeFormatter

  2. MySQL 中的 DATETIME 类型不会将时间转换成 UTC,但依旧不保留时区信息。

参考:聊一聊时间戳 | 三点水

相关推荐
JH30737 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
Coder_Boy_8 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble8 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟8 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖9 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
qq_124987075310 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_10 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
Mr_sun.10 小时前
Day06——权限认证-项目集成
java
瑶山10 小时前
Spring Cloud微服务搭建四、集成RocketMQ消息队列
java·spring cloud·微服务·rocketmq·dashboard
abluckyboy10 小时前
Java 实现求 n 的 n^n 次方的最后一位数字
java·python·算法