SpringBoot如何根據用戶系統時區動態展示時間
根據用戶系統時區動態展示時間
當我們使用SpringBoot+Mysql開發系統時,總是統一設置UTC+8時區,這樣用戶在任何地區訪問系統,展示的時間都是國內標準時間,體驗不友好,下面通過獲取當前用戶系統所在的時區,給用戶展示不同的時間。
一、用戶時區的獲取
我們可以通過JavaScript來獲取系統所在的時區,然後統一設置在請求頭裡。
Intl.DateTimeFormat().resolvedOptions().timeZone; // Asia/Shanghai
二、核心代碼
這裡統一使用LocalDateTime,更方便的處理時區轉換問題,通過標識當前LocalDateTime對象所屬時區,然後轉換為目標時區時間。
public LocalDateTime convertLocalDateTime(LocalDateTime localDateTime, ZoneId originZoneId, ZoneId targetZoneId) { return localDateTime.atZone(originZoneId).withZoneSameInstant(targetZoneId).toLocalDateTime(); }
三、SpringBoot返回json時統一處理時區
當程序從數據庫中讀取出並轉換成LocalDateTime對象,並經過業務邏輯處理,這時候該對象還是屬於UTC+8時區,對應的ZoneId=Asia/Shanghai,當需要返回給前端時,可以通過自定義jackson序列化器,在LocalDateTime轉json前轉換到用戶目標時區。
@Configuration public class JacksonConfiguration { @Autowired private JacksonProperties jacksonProperties; /** * 時區轉換 * * @param localDateTime * @param originZoneId * @param targetZoneId * @return */ public static LocalDateTime convertLocalDateTime(LocalDateTime localDateTime, ZoneId originZoneId, ZoneId targetZoneId) { return localDateTime.atZone(originZoneId).withZoneSameInstant(targetZoneId).toLocalDateTime(); } /** * LocalDateTime序列化 */ public static class CustomLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { private DateTimeFormatter formatter; public CustomLocalDateTimeSerializer(DateTimeFormatter formatter) { super(); this.formatter = formatter; } @Override public void serialize(LocalDateTime value, JsonGenerator generator, SerializerProvider provider) throws IOException { generator.writeString(convertLocalDateTime(value, ZoneId.of("Asia/Shanghai"), ZoneId.of("Africa/Sao_Tome")) .format(formatter)); } } /** * LocalDateTime反序列化 * */ public static class CustomLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { private DateTimeFormatter formatter; public CustomLocalDateTimeDeserializer(DateTimeFormatter formatter) { super(); this.formatter = formatter; } @Override public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JacksonException { return convertLocalDateTime(LocalDateTime.parse(parser.getText(), formatter), ZoneId.of("Africa/Sao_Tome"), ZoneId.of("Asia/Shanghai")); } } @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -> { builder.serializerByType(LocalDateTime.class, new CustomLocalDateTimeSerializer(DateTimeFormatter.ofPattern(jacksonProperties.getDateFormat()))); builder.deserializerByType(LocalDateTime.class, new CustomLocalDateTimeDeserializer(DateTimeFormatter.ofPattern(jacksonProperties.getDateFormat()))); }; } }
上面示例代碼設定用戶時區ZoneId=Africa/Sao_Tome,並且自定義處理瞭LocalDateTime反序列化器,當使用ResquestBody註解時,對象中的LocalDateTime屬性值也會轉換成UTC+8時區,不用再額外處理,可直接保存到數據庫。
四、SpringBoot接收時間參數統一處理時區
除瞭上面所說通過ResquestBody註解來接收參數外,還可能通過Get或者Post參數來接收LocalDateTime對象,這時候我們就要自定義一個Converter來處理String轉換到LocalDateTime,同時把用戶提交的屬於用戶時區的對象轉換成UTC+8時區對象。
@Configuration public class WebMvcConfiguration implements WebMvcConfigurer { @Autowired private WebMvcProperties webMvcProperties; @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new Converter<String, LocalDateTime>() { private LocalDateTime convertLocalDateTime(LocalDateTime localDateTime, ZoneId originZoneId, ZoneId targetZoneId) { return localDateTime.atZone(originZoneId).withZoneSameInstant(targetZoneId).toLocalDateTime(); } @Override public LocalDateTime convert(String source) { return convertLocalDateTime( LocalDateTime.parse(source, DateTimeFormatter.ofPattern(webMvcProperties.getFormat().getDateTime())), ZoneId.of("Africa/Sao_Tome"), ZoneId.of("Asia/Shanghai")); } }); }}
五、總結
通過上面的處理,JavaScript負責獲取用戶時區,並且每次請求時帶到後臺,後臺在接收請求和返回前端時統一轉換用戶時區,業務處理時不必再考慮時區問題。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Java8的DateTimeFormatter與SimpleDateFormat的區別詳解
- Java LocalDateTime常用操作方法
- JDK8時間相關類超詳細總結(含多個實例)
- Springboot 格式化LocalDateTime的方法
- Java 8 Time Api 使用方法技巧