时区处理综述(java技术栈)

文章目录

  • 一、jdbc协议对时间的序列化(不含时区信息)
    • 数据库的两个时间类型
    • 中国数据库使用的CST时区无法被java正确识别
    • jdbc协议跨时区传输方案
  • 二、数字时间戳(可跨时区传输)
    • 定义
    • 用途
  • 三、ISO8601(可跨时区传输)
    • java的序列化方法
    • js的序列化方法
  • 四、解析时间字符串(不含时区信息)
    • 夏令时
    • sql函数
  • 五、跨时区binlog同步
    • master-slave原生同步
    • otter同步

通过这篇文章,澄清以下几个对时间数据进行序列化/反序列化的阶段:
mysql进程从数据块里读取时间字段、把时间字段通过jdbc发送、接收从jdbc读取的时间字段、web服务传输json序列化后的时间字段;
让java开发员确信目前使用的技术栈,实际已经是在正确处理跨时区的时间信息传输

一、jdbc协议对时间的序列化(不含时区信息)

数据库的两个时间类型

https://dev.mysql.com/doc/refman/8.0/en/datetime.html
datetime和timestamp在mysql内部的存储方式不同,且理论上timestamp类型的数据在数据库时区变更后依然能正确读取到存入时的数值;
但是,在数据库本地提取数据时,已正确完成转换的时间数据,经过jdbc协议传输到远程客户端后,还是有可能被错误处理。

CREATE TABLE `test_datetime` (
`id` bigint(21) NOT NULL AUTO_INCREMENT COMMENT '流水号',
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
`create_time` timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
`modify_time` datetime(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间(每次更新都记录)',
PRIMARY KEY (`id`),
UNIQUE KEY `index_create_by` (`create_by`)) 
ENGINE = InnoDB CHARSET = utf8mb4 COMMENT '验证'; 

中国数据库使用的CST时区无法被java正确识别

MySQL将来自操作系统的CST时区识别为中国标准时间;
jdbc客户端8.0.22及以下版本,虽然会主动获取数据库的时区配置,但会将CST认为是美国中部时间,这就导致反序列化后的Date与实际时间会相差13小时,如果处在冬令时还会相差14个小时;
因为在数据库进程正确从本地文件块里解析后的datetime、timestamp数据,在通过jdbc协议传输前,会使用数据库默认时区进行序列化(序列化结果不含时区信息),被jdbc客户端接收后再使用美国中部时区进行反序列化,于是得到一个错误的java.util.Date。

mysql-connector-java的8.0.23版本开始对CST时区进行了解析修复,对应到了Asia/Shanghai。

jdbc协议跨时区传输方案

jdbc的url参数里,明确指定数据库服务器实际时区后,可避免jdbc序列化反序列化时间数据的异常

?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull

通过jdbc能否获取到正确的Date数据,和java进程本身的时区没有联系,通过修改java进程默认时区即可验证这点:-Duser.timezone=GMT+9

二、数字时间戳(可跨时区传输)

定义

UNIX认为GMT时区的1970年1月1日零点是时间纪元起始;

大多数语言/系统的时间戳就是相对上述时间点的毫秒数,使用long类型记录和传输

用途

java的Date类型内部实际使用数字时间戳(long类型)进行记录,可认为Date与进程、服务器时区无关;java.sql.Timestamp继承自Date,扩展了对纳秒信息的记录。

json反序列化时(把json格式的字符串转换为内部对象),各框架(jackson、fastjson)都能自动把数字字段转换到java的Date字段;json序列化时,各框架(jackson、fastjson)都默认是把Date字段序列化为数字时间戳。

java和javascript的Date构造函数,都可用数字时间戳作为初始化参数;java和javascript的Date都有getTime方法来获取内部存储的数字时间戳。

浏览器端提交的数据字段,在服务端是时间类型时,在浏览器端提交数字时间戳即可自动正确转换,即使两端时区不一致。

三、ISO8601(可跨时区传输)

java的序列化方法

// 解析
OffsetDateTime dateTime = OffsetDateTime.parse("1997-07-16T19:20:30.010+01:00");// 序列化(线程不安全),默认使用进程时区,可以另外指定
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
log.info(format.format(Date.from(dateTime.toInstant())));

controller层返回结果时,springboot使用的jackson默认把Date字段序列化为ISO8601字符串,可通过配置项修改为数字时间戳:

spring.jackson.serialization.write-dates-as-timestamps=true

建议前端先转化为Date数据类型后,再根据浏览器本地时区展示为可阅读的字符串

js的序列化方法

// Date的构造函数既可传入时间戳数值,又可传入ISO8601字符串
new Date('1997-07-16T19:20:30+01:00');// Date实例的toJSON、toISOString方法都能获取到ISO8601格式字符串
// JSON.stringify方法处理Date类型的字段时,就是用toJSON进行序列化
(new Date()).toJSON();
(new Date()).toISOString();

在浏览器端用JSON.stringify处理js对象的Date字段后(ISO8601序列化)再传输,即使两端时区不一致,服务端也能正确解析到java的Date字段。

四、解析时间字符串(不含时区信息)

java服务端、sql里尽量避免直接处理不含时区信息的时间字符串;
无法避免时,每个功能点需要慎重确认是不是适合使用服务端java程序的启动时区,是否应该要求客户端指定时区。

夏令时

如果时区信息包含夏令时/冬令时处理规范,则解析不含时区信息的时间字符串时,会进行偏移处理;比如下面的例子,"GMT+8"时区不进行时间偏移,"Asia/Shanghai"时区进行时间偏移

SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
isoFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));     String strDate = "1989-05-31 08:00:00"; //不含时区信息SimpleDateFormat gmt8Format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
gmt8Format.setTimeZone(TimeZone.getTimeZone("GMT+8"));SimpleDateFormat shanghaiFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
shanghaiFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
log.info("不使用中国夏令时:{},使用中国夏令时:{}", isoFormat.format(gmt8Format.parse(strDate)),isoFormat.format(shanghaiFormat.parse(strDate)));

sql函数

STR_TO_DATE
DATE_FORMAT
如果在sql执行时进行时间字符串转换,那么结果受当前session的时区信息影响;
而jdbc建立的session,是使用数据库默认时区(不受serverTimezone配置影响),可通过获取select @@SESSION.time_zone的结果进行确认;
如果java应用非要把字符串写入数据表的datetime字段,这个字符串必须表示的是数据库默认时区下的时间点(强烈不推荐)

# 时区是否支持夏令时规范、是否在经度上一致,影响获得的数字时间戳
SET time_zone = 'Asia/Shanghai';
SELECT UNIX_TIMESTAMP(STR_TO_DATE('1989-05-31 08:00:00','%Y-%m-%d %T'));
SET time_zone = '+08:00';SELECT DATE_FORMAT(NOW(6), '%Y-%m-%d %T.%f');

五、跨时区binlog同步

master-slave原生同步

因为无法指定slave连接master时使用的时区,binlog里序列化的datetime数据无法正确反序列化,所以master、slave的时区配置不一致时,不能正确同步

otter同步

因为存在中间转换,只要otter连接到两个数据库节点的serverTimezone配置正确,即可正确同步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/182061.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C 语言经典100例】C 练习实例6

题目:用*号输出字母C的图案。 程序分析:可先用’*号在纸上写出字母C,再分行输出。 程序源代码: #include "stdio.h" int main() {printf("用 * 号输出字母 C!\n");printf(" ****\n");printf(&quo…

【傻瓜级JS-DLL-WINCC-PLC交互】8.DLL读写WINCC连接的PLC数据

思路 JS-DLL-WINCC-PLC之间进行交互,思路,先用Visual Studio创建一个C#的DLL控件,然后这个控件里面嵌入浏览器组件,实现JS与DLL通信,然后DLL放入到WINCC里面的图形编辑器中,实现DLL与WINCC的通信。然后PLC与…

Vue实现可拖拽边界布局

Vue实现可拖拽边界布局 在前端开发中,有时需要实现一种可拖拽边界的布局,通过拖动分隔线来调整不同区域大小。例如,下图是一个典型的可拖拽边界布局,它由左右两个区域组成,左边是一个树形菜单,右边是一个上…

expect自动化交互

目录 1. expect作用: 2. expect语言用法: 3. 实例 1. expect作用: 是建立在tcl语言基础上的一个工具,常被用于进行自动化控制和测试,解决shell脚本中交互的相关问题。 2. expect语言用法: spawn开启免…

Spark_spark参数配置优先级

总结 &#xff1a; 优先级低-》优先级高 spark-submit 提交的优先级 < scala/java代码中的配置参数 < spark SQL hint spark submit 中提交参数 #!/usr/bin/env bashsource /home/work/batch_job/product/common/common.sh spark_version"/home/work/opt/spark&q…

Linux线程池

线程池 C版本 C版本 threadpool.h #include <pthread.h>#ifndef _THREADPOOL_H #define _THREADPOOL_Htypedef struct ThreadPool ThreadPool; // 创建线程池并初始化 ThreadPool *threadPoolCreate(int min, int max, int queueSize);// 销毁线程池 int threadPoolDest…

利用VHDL实现一定系数范围内的信号分频电路

实验要求&#xff1a; 采用 3 个开关以二进制形式设定分频系数&#xff08;0-7&#xff09;&#xff0c;实现对已知信号的分频。 实现代码&#xff08;VHDL&#xff09;&#xff1a; library ieee ; use ieee.std_logic_1164.all ; use ieee.numeric_std.all ; use ieee.std_…

ANN人工神经网络:从基础认知到现实理解

什么是神经网络&#xff1f; 神经网络的再认知 前面我们了解过&#xff0c;人工神经网络&#xff08;Artificial Neural Network&#xff0c;ANN&#xff09;是人类为了模仿人大脑的神经网络结构创建出来的一种计算机系统结构。但如果仔细深入到神经网络当中&#xff0c;会慢…

排序算法基本原理及实现1

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f4d1;插入排序 &#x1f4…

JAVAEE---多线程线程安全

根本原因&#xff1a;随机调度&#xff0c;抢占式执行 多个线程同时修改同一个变量 修改操作不是原子的 内存可见性 指令重排序 上面这段代码可以正常打印出hello&#xff0c;按照我们前面所学&#xff0c;第一次加锁之后&#xff0c;第二次加锁应该有所冲突啊。这里是因为…

c++ day2

自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void show() #ifndef RECT_H …

23.解释不同方式的自动装配,spring 自动装配 bean 有哪些方式?

解释不同方式的自动装配&#xff0c;spring 自动装配 bean 有哪些方式&#xff1f; 在spring中&#xff0c;对象无需自己查找或创建与其关联的其他对象&#xff0c;由容器负责把需要相互协作的对象引用赋予各个对象&#xff0c;使用autowire来配置自动装载模式。 在Spring框架…

量子力学应用:探索科技前沿的奇幻之旅

量子力学应用:探索科技前沿的奇幻之旅 引言 量子力学,这门探讨微观世界规律的学科,自其诞生以来就充满了神秘与奇幻。随着科学技术的不断进步,量子力学已经从纯理论研究走向了实际应用领域,为我们打开了一个全新的科技世界。在本文中,我们将深入探讨量子力学的应用方面,…

JS 绘制半径不一致的环形图进度条

HTML部分: <canvas id"mycanvas" width"100" height"100"></canvas>JS部分&#xff1a; const option {element: "mycanvas", // 元素count: 26, // 高亮数据totalCount: 129, // 总数据progressColor: #3266FB, // 进…

一文详解Python中常用数据类型

文章目录 Python 中常用的数据类型包括&#xff1a;Python 中布尔类型(bool)Python 中的数字类型概述Pyhon中的字符串概述Python 中的List概述Python 中的元组类型(tuple)Python中的字典&#xff08;Dictionary&#xff09;Python中的集合&#xff08;Set&#xff09;Python中的…

入门 PyTorch

要入门 PyTorch&#xff0c;可以按照以下步骤&#xff1a; 安装 PyTorch&#xff1a;在 PyTorch 的官方网站 PyTorch 上可以找到对应的安装方式和教程&#xff0c;选择适合自己的版本进行安装。 学习 PyTorch 基础知识&#xff1a;可以从官方文档中的入门教程开始学习&#xf…

SpringBoot自动装配和自动配置

1. 自动装配 Spring Boot 的自动装配机制允许自动配置第三方组件&#xff0c;这是通过以下几个关键步骤实现的&#xff1a; EnableAutoConfiguration 注解: Spring Boot 应用程序通常在其主类上使用 SpringBootApplication 注解&#xff0c;该注解包含 EnableAutoConfiguratio…

你了解vue的diff算法吗?

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue中的diff算法 目录 一、是什么 二、比较方式 三、原理分析 小结 一、是什么 diff 算法是一…

【蓝桥杯选拔赛真题69】Scratch洗牌发牌 少儿编程scratch图形化编程 蓝桥杯创意编程选拔赛真题解析

目录 scratch洗牌发牌 一、题目要求 编程实现 二、案例分析 1、角色分析

(C++)字符串相乘

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 题目链接如下&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名…