java日期时区问题

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测试情况

date_cdt_2.png

可以看到传入的是19911103,返回的却是1991-11-02,出现的日期错误的情况。

当时用的方案是给DTO加上时区配置,如下:

@Data
public class DateCDT {

    private String dateStr;

    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    private Date date;
}

重新测试

date_cdt_3.png

现在发现日期没有问题了。

问题再现

前面貌似解决了时区导致日期错乱问题,但是测试中发现还是会出现部分日期错乱问题。

date_cdt_1.png

这次通过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);
}

然后发现,结果又正常了。

date_cdt_4.png

问题又又又出现了。

结果过了一段时间,问题又出现了。

原文地址:https://www.cnblogs.com/jimmyfan/p/14090538.html