询问一个关于 Java 日期在数据库存储的格式问题

2022-07-12 16:20:36 +08:00
 MuXia

java 新人目前只在一家公司工作过

想问下各位,一般 Java 写后端的话,对于日期字段的处理,一般数据库存储的是什么格式的?

时间戳? yyyy-MM-dd HH:mm:ss 格式字符串? 还是其他的?

3357 次点击
所在节点    Java
32 条回复
siweipancc
2022-07-13 11:03:53 +08:00
@dcsuibian 最近搞智能硬件就用 long 存储了,还被同事吐槽,查个时间范围的数据还得手动转一次,我只能说开心就好
dcsuibian
2022-07-13 11:45:37 +08:00
@nothingistrue
@MuXia
时间戳是跟当前在什么时区无关的。
https://www.liaoxuefeng.com/article/978494994163392
dcsuibian
2022-07-13 11:50:46 +08:00
nothingistrue
2022-07-13 12:09:35 +08:00
@dcsuibian #21 并不是,你所谓的无关,其实是隐含了 UTC 0 。数值型的时间戳,都是基于 UNIX 时间戳,而 UNIX 时间戳的定义是:从 UTC 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总秒数。单独的一个数值,不是时间戳,带上时区,最起码要隐含 UTC 0 ,才是时间戳。

其实时间戳最大的问题,不是带时区,而是这个时区怎么带,根本没有统一规范。有的总是存储 UTC0 , 有的读写时跟随随环境变量(会发生因不同时区导致的读写不一致问题),有的将时区跟数值一起保存。
dcsuibian
2022-07-13 13:05:00 +08:00
@nothingistrue 不带时区的。你可以先正常运行一遍:
System.out.println(System.currentTimeMillis());
然后,换个时区再运行一次,你看看这两个数字差了多少就知道了。(毫秒)

时间戳只是针对某个时间点的偏移量。只不过这个时间点是 UTC 1970 年 1 月 1 日 0 时 0 分 0 秒
完全可以说时间戳是从 UTC+8 1970 年 1 月 1 日 8 时 0 分 0 秒 起至现在的秒数,这俩就是同一个时间点。
只不过挑一个基准时间点,总归要挑个规整点的罢了。
nothingistrue
2022-07-13 17:54:05 +08:00
@dcsuibian System.currentTimeMillis() 生成的是 UTC0 时间戳,隐式 UTC0 ,不代表没有 UTC 0 。这个区别很重要,因为有些工具生成的当前时间不是 UTC0 的。

试试 Mysql 下执行这个 SELECT CURRENT_TIMESTAMP(),LOCALTIMESTAMP(),UTC_TIMESTAMP(),NOW() FROM DUAL;

另外实际上只有 UNIX 时间戳才是从 UTC 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总秒数,ISO 8601 时间戳就是年月日时分表时区的组合。
dcsuibian
2022-07-13 21:18:21 +08:00
@nothingistrue MySQL 的 Timestamp 会在你 select 帮你格式化成文本帮助你阅读,格式化时就会用到时区信息,但底层的数字是没有的时区信息的。

对应的,你套上 UNIX_TIMESTAMP()函数,然后再看看。
SELECT
UNIX_TIMESTAMP(CURRENT_TIMESTAMP()),UNIX_TIMESTAMP(LOCALTIMESTAMP()),UNIX_TIMESTAMP(UTC_TIMESTAMP()),UNIX_TIMESTAMP(NOW())
FROM DUAL;

世界上各个时区的人在同一时间点调用 System.currentTimeMillis() 拿到的是同一个数字。
那用这个数字来表示时间点就不会因为时区、显示而产生歧义了啊
dcsuibian
2022-07-13 23:08:51 +08:00
@nothingistrue 扯远了。
回到用 long 存时间戳的问题上,假如现在有一台 MySQL (无论在哪儿),Java 程序 1 在北京,Java 程序 2 在纽约,它们都连接着这个数据库。

实验 1:
先是北京产生了一条记录,然后 10 分钟后纽约产生了一条记录。Java 程序都使用 System.currentTimeMillis()将得到的 long 数字存入数据库。那么这两条记录差的大概就是 10*60*1000 毫秒。无论你是否设置了 serverTimezone 参数,可测试。

实验 2:
使用如下 Java 程序插入一条新纪录。
String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC%2B8";
Connection conn = DriverManager.getConnection(url, "root", "password");
PreparedStatement stmt = conn.prepareStatement("INSERT INTO record(`time`) VALUES(?)");
Date date = new Date();
stmt.setObject(1, date);
stmt.execute();
stmt.close();
conn.close();
在保留和去除 serverTimezone=UTC%2B8 这个参数的情况下分别插入一条数据。那么你在数据库里看到的时间差别就很大。这是因为虽然 MySQL 底层虽然用了存整形的方法存时间戳。但你 insert 的时候仍然得用字符串:
INSERT INTO record(`time`) VALUES('2022-07-13 00:00:00')
这就有了借助时区的转换过程,然后就会有问题。
nothingistrue
2022-07-14 09:40:07 +08:00
@dcsuibian 你到现在还没发现问题吗,同一个 Unix 时间戳值,在读取 /显示的时候,不同时区是不一样的。

你局限在数值的不变上,但一个显示值随时区变化的数值,压根就不能成为数据,完整的数据,要是数值+时区。这就是数值型时间戳必须额外带时区,或者说数值型时间戳跟时区相关的原因。

你也局限在了 Java 上,java.util.Date 及其相关类的内部值,是存储的自 1970-1-1T00:00:00+0 到现在的毫秒数,但这只是 Java 的规范。Unix 时间戳不是国际标准,其他语言、数据库都可能定义自己的规范,比如有的语言会把时间戳定义为当前时区自 1970-1-1T00:00:00 到现在的毫秒数。结合使用的时候就容易出坑。
dcsuibian
2022-07-14 12:01:30 +08:00
@nothingistrue
ISO 是国际标准化组织,又不是只面向计算机从业者的,8601 是规定了显示方法。数据的存储和显示相分离的设计原则不是再正常不过了吗?
我一开始不就说了使用“毫秒”级时间戳。确实时间戳没有国际标准。各种语言都可以轻松地处理。
Python 的 time.time(),Java 的 System.currentTimeMills(),JavaScript 的 Date.now(),也就差一个 1000 处理。推荐使用“毫秒”时间戳只是因为整数比浮点更好处理罢了。
最重要是,处理过程中没有涉及到任何“时区”相关的东西?

这个数字只跟时间点有关。如果你没条件找个其它时区的人跟你一起试的话。至少换个系统时区再试试,关键是



你到底有没有试过?
dcsuibian
2022-07-14 12:14:04 +08:00
@nothingistrue
[1-中国标准时间.jpg]( https://dcsuibian-public-resources.oss-cn-hangzhou.aliyuncs.com/img/1-%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4.jpg)

[2-亚马逊标准时间.jpg]( https://dcsuibian-public-resources.oss-cn-hangzhou.aliyuncs.com/img/2-%E4%BA%9A%E9%A9%AC%E9%80%8A%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4.jpg)

你看看在切换时间后,时间戳这两个数字差了多少? 36438 毫秒,也就是 36.4 秒,就是我在第一个运行完、截图、改时区等操作花了一会儿而已。
dcsuibian
2022-07-14 12:21:46 +08:00
“有的语言会把时间戳定义为当前时区自 1970-1-1T00:00:00 到现在的毫秒数”
根本就没有语言这么做,再者说就算这么做了,也跟我时间戳(无论是毫秒还是秒)这个普遍概念没关系了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/865700

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX