一、简介
通常与前端互相传递时间相关参数,一般为String或Date 类型,而DTO中一般用LocalDateTime处理,这样会发生多次时间格式或类型转换,产生冗余代码。故可以通过全局AOP实现前端所传String类型时间自动映射转化为LocalDateTime类型,后端处理后也自动映射LocalDateTime为String 返回前段。
其中对于PostgreSQL数据库,不需要额外配置,可以直接在Entity类中定义LocalDateTime类型,通过JPA直接保存或查询时间映射为LocalDateTime类型,对于MySQL可能数据库支持的Date转LocalDateTime需要额外的全局配置,可查阅资料解决。
二、时间转换类的配置
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
@Configuration
public class DateConfig {
/** 默认日期时间格式 */
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/** 默认日期格式 */
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
/** 默认时间格式 */
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
/**
* LocalDate转换器,用于转换RequestParam和PathVariable参数
*/
@Bean
public Converter localDateConverter() {
return new Converter() {
@Override
public LocalDate convert(@NotNull String source) {
return LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
}
};
}
/**
* LocalDateTime转换器,用于转换RequestParam和PathVariable参数
*/
@Bean
public Converter localDateTimeConverter() {
return new Converter() {
@Override
public LocalDateTime convert(@NotNull String source) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT));
}
};
}
/**
* LocalTime转换器,用于转换RequestParam和PathVariable参数
*/
@Bean
public Converter localTimeConverter() {
return new Converter() {
@Override
public LocalTime convert(@NotNull String source) {
return LocalTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));
}
};
}
/**
* Date转换器,用于转换RequestParam和PathVariable参数
*/
@Bean
public Converter dateConverter() {
return new Converter() {
@Override
public Date convert(@NotNull String source) {
SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
try {
return format.parse(source);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
};
}
/**
* Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json
*/
@Bean
public ObjectMapper objectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
//LocalDateTime系列序列化和反序列化模块,继承自jsr310,我们在这里修改了日期格式
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//Date序列化和反序列化
javaTimeModule.addSerializer(Date.class, new JsonSerializer() {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
SimpleDateFormat formatter = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
String formattedDate = formatter.format(date);
jsonGenerator.writeString(formattedDate);
}
});
javaTimeModule.addDeserializer(Date.class, new JsonDeserializer() {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
String date = jsonParser.getText();
try {
return format.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
});
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
}
通用时间工具类DateTimeUtil
import org.apache.commons.lang3.StringUtils;
import javax.validation.constraints.NotNull;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class DateTimeUtil {
private static final String YMDHMS_DATE_FORMAT_T = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
private static final String ISO8601_DATE_FORMAT_T = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
private static final String YMDHMS_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String YMDHMS_DATE_FORMAT_V1 = "yyyy/MM/dd HH:mm:ss";
private static final String CSV_DATE_FORMAT = "yyyyMMddHHmmss";
private static final String YMD_DATE_FORMAT = "yyyy-MM-dd";
private static final String YYYYMMDDHHMMSS_DATE_FORMATE= "yyyyMMddHHmmssSSS";
/*** 1毫秒 */
public static final long ONE_MILLI_SECOND = 1l;
/*** 1秒 */
public static final long ONE_SECOND = ONE_MILLI_SECOND * 1000;
/*** 1分钟 */
public static final long ONE_MINUTE = ONE_SECOND * 60;
/*** 1小时 */
public static final long ONE_HOUR = ONE_MINUTE * 60;
/*** 1天 */
public static final long ONE_DAY = ONE_HOUR * 24;
/**
* 根据日期时间字符串获取日期时间
* @param datetime
* @param format 为null或者空时默认为yyyy-MM-dd HH:mm:ss
* @return
*/
public static LocalDateTime parseByFormat(@NotNull String datetime,String format) {
if (StringUtils.isEmpty(format)) {
format = "yyyy-MM-dd HH:mm:ss";
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
return LocalDateTime.parse(datetime,formatter);
}
/**
* 获取指定格式的日期字符串
* @param dateTime
* @param format 为null或者空时默认为yyyy-MM-dd HH:mm:ss
* @return
*/
public static String formatDateTime(@NotNull LocalDateTime dateTime, String format) {
//时间转字符串格式化
if (StringUtils.isEmpty(format)) {
format = "yyyy-MM-dd HH:mm:ss";
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format, Locale.SIMPLIFIED_CHINESE);
return dateTime.format(formatter);
}
/**
* LocalDateTime 转为 Date
* @param localDateTime
* @return
*/
public static Date localDateTimeToDate(LocalDateTime localDateTime){
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
* Date 转 LocalDateTime
* @param date
* @return
*/
public static LocalDateTime dateToLocalDateTime(Date date){
return LocalDateTime.ofInstant(date.toInstant(),ZoneId.systemDefault());
}
/**
* 根据时间获取时间戳
* @param dateTime
* @return
*/
public static Long dateTimeToTimestamp(LocalDateTime dateTime) {
if (dateTime == null) {
return null;
}
return dateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
}
/**
* 根据时间戳获取日期时间
* @param timestamp
* @return
*/
public static LocalDateTime timestampToDateTime(Long timestamp) {
return timestamp==null?null:LocalDateTime.ofInstant( Instant.ofEpochMilli(timestamp),ZoneId.systemDefault());
}
/**
* 获取当日开始时间0点
*/
public static LocalDateTime todayStartTime(){
return LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
}
/**
* 获取当日结束时间0点
* @return
*/
public static LocalDateTime todayEndTime(){
return LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
}
public static void main(String[] args) {
Date date = new Date();
String datStr = LocalDateTime.now().toString();
LocalDateTime localDateTime = DateTimeUtil.dateToLocalDateTime(date);
Long longSc = DateTimeUtil.dateTimeToTimestamp(localDateTime);
LocalDateTime localDateTimeStamp = DateTimeUtil.timestampToDateTime(longSc);
String dateToStr = DateTimeUtil.formatDateTime(localDateTime, YMDHMS_DATE_FORMAT_V1);
Date date1 = DateTimeUtil.localDateTimeToDate(localDateTime);
System.out.println("today startTime : " + localDateTimeToDate(DateTimeUtil.todayStartTime()));
System.out.println("today endTime : " + localDateTimeToDate(DateTimeUtil.todayEndTime()));
System.out.println(parseByFormat("2018-12-07 09:50:33",null));
String isoTime = "2019-06-26T19:00:35.823+08:00";
LocalDateTime localDateTime1 = DateTimeUtil.parseByFormat(isoTime, DateTimeUtil.ISO8601_DATE_FORMAT_T);
System.out.println("iso Time :" + localDateTime1);
String isoTime1 = LocalDateTime.now().toString() + "+08:00";
LocalDateTime localDateTime2 = DateTimeUtil.parseByFormat(isoTime1, DateTimeUtil.ISO8601_DATE_FORMAT_T);
System.out.println("str iso time : " + isoTime1);
}
public static String getGMTTime() {
Calendar c = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("EEE d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
return sdf.format(c.getTime());
}
}
三、 测试验证
构造测试数据TimeInfoQuery.java 和 TimeInfoVO.java
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @Author: zhenglei12
* @Data: Created in 2019/7/2 15:15
* @Description:
*/
@Data
public class TimeInfoQuery {
private String message;
@ApiModelProperty(value="时间,格式:yyyy-MM-dd HH:mm:ss", hidden=false, notes="格式:yyyy-MM-dd HH:mm:ss", dataType="String")
private LocalDateTime startTime;
}
TimeInfoVO.java
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
/**
* @Author: zhenglei12
* @Data: Created in 2019/7/2 15:14
* @Description:
*/
@Data
public class TimeInfoVO {
private String message;
@ApiModelProperty(value="时间,格式:yyyy-MM-dd HH:mm:ss", hidden=false, notes="格式:yyyy-MM-dd HH:mm:ss", dataType="String")
@DateTimeFormat
private LocalDateTime startTime;
}
TimeInfoRepository
import com.hikvision.fsd.ums.core.model.entity.TimeInfoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
/**
* @Author: zhenglei12
* @Data: Created in 2019/7/2 15:43
* @Description:
*/
@Repository
public interface TimeInfoRepository extends JpaRepository, JpaSpecificationExecutor {
@Query("select u from TimeInfoEntity u where u.id=:id")
TimeInfoEntity findById(@Param("id") Integer id);
}
controller类测试:
import com.***.fsd.ums.core.model.entity.TimeInfoEntity;
import com.***.fsd.ums.core.model.query.TimeInfoQuery;
import com.***.fsd.ums.core.model.vo.TimeInfoVO;
import com.***.fsd.ums.core.repository.TimeInfoRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
/**
* @Author: zhenglei12
* @Data: Created in 2019/7/2 14:57
* @Description:
*/
@RestController
@Api(tags = "时间测试")
public class DateController {
@Autowired
private TimeInfoRepository timeInfoRepository;
@Autowired
private MapperFactory mapperFactory;
@Autowired
private MapperFacade mapperFacade;
@GetMapping("/getDate")
@ApiOperation(value = "获取时间")
public LocalDateTime getDate(@RequestParam LocalDate date,
@RequestParam LocalDateTime dateTime,
@RequestParam Date originalDate) {
System.out.println(date);
System.out.println(dateTime);
System.out.println(originalDate);
return LocalDateTime.now();
}
@PostMapping("/postDateInfo")
@ApiOperation(value = "上传时间信息")
public TimeInfoVO saveTimeInfo(@RequestBody TimeInfoQuery query){
TimeInfoVO vo = new TimeInfoVO();
vo.setMessage("new Time");
vo.setStartTime(LocalDateTime.now());
TimeInfoEntity entity = new TimeInfoEntity();
entity.setMessage(query.getMessage());
entity.setStartTime(query.getStartTime());
timeInfoRepository.save(entity);
return vo;
}
@PostMapping("/getLocalTimeInfoById")
@ApiOperation(value = "根据id获取时间信息")
public TimeInfoVO getLocalTimeInfoById(@RequestParam("id")Integer id){
TimeInfoVO vo =new TimeInfoVO();
TimeInfoEntity entity = timeInfoRepository.findById(id);
vo = mapperFacade.map(entity, TimeInfoVO.class);
return vo;
}
}
测试结果: