SpringBoot如何根據(jù)用戶系統(tǒng)時(shí)區(qū)動(dòng)態(tài)展示時(shí)間
根據(jù)用戶系統(tǒng)時(shí)區(qū)動(dòng)態(tài)展示時(shí)間
當(dāng)我們使用SpringBoot+Mysql開發(fā)系統(tǒng)時(shí),總是統(tǒng)一設(shè)置UTC+8時(shí)區(qū),這樣用戶在任何地區(qū)訪問系統(tǒng),展示的時(shí)間都是國(guó)內(nèi)標(biāo)準(zhǔn)時(shí)間,體驗(yàn)不友好,下面通過獲取當(dāng)前用戶系統(tǒng)所在的時(shí)區(qū),給用戶展示不同的時(shí)間。
一、用戶時(shí)區(qū)的獲取
我們可以通過JavaScript來(lái)獲取系統(tǒng)所在的時(shí)區(qū),然后統(tǒng)一設(shè)置在請(qǐng)求頭里。
Intl.DateTimeFormat().resolvedOptions().timeZone; // Asia/Shanghai
二、核心代碼
這里統(tǒng)一使用LocalDateTime,更方便的處理時(shí)區(qū)轉(zhuǎn)換問題,通過標(biāo)識(shí)當(dāng)前LocalDateTime對(duì)象所屬時(shí)區(qū),然后轉(zhuǎn)換為目標(biāo)時(shí)區(qū)時(shí)間。
public LocalDateTime convertLocalDateTime(LocalDateTime localDateTime, ZoneId originZoneId, ZoneId targetZoneId) { return localDateTime.atZone(originZoneId).withZoneSameInstant(targetZoneId).toLocalDateTime(); }
三、SpringBoot返回json時(shí)統(tǒng)一處理時(shí)區(qū)
當(dāng)程序從數(shù)據(jù)庫(kù)中讀取出并轉(zhuǎn)換成LocalDateTime對(duì)象,并經(jīng)過業(yè)務(wù)邏輯處理,這時(shí)候該對(duì)象還是屬于UTC+8時(shí)區(qū),對(duì)應(yīng)的ZoneId=Asia/Shanghai,當(dāng)需要返回給前端時(shí),可以通過自定義jackson序列化器,在LocalDateTime轉(zhuǎn)json前轉(zhuǎn)換到用戶目標(biāo)時(shí)區(qū)。
@Configuration public class JacksonConfiguration { @Autowired private JacksonProperties jacksonProperties; /** * 時(shí)區(qū)轉(zhuǎn)換 * * @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()))); }; } }
上面示例代碼設(shè)定用戶時(shí)區(qū)ZoneId=Africa/Sao_Tome,并且自定義處理了LocalDateTime反序列化器,當(dāng)使用ResquestBody注解時(shí),對(duì)象中的LocalDateTime屬性值也會(huì)轉(zhuǎn)換成UTC+8時(shí)區(qū),不用再額外處理,可直接保存到數(shù)據(jù)庫(kù)。
四、SpringBoot接收時(shí)間參數(shù)統(tǒng)一處理時(shí)區(qū)
除了上面所說(shuō)通過ResquestBody注解來(lái)接收參數(shù)外,還可能通過Get或者Post參數(shù)來(lái)接收LocalDateTime對(duì)象,這時(shí)候我們就要自定義一個(gè)Converter來(lái)處理String轉(zhuǎn)換到LocalDateTime,同時(shí)把用戶提交的屬于用戶時(shí)區(qū)的對(duì)象轉(zhuǎn)換成UTC+8時(shí)區(qū)對(duì)象。
@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")); } }); }}
五、總結(jié)
通過上面的處理,JavaScript負(fù)責(zé)獲取用戶時(shí)區(qū),并且每次請(qǐng)求時(shí)帶到后臺(tái),后臺(tái)在接收請(qǐng)求和返回前端時(shí)統(tǒng)一轉(zhuǎn)換用戶時(shí)區(qū),業(yè)務(wù)處理時(shí)不必再考慮時(shí)區(qū)問題。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java線程中的Thread.yield()詳細(xì)解析
這篇文章主要介紹了Java線程中的Thread.yield()詳細(xì)解析,yield()讓當(dāng)前線程從運(yùn)行狀態(tài)?轉(zhuǎn)為?就緒狀態(tài),以允許具有相同優(yōu)先級(jí)的其他線程獲得運(yùn)行機(jī)會(huì),需要的朋友可以參考下2023-11-11MyBatis中如何查詢某個(gè)時(shí)間段內(nèi)的數(shù)據(jù)
這篇文章主要介紹了MyBatis中如何查詢某個(gè)時(shí)間段內(nèi)的數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08tk.Mybatis 插入數(shù)據(jù)獲取Id問題
本文主要介紹了tk.Mybatis 插入數(shù)據(jù)獲取Id問題,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12@JsonSerialize(using = LongToStringUtil.class)注解的使
這篇文章主要介紹了@JsonSerialize(using = LongToStringUtil.class)注解的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08SpringCloud Zuul過濾器和谷歌Gauva實(shí)現(xiàn)限流
這篇文章主要介紹了SpringCloud Zuul過濾器和谷歌Gauva實(shí)現(xiàn)限流,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03