时间戳在接口、代码和数据库类型选用

时间戳在接口、代码和数据库分别应该用什么类型

首先用一个故事先讲下时区的概念

比如 在英国(假设时区是+00:00);在中国(时区是+08:00);在日本(时区是+09:00),在2020年10月10号凌晨0点发了条Twitter动态,早上8点钟刚起床刚好看到发的动态,而在日本的9点整刚好已经到公司也刷到的动态。这3个人的事件都在发生同一时刻,但是3人各自的本地时间都不同……
的这条个人动态记录会存储在推特服务器上,服务器肯定不可能为3人分别存不同的时间戳,它可能以相对于本机的时区的普通时间格式来存,也可能是以带时区信息的时间格式来存的:

假设服务器在美国(时区是-07:00),存的时间可能有以下几种情况:

  1. 2020-10-09T17:00:00 相对于本机时区来存
  2. 2020-10-10T00:00:00Z UTC时区,Z代表0时区
  3. 2020-10-09T17:00:00-07:00 带上了时区信息

Oracle数据库一般只能存第1种,如果是PostgreSQL可以用timestamptz类型

无论服务器内部以什么格式保存,在给客户端返回的接口数据里一般都不应该用服务器自己本机时区的时间;简单的做法就是接口数据里统一都用带时区的ISO8601标准日期时间格式,然后各个客户端那边它们自己根据用户设备的时区来格式化,比如JSON数据里是2020-10-09T17:00:00-07:00的App里格式化显示的是2020年10月10日8:00点

Java的DTO类里时间戳字段一般以java.time.OffsetDateTime类型,在序列化成JSON时一般的库都会自动转成ISO8601格式;而实体类通常跟DTO作自动映射,所以时间戳属性也会用OffsetDateTime类型,无论数据库字段类型是否支持时区

2020-10-10T00:00:00Z2020-10-09T17:00:00-07:00这2个值虽然不在一个时区,但它们都是同一个时间点,是相等的

前端在格式化日期的时候,不要直接用substring/split之类的方法来截取,应该用dayjs这样的库;比如2020-10-09T17:00:00-07:00直接截取是2020-10-09,实际应该是2020-10-10

查询某一天的数据

1
2
3
4
5
6
7
8
9
ZoneOffset zoneOffset = ZoneOffset.ofHours(+8); // 可以从配置文件里读或者固定代码写死——假设系统用户只针对国内用户

var date = LocalDate.of(2020, Month.OCTOBER, 21); // 取某一天的日期,日期跟时间戳不同,没有时区概念,“几几年几月几号”都是相对日期 Java这边用LocalDate这个类

// 查某一天数据实际上是查一个范围,0点0分0秒到23点59分59秒;并且需要把LocalDate转化成绝对时间来比较
var startOfDate = OffsetDateTime.of(date, LocalTime.MIN, zoneOffset); // 2020-10-21T00:00+08:00
var endOfDate = OffsetDateTime.of(date, LocalTime.MAX, zoneOffset); // 2020-10-21T23:59:59.999999999+08:00

// 用startOfDate < entity.timestamp < endOfDate这个条件来进行查询,假设entity.timestamp属性是OffsetDateTime类型

时间戳在接口、代码和数据库类型选用
http://blog.jingxiang.ltd/2023/06/06/时间戳在接口、代码和数据库类型区分/
作者
yemangran
发布于
2023年6月6日
许可协议