Why OpenFeign Misinterprets Dates and How to Fix It
When converting Date parameters in OpenFeign calls, the client serializes them to strings using the local CST timezone, causing a 14‑hour discrepancy that can be resolved by using timestamps, @DateTimeFormat, or custom formatters on both client and server sides.
Introduction
During a migration from a monolithic to a micro‑service architecture, the project used OpenFeign for inter‑service calls. While development proceeded smoothly, passing a Date parameter caused unexpected time differences.
Problem Reproduction
Version information:
spring‑boot: 2.1.10.RELEASE
spring‑cloud: Greenwich.SR5
spring‑cloud‑alibaba: 2.1.2.RELEASE
Feign Service (Server)
application.yml
server:
port: 3001
spring:
application:
name: feign-service
# nacos config
cloud:
nacos:
discovery:
server-addr: localhost:8800Startup class
@SpringBootApplication
@EnableDiscoveryClient // register service to Nacos
@Slf4j
public class FeignServiceApplication {
public static void main(String[] args) {
SpringApplication.run(FeignServiceApplication.class, args);
log.info("feign服务已启动...");
}
}Controller
@RestController
@RequestMapping("/api")
@Slf4j
public class FeignServiceController {
@RequestMapping(value = "service/test", method = RequestMethod.GET)
public void feignDateTest(@RequestParam("date") Date date) {
log.info("传递过来的时间参数:{}", date);
}
}Feign Client
application.yml
server:
port: 3002
spring:
application:
name: client-service
# nacos config
cloud:
nacos:
discovery:
server-addr: localhost:8800Startup class
@SpringBootApplication
@EnableFeignClients // scan @FeignClient interfaces
@Slf4j
public class FeignClientApplication {
public static void main(String[] args) {
SpringApplication.run(FeignClientApplication.class, args);
}
}Controller
@RestController
@RequestMapping("/api")
@Slf4j
public class FeignClientController {
@Autowired
IFeignClient iFeignClient;
@RequestMapping(value = "test", method = RequestMethod.GET)
public void feignDateTest() {
Date date = new Date();
log.info("date参数:{}", date);
// call server
iFeignClient.feignDateTest(date);
}
}Running both services and invoking http://localhost:3002/api/test produced two different timestamps, differing by 14 hours.
Root Cause Analysis
OpenFeign converts Date objects to String before transmission. The default conversion uses the JVM’s default timezone. In China the timezone is CST (UTC+8), but the string "CTS" is ambiguous and the parser interprets it as Central Standard Time (UTC‑6), adding a 14‑hour offset.
During network communication only textual data can be transmitted, so Java’s Date must be formatted to a string (often using the ISO‑8601 pattern) before being sent. The server receives the string and constructs a new Date via the deprecated Date(String) constructor, which internally calls parse(String) . If no explicit timezone is supplied, the parser uses the local timezone, leading to the observed discrepancy.
The legacy parse(String) method relies on the timezone offset embedded in the string; because "CTS" is interpreted as the US Central time zone, the conversion adds 14 hours (UTC+8 vs UTC‑6).
Solutions
Solution 1: Use Timestamps or Plain Strings
Transmit the time as a long timestamp (milliseconds since epoch) or as a plain formatted string, and convert it to Date on the server side.
Solution 2: Use @DateTimeFormat
Annotate the method parameter with @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") so Spring parses the incoming string with the correct pattern and timezone.
// Client interface
@FeignClient(name = "feign-service")
public interface IFeignClient {
@RequestMapping(value = "/api/service/test", method = RequestMethod.GET)
void feignDateTest(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @RequestParam("date") Date date);
}
// Server controller
@RestController
@RequestMapping("/api")
@Slf4j
public class FeignServiceController {
@RequestMapping(value = "service/test", method = RequestMethod.GET)
public void feignDateTest(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @RequestParam("date") Date date) {
log.info("传递过来的时间参数:{}", date);
}
}Solution 3: Custom Date Formatter for Feign
Register a custom Converter that formats Date to a specific string on the client side and a matching converter that parses the string back to Date on the server side.
// Client side – register Date → String converter
@Component
public class DateFormatRegister implements FeignFormatterRegistrar {
@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addConverter(Date.class, String.class, new Date2StringConverter());
}
private static class Date2StringConverter implements Converter<Date, String> {
@Override
public String convert(Date source) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(source);
}
}
} // Server side – register String → Date converter
@Configuration
public class String2DateConfig {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
@PostConstruct
public void initEditableValidation() {
ConfigurableWebBindingInitializer initializer =
(ConfigurableWebBindingInitializer) handlerAdapter.getWebBindingInitializer();
if (initializer.getConversionService() != null) {
GenericConversionService conversionService =
(GenericConversionService) initializer.getConversionService();
conversionService.addConverter(String.class, Date.class, new String2DateConverter());
}
}
private static class String2DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(source);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
}
}After applying any of the above solutions, the timestamps from client and server match.
Now the time values are consistent and the issue is resolved.
Xuanwu Backend Tech Stack
Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
