SpringCloudFeign傳輸date型別引數,時間差14個小時
一、Java Date型別的時差問題
請看下邊這段程式碼
public static void main(String[] args) throws Exception {
Date date1 = new Date();
System.out.println("date1: " + date1.toString());
Date date2 = new Date(date1.toString());
System.out.println("date2: " + date2.toString());
}
執行結果如下
date1: Mon Jul 22 08:47:19 CST 2019 date2: Mon Jul 22 22:47:19 CST 2019
當前時間是2019年7月22日8點48分,CST是中國的時區China Standard Time的簡稱,但是可以看到date2的輸入比實際時間多了14個小時。
CTS代表的時區其實有四個(Central Standard Time (USA) UT-6:00、Central Standard Time (Australia) UT+9:30、China Standard Time UT+8:00、Cuba Standard Time UT-4:00),同時表示美國,澳大利亞,中國,古巴四個國家的標準時間。
原因
new Date(date1.toString())
這個方法會呼叫Date.parse(String)方法,它傳的引數是Mon Jul 22 08:47:19 CST 2019,這個方法上有一段註釋
* <li>Any word that matches <tt>EST, CST, MST</tt>, or <tt>PST</tt>, * ignoring case, is recognized as referring to the time zone in * North America that is five, six, seven, or eight hours west of * Greenwich, respectively. Any word that matches <tt>EDT, CDT, * MDT</tt>, or <tt>PDT</tt>, ignoring case, is recognized as * referring to the same time zone, respectively, during daylight * saving time.</ul><p>
可以看到CST會被當作美國中部的時區Central Standard Time,即JVM認為你傳入的時間是美國中部的時間,而當date2呼叫toString方法的時候,它會檢測到系統的時區是中國,就會自動加14個小時(東八區與西六區的時差),就變成了Mon Jul 22 22:47:19 CST 2019
解決方法
這個問題其實如果自己寫程式碼的話很難出現,因為所有的Java書籍都不會這麼教,大多數都是通過SimpleDateFormat,進行Date和String的轉換,畢竟new Date(date1.toString())這個方法已經標註為廢棄了
二、Feign客戶端的問題
Feign客戶端在進行通訊時,會呼叫Date的toString方法轉為String型別,服務端在接受的時候,使用的就是new Date(String)這個方法,這裡就會發生前邊介紹的問題,產生14個小時的時差
解決方法
在客戶端新增程式碼,規定Feign在將Date引數轉化成String引數的格式:
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FeignFormatterRegistrar;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Slf4j
@Component
public class FeignDateFormatRegister implements FeignFormatterRegistrar {
@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addConverter(Date.class, String.class, new Date2StringConverter());
}
private class Date2StringConverter implements Converter<Date, String> {
@Override
public String convert(Date source) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(source);
}
}
}
在服務端新增程式碼,規定SpringContext在String和Date時的用的轉化器,讓轉化器知道我們在客戶端配置的引數格式:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import javax.annotation.PostConstruct;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Slf4j
@Configuration
public class FeignConfiguration {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
/**
* 增加字串轉日期的功能
*/
@PostConstruct
public void initEditableValidation() {
ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) handlerAdapter.getWebBindingInitializer();
if (initializer.getConversionService() != null) {
GenericConversionService genericConversionService = (GenericConversionService) initializer.getConversionService();
genericConversionService.addConverter(String.class, Date.class, new String2DateConverter());
}
}
class String2DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
log.error("", e);
}
return null;
}
}
}
注意以上兩個配置類需要自己配置包掃描之類的把它們加到Spring環境中
參考資料: