-
前端js的Date类型和后端SpringBoot的Date类型在网络传输中是自动基于UNIX时间戳封装的,因此只要对应使用这两种数据类型,就不会带来时区差异
-
无论js,java,python,在未指定时区的情况下,将Date类型的对象格式化为string时,均会使用机器本地的时区,可以通过修改电脑的时区,再格式化同一个时间戳的Date对象来测试
在将一个字符串转为Date对象时,也会默认使用当前电脑的时区。但需要注意的是,转换成的Date对象本身是不带时区属性的,只是将字符串解释成时间戳的过程中使用了本地时区
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date d = formatter.parse("2023-06-06 10:48:24.476");
log.info("d timestamp = {}", d.getTime());
# 东八区 d timestamp = 1686019704476
- mysql的datetime类型保存格式为
yyyy-MM-dd HH:mm:ss
(如使用datetime(3)则为yyyy-MM-dd HH:mm:ss.SSS
)的日期时间字符串,不带时区信息,保存值以mysql server的时区信息为准。因此如果有多台不同的mysql server,最好统一所有的时区为UTC
- 通过ORM将mysql数据库里的datetime列转为java中Date对象时,会使用字符串解析的方法封装成Date类,当java向mysql数据库传递Date类时,会将Date类转为字符串datetime格式传递
当java应用与mysql数据库之间的连接有serverTimezone
参数时,此处的server指的是mysql数据库服务器,而连接到数据库的应用,虽然有可能是个SpringBoot服务器,但从ORM的角度依然是客户端。serverTimezone
参数会让java应用认为mysql数据库的时区为该指定的时区,故在根据yyyy-MM-dd HH:mm:ss
字符串计算出时间戳后,会额外加减运行java应用的当前电脑与此时区的时间差。
如数据库在一台东八区的电脑上,其中有·datetime·数据
mysql> SELECT * FROM date_test;
+----+-------------------------+
| id | create_time |
+----+-------------------------+
| 1 | 2023-06-06 10:48:24.476 |
+----+-------------------------+
用同一台东八区的电脑上的SpringBoot应用去连接它,连接配置为
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/spinq_cloud_computing?characterEncoding=utf-8&serverTimezone=UTC
数据库映射类如下
package cn.spinq.cloud.mini.entity;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
@Entity
@Data
@Table(name = "date_test")
public class DateTest {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private long id;
@Column(name = "create_time", nullable = false, columnDefinition = "datetime(3)")
private Date createTime;
public DateTest() {
Date d = new Date();
createTime = d;
}
public DateTest(Date d) {
createTime = d;
}
运行如下测试
@Test
void DateDiffTest() throws ParseException {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Optional<DateTest> d1Op = dateTestRepository.findById(new Long(1));
Date d1 = d1Op.get().getCreateTime();
log.info("d1 is = {}, shown by str = {}", d1.getTime(), formatter.format(d1));
Date d2 = formatter.parse("2023-06-06 10:48:24.476");
log.info("unwritten d2 is = {}, shown by str = {}", d2.getTime(), formatter.format(d2));
DateTest d2Test = dateTestRepository.save(new DateTest(d2));
log.info("written d2 is = {}, shown by str = {}", d2Test.getCreateTime().getTime(), formatter.format(d2Test.getCreateTime()));
log.info("d2 is {} hours more than d1.", (d2.getTime() - d1.getTime())/(3600*1000));
}
获取到结果
INFO main Method: DateDiffTest - d1 is = 1686048504476, shown by str = 2023-06-06 18:48:24.476
INFO main Method: DateDiffTest - unwritten d2 is = 1686019704476, shown by str = 2023-06-06 10:48:24.476
INFO main Method: DateDiffTest - written d2 is = 1686019704476, shown by str = 2023-06-06 10:48:24.476
INFO main Method: DateDiffTest - d1 is 8 hours more than d2.
可知此处SpringBoot应用将mysql数据库中id=1的2023-06-06 10:48:24.476
当做了UTC时区的时间来处理,生成Date对象时为其多加了8个小时
查看数据库,发现插入的第二条数据如下
mysql> SELECT * FROM date_test;
+----+-------------------------+
| id | create_time |
+----+-------------------------+
| 1 | 2023-06-06 10:48:24.476 |
| 2 | 2023-06-06 02:48:24.476 |
+----+-------------------------+
可知在插入d2的时候,因为java判断mysql需要UTC的时间,因此将程序中当前时区的时间减去了8个小时,再存入了数据库
- 当mysql数据库时区和
serverTimezone
固定,改变java应用的时区,发现均可向mysql server写入正确的东八区时间字符串,读出后,Date对象的时间戳值保持一致
固定mysql server时区为GMT+8,serverTimezone=Asia/Shanghai时,mysql中放人时间2023-01-01 00:00:00.000
,当前电脑运行如下测试用例
@Test
void DateDiffTest() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Optional<DateTest> d1Op = dateTestRepository.findById(new Long(1));
Date d1 = d1Op.get().getCreateTime();
log.info("d1 is = {}, shown by str = {}", d1.getTime(), formatter.format(d1));
}
可以发现,字符串格式的时间不同,但时间戳保持一致
// 电脑时区设置为东八区
INFO main Method: DateDiffTest - d1 is = 1672502400000, shown by str = 2023-01-01 00:00:00.000
// 电脑时区设置为西五区,包含夏令时
INFO main Method: DateDiffTest - d1 is = 1672502400000, shown by str = 2022-12-31 11:00:00.000
总结
-
javascript
和java
中Date类的默认封装拆箱都可以获取到正确的时间,无需做特殊处理【因为用json传递数据,因此并不一定传递的真的是时间戳,也可能是带了时区信息的字符串,此处还未抓包求证,但总之可能正常传输】 - 多地区提供服务时,应该控制所有数据库为同一时区,并将jdbc连接的
serverTimezone
参数设置为与数据库时区一致,则不同时区的服务器都可以获取正确的日期数据
网友评论