美文网首页
网页应用的多时区日期格式的处理

网页应用的多时区日期格式的处理

作者: Lyudmilalala | 来源:发表于2023-06-06 17:39 被阅读0次
  1. 前端js的Date类型和后端SpringBoot的Date类型在网络传输中是自动基于UNIX时间戳封装的,因此只要对应使用这两种数据类型,就不会带来时区差异

  2. 无论js,java,python,在未指定时区的情况下,将Date类型的对象格式化为string时,均会使用机器本地的时区,可以通过修改电脑的时区,再格式化同一个时间戳的Date对象来测试

修改电脑时区前后对比.png

在将一个字符串转为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
  1. mysql的datetime类型保存格式为yyyy-MM-dd HH:mm:ss(如使用datetime(3)则为yyyy-MM-dd HH:mm:ss.SSS)的日期时间字符串,不带时区信息,保存值以mysql server的时区信息为准。因此如果有多台不同的mysql server,最好统一所有的时区为UTC
mysql datetime.png
  1. 通过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个小时,再存入了数据库

  1. 当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

总结

  1. javascriptjava中Date类的默认封装拆箱都可以获取到正确的时间,无需做特殊处理【因为用json传递数据,因此并不一定传递的真的是时间戳,也可能是带了时区信息的字符串,此处还未抓包求证,但总之可能正常传输】
  2. 多地区提供服务时,应该控制所有数据库为同一时区,并将jdbc连接的serverTimezone参数设置为与数据库时区一致,则不同时区的服务器都可以获取正确的日期数据

参考资料1

相关文章

  • 国际化(Internationalization)和本地化(Lo

    根据用户的本地语言,处理文本的输入和输出。 处理不同的日期,时间,数字显示格式 利用合适的日历和时区处理日期。 把...

  • JavaScript - 8.日期 Dates

    1 日期 Date ◆ 日期默认格式 ◆ 创建日期对象(4种方式) ◆ 显示日期 ◆ 时区 2 日期的格式

  • 【最佳实践】PHP日期时间和时区处理 API 及组件

    处理日期和时间需要考虑很多事情,例如日期的格式、时区、闰年和天数各异的月份,自己处理太容易出错了,我们应该使用PH...

  • NSDate

    创建时间: 时间增减: 时间戳: 日期比较: 设置日期格式: 获取所有时区名称: 设置时区: 将时区配置给date...

  • GoLang 日期时间格式化&反向日期时间

    日期格式化时区 获取当前时间并格式化 格式化后的日期字符串转回Time

  • 日期格式处理

    前端转化成 “yyyy-MM-dd HH:mm:ss” 格式 后端java:

  • 对日期格式的处理

    // 正则显示时间格式一天内一周内更早 + (NSString*) compareCurrentTime:(NSS...

  • NSDate

    目录:1、时区转换2、日期格式转换3、时间格式转换4、计算今天开始7天内日期、星期(今天,01-13|星期六,01...

  • PHP基础 —— 日期与时间

    日期与时间 PHP文档 : 日期与时间 设置时区 默认时区是UTC 文档中的 时区列表,使用时进行参照 设置时区 ...

  • 172、Excel日期数据处理及vlookup函数应用

    一、日期数据处理 其中【日期】这一列的格式是乱的,没有统一格式,接下来对这列的数据进行处理。 按日期的格式进行分列...

网友评论

      本文标题:网页应用的多时区日期格式的处理

      本文链接:https://www.haomeiwen.com/subject/fvniedtx.html