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);
}
然后發現,結果又正常了。
問題又又又出現了。
結果過了一段時間,問題又出現了。