最近有一個關於店鋪數據實時分析的需求,須要實時統計店鋪當天的數據:例如訪客數,瀏覽量、商品排行榜等。因爲店鋪能夠自主選擇店鋪所在時區(全球二十四個時區),而數倉統計後落庫的時間是GMT+8時區對應的UNIX時間戳。所以,在咱們調用中臺的接口時,不能直接取服務器的UNIX時間戳做爲傳參。java
這麼一聽,若是以前沒深刻過UNIX時間戳
與時區
的概念,可能你們都會有點懵逼了;其實我也是,特別是我是昨天晚上十點多才接收到這個信息,心裏就更加慌張了,畢竟本來昨天就要提測了,如今由於這個時間戳的緣由而推遲了。下面咱們先來了解UNIX時間戳和時區的概念,而後再繼續討論這個問題。服務器
UNIX時間戳:從1970年1月1日(UTC/GMT的午夜)開始所通過的秒數,不考慮閏秒。
也就是指格林威治時間1970年01月01日00時00分00秒開始到如今的總秒數。.net
對的,你們能夠看到,其實UNIX時間戳說的是秒數,而一般咱們講的是毫秒~unix
時區:爲了克服時間上的混亂,1884年在華盛頓召開的一次國際經度會議(又稱國際子午線會議)上,規定將全球劃分爲24個時區(東、西各12個時區)。規定英國(格林尼治天文臺舊址)爲中時區(零時區)、東1—12區,西1—12區。每一個時區橫跨經度15度,時間正好是1小時。最後的東、西第12區各跨經度7.5度,以東、西經180度爲界。每一個時區的中央經線上的時間就是這個時區內統一採用的時間,稱爲區時,相鄰兩個時區的時間相差1小時。code
例如:中國所在東8區的時間總比莫斯科所在東3區的時間多5個小時。blog
看完上面兩個定義,咱們能夠得出結論:時間戳是沒有時區之分的,僅僅是日期展現和時區有關係;同一個時間戳,在不一樣時區,顯示的日期是不同的。接口
因此上面的需求,若是直接用服務器所在的時間戳來做爲查詢時的時間傳參,那麼通常都是行不通的;除非店鋪的時區和咱們服務器的時區是同樣的(容器中的時區都是GMT+8,也就是東八區),否則店鋪的時間和服務器的時間是有可能不同的。get
假設咱們如今根據北京時間 2021-01-12 03:00:00,獲取到對應的時間戳是:1610391600000 (毫秒),
而這個時間戳對應的莫斯科時間爲:2021-01-11 22:00:00,這顯然和東八區與東三區的時差(五個小時)是對得上的。class
很明顯,上面的例子中,同一UNIX時間戳,不一樣時區的時間時不同的,甚至存在兩時區不在同一日;那至於上面的問題也就隨之被解答了,查詢店鋪的實時數據,那必需要拿到店鋪所在時區的當前時間了。容器
關於展現同一UNIX時間戳兩個時區的時間區別,能夠看下面代碼:
/*** * 對比同一時間戳,不一樣時區的時間顯示 * @author winfun * @param sourceTimezone sourceTimezone * @param targetTimezone targetTimezone * @return {@link Void } **/ public static void compareTimeByTimezone(String sourceTimezone,String targetTimezone){ // 當前時間服務器UNIX時間戳 Long timestamp = System.currentTimeMillis(); // 獲取源時區時間 Instant instant = Instant.ofEpochMilli(timestamp); ZoneId sourceZoneId = ZoneId.of(sourceTimezone); LocalDateTime sourceDateTime = LocalDateTime.ofInstant(instant,sourceZoneId); // 獲取目標時區時間 ZoneId targetZoneId = ZoneId.of(targetTimezone); LocalDateTime targetDateTime = LocalDateTime.ofInstant(instant,targetZoneId); System.out.println("The timestamp is "+timestamp+",The DateTime of Timezone{"+sourceTimezone+"} is "+sourceDateTime+ ",The " + "DateTime of Timezone{"+targetTimezone+"} is "+targetDateTime); }
其中一次的執行結果:
The timestamp is 1610594585422,The DateTime of Timezone{Europe/Moscow} is 2021-01-13 06:23:05.422,The DateTime of Timezone{Asia/Shanghai} is 2021-01-13 11:23:05.422
到此,咱們應該能夠將時間戳和時區很好地區分出來了。
上面已經很好地分析了UNIX時間戳與時區了,接下來繼續咱們的需求分析~
若是隻是拿店鋪所在時區的當前時間,其實很是簡單,咱們能夠利用 LocalDateTime#now(ZoneId zone) 便可。
但是我上面的需求,到這一步還沒夠,由於數倉保存的是GMT+8時區對應的UNIX時間戳;因此當咱們拿到店鋪所在時區的時間後,還須要轉爲GMT+8時區對應的UNIX時間戳。
因爲咱們是直接查詢當天,咱們能夠簡化爲獲取店鋪當前的零點零分和23點59分,而後轉爲GMT+8時區對應的時間戳;最後,利用 JDK8 中的 LocalDateTime 能夠很是簡單的完成。
雖然咱們最後須要的是GMT+8時區的時間戳,可是爲了使得方法更加通用,參數分別爲源時區和目標時區,能夠兼容更多的使用場景。
/*** * 獲取源時區的當前日期的零點零分,轉爲目標時區對應的時間戳 * @author winfun * @param sourceTimezone 源時區 * @param targetTimezone 目標時區 * @return {@link Void } **/ public static void getStartTimeFromSourceTimezoneAndConvertTimestampToTargetTimezone(String sourceTimezone,String targetTimezone){ // 獲取指定時區的當前時間 ZoneId sourceZoneId = ZoneId.of(sourceTimezone); LocalDateTime dateTime = LocalDateTime.now(sourceZoneId); LocalDate date = LocalDate.now(sourceZoneId); // 獲取上面時間的當天0點0分 LocalDateTime startTime = LocalDateTime.of(date, LocalTime.MIN); // 轉成目標時區對應的時間戳 ZoneId targetZoneId = ZoneId.of(targetTimezone); ZoneOffset targetZoneOffset = targetZoneId.getRules().getOffset(dateTime); Long gmt8Timestamp = startTime.toInstant(targetZoneOffset).toEpochMilli(); System.out.println("The Date of Timezone{"+sourceTimezone+"} is " + date + ",Thd StartTime of Timezone{"+sourceTimezone+ "} is,"+ startTime + ",convert to Timezone{"+targetTimezone+"} timestamp is "+gmt8Timestamp); } /*** * 獲取源時區的當前日期的23點59分,轉爲目標時區對應的時間戳 * @author winfun * @param sourceTimezone 源時區 * @param targetTimezone 目標時區 * @return {@link Void } **/ public static void getEndTimeFromSourceTimezoneAndConvertTimestampToTargetTimezone(String sourceTimezone,String targetTimezone){ // 獲取指定時區的當前時間 ZoneId sourceZoneId = ZoneId.of(sourceTimezone); LocalDateTime dateTime = LocalDateTime.now(sourceZoneId); LocalDate date = LocalDate.now(sourceZoneId); // 獲取上面時間的當天23點59分 LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX); // 轉成目標時區對應的時間戳 ZoneId targetZoneId = ZoneId.of(targetTimezone); ZoneOffset targetZoneOffset = targetZoneId.getRules().getOffset(dateTime); Long gmt8Timestamp = endTime.toInstant(targetZoneOffset).toEpochMilli(); System.out.println("The Date of Timezone{"+sourceTimezone+"} is " + date + ",The EndTime of Timezone{"+sourceTimezone+ "} is"+ endTime + ", convert to Timezone{"+targetTimezone+"} timestamp is "+gmt8Timestamp); }
其中一次執行結果:
The Date of Timezone{Europe/Moscow} is 2021-01-14,Thd StartTime of Timezone{Europe/Moscow} is,2021-01-14T00:00,convert to Timezone{Asia/Shanghai} timestamp is 1610553600000 The Date of Timezone{Europe/Moscow} is 2021-01-14,The EndTime of Timezone{Europe/Moscow} is2021-01-14T23:59:59.999999999, convert to Timezone{Asia/Shanghai} timestamp is 1610639999999
固然,其餘場景不必定就是拿當天的開始時間和結束時間,有可能僅僅是根據源時區當前時間獲取目標時區對應的時間戳。
這個也是很是簡單,直接看下面代碼便可:
/*** * 獲取源時區的當前時間,轉爲目標時區對應的時間戳 * @author winfun * @param sourceTimezone 源時區 * @param targetTimezone 目標時區 * @return {@link Void } **/ public static void getTimeFromSourceTimezoneAndConvertToTargetTimezoneToTargetTimezone(String sourceTimezone,String targetTimezone){ // 獲取指定時區的當前時間 ZoneId sourceZoneId = ZoneId.of(sourceTimezone); LocalDateTime dateTime = LocalDateTime.now(sourceZoneId); /** * 轉成指定時區對應的時間戳 * 一、根據zoneId獲取zoneOffset * 二、利用zoneOffset轉成時間戳 */ ZoneId targetZoneId = ZoneId.of(targetTimezone); ZoneOffset offset = targetZoneId.getRules().getOffset(dateTime); Long timestamp = dateTime.toInstant(offset).toEpochMilli(); System.out.println("The DateTime of Timezone{"+sourceTimezone+"} is " + dateTime + ",convert to Timezone{"+targetTimezone+"} timestamp is "+timestamp); }
其中一次執行結果:
The DateTime of Timezone{Europe/Moscow} is 2021-01-14T06:23:05.486,convert to Timezone{Asia/Shanghai} timestamp is 1610576585486
到此,此次驚險的UNIX時間戳與時區的旅行就到此結束了,但願你們也能從此次分享中獲得有用的信息~