使用LocalDate和LocalDateTime接收日期时间
Java8推出了新的日期时间API位于java.time包下。在使用SpringBoot搭建前后端分离的项目提供Restful风格的Http接口时肯定会遇到提供日期时间查询的需求。因为Spring目前暂不支持自动转换为LocalDate或LocalDataTime所以需要进行配置以支持使用LocalDate或LocalDataTime来接收日期时间字段。项目演示创建名为14-time的maven项目pom文件如下?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-parent/artifactId version2.1.6.RELEASE/version /parent artifactId14-time/artifactId dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId /dependency /dependencies build plugins !-- 指定jdk -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId configuration source1.8/source target1.8/target encodingUTF-8/encoding /configuration /plugin /plugins /build /project1.编写App类package com.zccoder.demo.time; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 启动类 * * author zc 2019-08-30 */ SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }2.编写RequestBean类package com.zccoder.demo.time.domain; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; /** * 请求对象 * * author zc 2019-08-30 */ public class RequestBean implements Serializable { NotNull private LocalDate startDate; NotNull private LocalDateTime startTime; Override public String toString() { return RequestBean{ startDate startDate , startTime startTime }; } public LocalDate getStartDate() { return startDate; } public void setStartDate(LocalDate startDate) { this.startDate startDate; } public LocalDateTime getStartTime() { return startTime; } public void setStartTime(LocalDateTime startTime) { this.startTime startTime; } }3.编写ResponseBean类package com.zccoder.demo.time.domain; import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; /** * 响应对象 * * author zc 2019-08-30 */ public class ResponseBean implements Serializable { private LocalDate resultDate; private LocalDateTime resultTime; Override public String toString() { return ResponseBean{ resultDate resultDate , resultTime resultTime }; } public LocalDate getResultDate() { return resultDate; } public void setResultDate(LocalDate resultDate) { this.resultDate resultDate; } public LocalDateTime getResultTime() { return resultTime; } public void setResultTime(LocalDateTime resultTime) { this.resultTime resultTime; } }4.编写TimeController类package com.zccoder.demo.time.controller; import com.zccoder.demo.time.domain.RequestBean; import com.zccoder.demo.time.domain.ResponseBean; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; /** * 时间控制层 * * author zc 2019-08-30 */ Validated RestController RequestMapping(/time) public class TimeController { /** * 增加一天 * * param requestBean 请求对象 * return 响应对象 */ GetMapping public ResponseBean plusOneDay(Valid RequestBean requestBean) { ResponseBean responseBean new ResponseBean(); responseBean.setResultDate(requestBean.getStartDate().plusDays(1)); responseBean.setResultTime(requestBean.getStartTime().plusDays(1)); return responseBean; } }5.编写AppTestSupport类package com.zccoder.demo.time; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; /** * 单元测试基类 * * author zc 2019-08-30 */ RunWith(SpringRunner.class) SpringBootTest public abstract class AppTestSupport { Autowired private WebApplicationContext webApplicationContext; Autowired protected ObjectMapper objectMapper; protected MockMvc mockMvc; Before public void setUp() { mockMvc MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } }6.编写TimeControllerTest类package com.zccoder.demo.time.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.zccoder.demo.time.AppTestSupport; import com.zccoder.demo.time.domain.ResponseBean; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * 时间控制层测试类 * * author zc 2019-08-30 */ public class TimeControllerTest extends AppTestSupport { /** * 增加一天 */ Test public void plusOneDay() throws Exception { // 构建请求参数 LinkedMultiValueMapString, String params new LinkedMultiValueMap(4); params.add(startDate, 2019-08-30); params.add(startTime, 2019-08-30 22:36:20); // 执行调用请求 String response mockMvc.perform(get(/time) .contentType(MediaType.APPLICATION_JSON_UTF8) .params(params)) .andExpect(status().isOk()) .andReturn() .getResponse() .getContentAsString(); TypeReferenceResponseBean typeReference new TypeReferenceResponseBean() { }; ResponseBean responseBean objectMapper.readValue(response, typeReference); System.out.println(responseBean); } }执行TimeControllerTest类plusOneDay()测试方法由于Spring目前暂不支持自动转换为LocalDate或LocalDataTime测试失败。我们通过查看控制台日志发现如下输出Field error in object requestBean on field startDate: rejected value [2019-08-30]; codes [typeMismatch.requestBean.startDate,typeMismatch.startDate,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [requestBean.startDate,startDate]; arguments []; default message [startDate]]; default message [Failed to convert property value of type java.lang.String to required type java.time.LocalDate for property startDate; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value 2019-08-30; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2019-08-30]] Field error in object requestBean on field startTime: rejected value [2019-08-30 22:36:20]; codes [typeMismatch.requestBean.startTime,typeMismatch.startTime,typeMismatch.java.time.LocalDateTime,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [requestBean.startTime,startTime]; arguments []; default message [startTime]]; default message [Failed to convert property value of type java.lang.String to required type java.time.LocalDateTime for property startTime; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDateTime] for value 2019-08-30 22:36:20; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2019-08-30 22:36:20]]]通过分析日志找到原因是缺少转换字段类型为LocalDate或LocalDataTime的org.springframework.format.Formatter配置Formatter1.编写LocalDateFormatter类package com.zccoder.demo.time.config; import org.springframework.format.Formatter; import java.text.ParseException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * 日期格式化 * * author zc 2019-08-30 */ public class LocalDateFormatter implements FormatterLocalDate { private final DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyy-MM-dd); Override public LocalDate parse(String text, Locale locale) throws ParseException { return LocalDate.parse(text, formatter); } Override public String print(LocalDate localDate, Locale locale) { return formatter.format(localDate); } }2.编写LocalDateTimeFormatter类package com.zccoder.demo.time.config; import org.springframework.format.Formatter; import java.text.ParseException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * 日期时间格式化 * * author zc 2019-08-30 */ public class LocalDateTimeFormatter implements FormatterLocalDateTime { private final DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss); Override public LocalDateTime parse(String text, Locale locale) throws ParseException { return LocalDateTime.parse(text, formatter); } Override public String print(LocalDateTime localDateTime, Locale locale) { return formatter.format(localDateTime); } }3.编写MvcConfig类package com.zccoder.demo.time.config; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.time.LocalDate; import java.time.LocalDateTime; /** * MVC配置类 * * author zc 2019-08-30 */ Configuration public class MvcConfig implements WebMvcConfigurer { Override public void addFormatters(FormatterRegistry registry) { registry.addFormatterForFieldType(LocalDate.class, new LocalDateFormatter()); registry.addFormatterForFieldType(LocalDateTime.class, new LocalDateTimeFormatter()); } }4.再次执行TimeControllerTest类plusOneDay()测试方法测试通过。