一、背景:

最近有一个关于店铺数据实时分析的需求,需要实时统计店铺当天的数据:例如访客数,浏览量、商品排行榜等。由于店铺可以自主选择店铺所在时区(全球二十四个时区),而数仓统计后落库的时间是GMT+8时区对应的UNIX时间戳。因此,在我们调用中台的接口时,不能直接取服务器的UNIX时间戳作为传参。

这么一听,如果之前没深入过UNIX时间戳时区的概念,可能大家都会有点懵逼了;其实我也是,特别是我是昨天晚上十点多才接收到这个信息,内心就更加慌张了,毕竟原本昨天就要提测了,现在因为这个时间戳的原因而推迟了。下面我们先来了解UNIX时间戳和时区的概念,然后再继续讨论这个问题。

二、概念:

UNIX时间戳:从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
也就是指格林威治时间1970年01月01日00时00分00秒开始到现在的总秒数。

对的,大家可以看到,其实UNIX时间戳说的是秒数,而通常我们讲的是毫秒~

时区:为了克服时间上的混乱,1884年在华盛顿召开的一次国际经度会议(又称国际子午线会议)上,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1—12区,西1—12区。每个时区横跨经度15度,时间正好是1小时。最后的东、西第12区各跨经度7.5度,以东、西经180度为界。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时,相邻两个时区的时间相差1小时。

例如:中国所在东8区的时间总比莫斯科所在东3区的时间多5个小时。

看完上面两个定义,我们可以得出结论:时间戳是没有时区之分的,仅仅是日期展示和时区有关系;同一个时间戳,在不同时区,显示的日期是不一样的。

所以上面的需求,如果直接用服务器所在的时间戳来作为查询时的时间传参,那么一般都是行不通的;除非店铺的时区和我们服务器的时区是一样的(容器中的时区都是GMT+8,也就是东八区),不然店铺的时间和服务器的时间是有可能不一样的。

例子:

假设我们现在根据北京时间 2021-01-12 03:00:00,获取到对应的时间戳是:1610391600000 (毫秒),
而这个时间戳对应的莫斯科时间为:2021-01-11 22:00:00,这显然和东八区与东三区的时差(五个小时)是对得上的。

很明显,上面的例子中,同一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);/*** 转成指定时区对应的时间戳* 1、根据zoneId获取zoneOffset* 2、利用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时间戳与时区的旅行就到此结束了,希望大家也能从这次分享中得到有用的信息~

如何利用JDK8彻底弄懂UNIX时间戳与时区的概念!相关推荐

  1. GMTUTC,UNIX时间戳,时区

    GMT和UTC GMT,即格林尼治标准时间,也就是世界时.GMT的正午是指当太阳横穿格林尼治子午线(本初子午线)时的时间.但由于地球自转不均匀不规则,导致GMT不精确,现在已经不再作为世界标准时间使用 ...

  2. linux元年时间搓,发布基于ANSI-C的RTC_Time库,利用UNIX时间戳格式,无中断实现万年历...

    基于STM32处理器 RTC只是个能靠电池维持运行的32位定时器over! 并不像实时时钟芯片,读出来就是年月日... 看过些网上的代码,有利用秒中断,在内存中维持一个年月日的日历. 我觉得,这种方法 ...

  3. 5分钟搞懂计算机的各种时间(GMT、UTC、CST、unix时间戳-timestamp)

    转自:https://baijiahao.baidu.com/s?id=1732139602203523267&wfr=spider&for=pc GMT 即:格林尼治时间(另有格林威 ...

  4. 【转载】万字详文彻底弄懂TCP协议:从三次握手和四次挥手说起

    今日头条 腾讯技术工程 作者:morganhuang,腾讯 IEG 后台开发工程师 说到 TCP 协议,相信大家都比较熟悉了,对于 TCP 协议总能说个一二三来,但是 TCP 协议又是一个非常复杂的协 ...

  5. 程序员,想要彻底弄懂Redis,这15点你一定要明白~(纯干货)

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:Java实现QQ登录和微博登录个人原创+1博客:点击前往,查看更多 作者:耿直的小码农 来源:https://s ...

  6. 彻底弄懂Python标准库源码(一)—— os模块

    目录 第1~22行 模块整体注释.nt与posix 第24~46行 模块引入._exists方法._get_exports_list方法 第48~97行 根据系统不同导入不同的方法和属性 第100~1 ...

  7. Unix时间戳(Unix timestamp)及其他时间标准

    以下内容摘自:http://blog.hehehehehe.cn/a/15592.htm Unix时间戳(Unix timestamp),或称Unix时间(Unix time).POSIX时间(POS ...

  8. 这个女生说:弄懂本文前,你所知道的区块链可能都是错的

    整个区块链行业的凛冽寒冬中,价格的涨跌已经左右了太多的人头脑之中的理智.可是,众人之中,究竟有几个人真正理解了区块链技术的密码学机制与分布式计算?究竟有几个人还会关心区块链在技术上的创新? 尘归尘,土 ...

  9. 《繁凡的深度学习笔记》前言、目录大纲 一文让你完全弄懂深度学习所有基础(DL笔记整理系列)

    <繁凡的深度学习笔记>前言.目录大纲 (DL笔记整理系列) 一文弄懂深度学习所有基础 ! 3043331995@qq.com https://fanfansann.blog.csdn.ne ...

最新文章

  1. php 的 危 险 参 数
  2. vs2015 企业版、专业版如何破解(秘钥)
  3. extern C的主要作用简单解释
  4. 转盘抽奖php,使用PHP实现转盘抽奖算法案例解析
  5. 这些Java代码优化细节,你需要注意!
  6. Linux mysql 登录 2002,Linux 下 Mysql error 2002 错误解决
  7. 『设计模式』Web程序开发最基本的编程模式--MVC编程模式
  8. 在nocdb转pdb的时候遇到小bug
  9. java se是不是java_Java SE和java EE究竟有什么实质上的区别
  10. 剑指offer最新版_剑指offer第二版速查表
  11. php xml 怎么去掉头,PHP如何删除xml某条数据
  12. java编程思想学习笔记——第2章 一切都是对象
  13. 2021 软考 软件设计师考试教程(详细版)
  14. Angular 字符串替换
  15. MAC版SecureCRT+SecureFX 安装说明
  16. 吴式太极拳的特点-和基础要求
  17. 阿里巴巴-1688-退款退货明细下载(导出)
  18. java设计模式 之 模板方法模式
  19. Cache与主存的地址映像
  20. 计算机设置了密码后不能打印了,win7系统共享打印机设置密码后无法连接如何解决...

热门文章

  1. Navicat 连接数据库报错:1045-Access denied for user ‘root‘@‘localhos
  2. [licode cs交互] 4 erizo controller对 android client鉴权通过
  3. Linux脚本开头#!/bin/bash和#!/bin/sh是什么意思以及区别
  4. java framemaker教程_Freemarker入门案例
  5. 机遇与危机,婚庆行业的转型之路
  6. Nginx配置文件目录实现pdf文件预览及下载
  7. windows和linux环境下的嵌入式开发区别
  8. Grafana使用双Y坐标轴详解
  9. 如何从Gitlab上拉取代码
  10. Espresso指南一(Espresso下载、安装、设置、基础、速查表)