Spring CloudFeign transfers date type parameters with a time difference of 14 hours

Posted by timvw on Mon, 22 Jul 2019 05:50:49 +0200

I. Time difference of Java Date type

Look at the code below.

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());
}

The results are as follows.

date1: Mon Jul 22 08:47:19 CST 2019
date2: Mon Jul 22 22:47:19 CST 2019

The current time is 8:48 on July 22, 2019. CST is short for China Standard Time, but you can see that the input of date 2 is 14 hours more than the actual time.

CTS represents four time zones (Central Standard Time (USA) UT-6:00, Central Standard Time (Australia) UT+9:30, China Standard Time UT+8:00 and Cuba Standard Time UT-4:00), and represents the standard time of the United States, Australia, China and Cuba Standard Time UT-4:00.

Reason

new Date(date1.toString())

This method calls the Date.parse(String) method, which passes a parameter of Mon Jul 2208:47:19 CST 2019 with a comment on it.

* <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>

You can see that CST will be regarded as the Central Standard Time of the Central United States, that is, JVM thinks that the time you pass in is the time of the Central United States, and when date2 calls the toString method, it will detect that the time zone of the system is China, and it will automatically add 14 hours (the time difference between East 8 and West 6), which will become Mon Jul. 22 22:47:19 CST 2019

Solution

This problem is hard to solve if you write your own code, because all Java books don't teach it that way. Most of them use SimpleDateFormat to convert Date to String. After all, the new Date(date1.toString()) method has been labeled abandoned.

II. Feign Client Problems

When the Feign client communicates, it calls Date's toString method to String type. When the server accepts it, it uses the new Date(String) method. Here, the problem mentioned earlier will occur, resulting in a 14-hour time difference.

Solution

Adding code to the client specifies that Feign is converting the Date parameter to String parameter format:

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);
        }

    }
}

Add code on the server side to specify the converter used by SpringContext in String and Date so that the converter knows the format of the parameters we configure on the client side:

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;

    /**
     *  Increase the function of string date conversion.
     */
    @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;
        }
    }
}

Note that the above two configuration classes need to configure their own package scans or the like to add them to the Spring environment

 

Reference material:

Time Difference Problem of Spring Cloud Feign Transferring Date Type Parameters

Java Date Data Type Time Difference Problem

Topics: Programming Java Lombok Spring jvm