时区问题
前提认知
日期的显示与接收
简单来说,时间是long数字,加上时区才有意义。
时区问题(数据库的默认时区问题,数据库连接配置,前台显示的时区问题(jackson的配置),date转string的时区问题(系统的时区设置)
https://www.cnblogs.com/suiyueshentou/p/7798340.html
https://www.cnblogs.com/ganbo/p/11244306.html
这个时区问题,主要出现的问题是,时区设置的问题,时区可以设置的地方有点多。
你有没有想过,为什么new date(),不同的地区,时间显示不同,其实也很简单,new date()里面拿到的其实就是1970年到现在的毫秒数,
而我们获取时间,显示都是毫秒转化过来,时区也不过是规定了转化的规则,这个new date的毫秒数是没被改变的,
那默认的时区从哪里读取的呢,从系统变量里读取的,也就是说你的new date()如果没错的话,大家的值都是一样的,但是因为设置的时区不同,导致显示不同。
第一点:数据库的时区,数据库的date类型,其实也是一个long类型的数字,然后转化成日期
但是数据库有个默认时区设置,然后让你连接到数据库可以查看数据库的时间
第二点:连接设置的时区,有些系统,说我存储到你的数据库的时区设置,我不用你的,你用我的,那你可以在你的连接上加上时区。
jdbc:mysql://192.168.0.206:3307/skf_ito?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
这个意思就是说,我存数据库的时区是这样的。你爱怎么显示怎么显示,但是我传给你的date数据,是这个时区的
所以如果你的数据库时区是GMT的,然后你的连接是GMT+8的,那么你就会很奇怪,我传给数据库的时间是12-20 8:00 然后我用命令行去看发现,数据成12-20 00:00.但是我代码里的时间确是12-20 8:00.别担心,这个才是正常的。
第三点,序列化的时候的时区
在传输的时候,你序列化的时候,如果指定时区的时区不一致
你可能就发现差了几个小时。
这个也很好理解,你序列化后给我,可能就是long的数据,但是我要解析啊,long数字不能代表一个时间,你不指定,那我用我系统时区了啊。
但是如果两个系统时间不一致呢,那问题就来了。
第三点,date 转string的时区问题
我date只是long的数字,只有加上时间,我才知道你这个date指的是哪天,我才能给你string。
之前没指定也可以转化,都是系统变量的锅,帮你做了。
所以,stirng转date和date转string都是要指定时区的。
改变系统变量的时区,
要么linux设置时区(百度linux修改时区,linux里date命令可以看时间,看自己的时区)
要么jar包启动时候传入时区(sudo nohup java -Xms256m -Xmx512m -jar -Duser.timezone=GMT+08 $project --spring.profiles.active=test server.port= p o r t > > port >> port>>log_dir/$logname &)
数据库存入的时间这个读取的是jvm的时区设置,
解决方法是要么设置jvm的时区,也就是启动的时候设置时区,或者代码里固定写死启动的时候时区设置为哪个,
第二个问题是json显示的问题,数据库存的是date的类型,然后vo类也是date类的话,json没有date的类型数据,要转字符串,规则是什么?dae转json的话,spring会有默认的jason的设置时区,我们可以在yml设置时区。
https://www.cnblogs.com/konghou/p/4118770.html
https://www.cnblogs.com/suiyueshentou/p/7798340.html
https://blog.csdn.net/jiahao1186/article/details/97892583
https://blog.csdn.net/qq_42031483/article/details/100625564
问题排错
这天,发现一个bug,具体是这样的。服务A要传信息给服务B.然后发现服务A的时间到了服务B是时间差了一天。这是咋回事。
服务A读取用户输入的时间,存储到数据库,然后转信息的时候,读取数据库的时间,转json给服务b。服务b存储到数据库,然后显示到前台。
先看数据库
发现服务A用的数据库连接配置是GMT+8,然后服务B的数据库连接配置是GMT。
再看序列化传输
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date opiDate;
服务A和服务B都没指定。
再看显示的时区指定
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date opiDate;
服务B没指定
思考
服务A的数据是取一天,yyyy-MM-dd,所以他的时间存储到数据库可能就是12-20 00:00 ,然后他数据库连接的时区是GMT+8。
但是序列化传递给服务B的时候,没指定时区,那就是默认的时区。
这个时候也没问题,服务A传递给服务B的是,GMT+8 的12-20 00:00。
服务B序列化解析,得到时间,哦,时间是12-20 00:00.但是时区呢,不管了,就用我的GMT吧。
那就是GMT 12-19 16:00 .
但是我要的格式是yyyy-MM-dd。那你就是GMT 12-19 00:00
但是我的数据库连接是GMT,那你存到数据库的时间就是 GMT 的12-19 00:00
我读出来,也是GMT 的12-19 00:00
显示到前端(date转String),也用我默认的GMT的吧,12-19.
就这样,华丽丽的用户输入12-20 ,然后服务B就变成了12-19。
解决方案
指定序列化传输的时候的时区。
dto里的字段。
/** opi精准交期 只有货期查询转报价单的时候存在**/
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date opiDate;
服务A指定序列化的时区,服务B反序列化的时候,也指定时区
演示
package com.xy.ito.product.tool.util;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* @description:
* @author: wzy
* @create: 2020-12-20 16:04
**/
@Data
public class TestA {
/** opi精准交期 只有货期查询转报价单的时候存在**/
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date opiDate;
}
package com.xy.ito.product.tool.util;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* @description:
* @author: wzy
* @create: 2020-12-20 16:05
**/
@Data
public class TestB {
/** opi精准交期 只有货期查询转报价单的时候存在**/
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date opiDate;
}
public static void main(String[] args) throws ParseException {
SimpleDateFormat simpleDateFormat0 = new SimpleDateFormat("yyyy-MM-dd");
Date date = simpleDateFormat0.parse("2020-12-20");
TestA a = new TestA();
a.setOpiDate(date);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
System.out.println(simpleDateFormat.format(a.getOpiDate()));
Gson gson =new Gson();
TestB testB = gson.fromJson(gson.toJson(a), TestB.class);
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd");
simpleDateFormat2.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(simpleDateFormat2.format(testB.getOpiDate()));
}