Java时区问题小记
问题是在公司项目中出现的,为了简单描述问题,我简单写了下demo,只描述问题部分。
基本情况
项目为微服务架构,我们的服务A接收第三方的报文(字符串)。解析后,传入自己的服务B,服务B在校验身份证和出生日期的时候。提示“出生日期和身份证号不符”。检查发现是生日字段出现问题。当时推断为时区问题,导致时间错位,日期减一。
时区问题初现
一开始项目代码中没做时区配置。唯一一处是数据库链接url上(这里有用,后面再说)
jdbc:mysql://127.0.0.1:3306/date_cdt?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
首先创建一个项目中DTO对象
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
@Data
public class DateCDT {
/**
* 这里用字符串接收postman传入的日期信息,然后手动解析,保存到date字段。
*/
private String dateStr;
/**
* 一开始只配置了日期格式,未配置时区
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date date;
}
这里模拟服务A和服务B。
服务A端口:9090
服务B端口:9091
服务A的Controller:
import com.itlaonong.demo.datecdt.client.LastDateClient;
import com.itlaonong.demo.datecdt.dto.DateCDT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
@RequestMapping("/date")
public class DateController {
@Autowired
private LastDateClient lastDateClient;
@PostMapping
public DateCDT test(@RequestBody DateCDT dateCDT) throws ParseException {
String dateStr = dateCDT.getDateStr();
//第一步获取日期字符串转成Date对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
Date date = simpleDateFormat.parse(dateStr);
dateCDT.setDate(date);
// 转换后调用服务B
return lastDateClient.test(dateCDT);
}
}
服务B的Controller:
import com.itlaonong.demo.datecdt.dao.DateTestRepository;
import com.itlaonong.demo.datecdt.dto.DateCDT;
import com.itlaonong.demo.datecdt.entity.DateTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
@RequestMapping("/date")
public class DateController {
@Autowired
private DateTestRepository dateTestRepository;
@PostMapping
public DateCDT test(@RequestBody DateCDT dateCDT) {
//转成内部的EO后保存数据库
Date date = dateCDT.getDate();
DateTest dateTest = new DateTest();
dateTest.setDate(date);
dateTest = dateTestRepository.save(dateTest);
dateCDT.setDate(dateTest.getDate());
//这里原先为调用其他服务进行校验,现在直接返回,可以通过postman中结果查看
return dateCDT;
}
}
postman测试情况
可以看到传入的是19911103,返回的却是1991-11-02,出现的日期错误的情况。
当时用的方案是给DTO加上时区配置,如下:
@Data
public class DateCDT {
private String dateStr;
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date date;
}
重新测试
现在发现日期没有问题了。
问题再现
前面貌似解决了时区导致日期错乱问题,但是测试中发现还是会出现部分日期错乱问题。
这次通过debug代码,发现出问题的日期 我们在服务A解析字符串转换成Date对象时时区显示为CDT,正常的为CST。查找资料发现,这个是夏令时问题,我国在1986-1991年期间实行过夏令时。
网上寻找解决办法,找到这篇博客。
采用博客中的方案,我给启动类加入了如下代码:
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
于是启动代码为:
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
SpringApplication.run(Application.class, args);
}
然后发现,结果又正常了。
问题又又又出现了。
结果过了一段时间,问题又出现了。