MySQL中,日期、时间与时间戳三种数据类型的区别

在 MySQL 中存储日期和时间有几种不同的方法,想要选择正确的方式就需要大家理解要存储的数据类型以及 MySQL 如何处理这些数据类型。

这篇文章就带大家梳理一下MySQL的这些日期与时间的类型。

MySQL的五种时间数据类型

在MySQL中,提供了五种可以存储时间数据的列类型:

  • DATE
  • DATETIME
  • TIMESTAMP
  • YEAR
  • TIME

每种列类型存储的数据略有不同,具有不同的最小值和最大值,并且需要不同的存储空间大小。

下表显示了每种列类型及其相关属性:

列类型 数据类型 字节 最小值 最大值
DATE 仅日期 3 1000-01-01 9999-12-31
DATETIME 日期 + 时间 8 1000-01-01 00:00:00 9999-12-31 23:59:59
TIMESTAMP 日期 + 时间 4 1970-01-01 00:00:00 2038-01-19 03:14:17
YEAR 仅年份 1 1901 2155
TIME 仅时间 3 -838:59:59 838:59:59

下面,我们针对上述的数据类型,来进行相应的分析和讲解。

日期、年份和时间

上面的表中展示了五种类型,在实践的过程中,我们首先需要思考的一个问题就是:在此场景下,我需要存储什么样的数据?

DATE类型

如果只需要存储日期而无需时间信息,那么选择非常简单------使用 DATE 类型。例如,它适合存储生日、纪念日、员工入职日期等日期相关信息。

DATE 类型非常简洁,仅需要 3 字节即可存储从公元 1000 年到 9999 年范围内的大量日期。

YEAR类型

类似地,如果只需要存储年份而无需日期信息,可以使用 YEAR 类型。虽然这种列类型不常见,但当你确实需要它时,它也能有效地达成目标。

YEAR 类型非常紧凑,仅占用 1 字节。不过其范围较小,仅覆盖从 1901 年到 2155 年。如果你需要更大的范围,可以考虑使用带符号或无符号的小整数(SMALLINT)。

TIME类型

最后是 TIME 类型,用于存储时间(hh:mm:ss 格式)。它的有效范围比常规时间(00:00:0023:59:59)更广,因为它可以存储时间间隔和墙上时钟时间。其范围为 -838:59:59838:59:59,大约覆盖正负 35 天。

如果你只需要存储时间或持续时长而无需日期,TIME 类型非常适合。如果需要一起存储日期和时间,最好使用 DATETIMETIMESTAMP ,以一种逻辑单元存储它们,而不要分成两列。

TIMESTAMP 与 DATETIME 的合法范围

在MySQL中,DATETIMETIMESTAMP 类型都适合存储特定时间的引用。例如,如果需要存储 2029 年 2 月 14 日上午 8:47,可以使用任意一种列类型。

选择 DATETIME 或 TIMESTAMP 的依据就是具体的业务场景所需,两者本身没有优劣之分。

存储大小

TIMESTAMP 类型的存储大小为 4 字节 ,而 DATETIME 列类型为 8 字节。虽然存储空间并不昂贵,但设计表结构时,我们通常希望选择能够容纳所有数据范围的最小列类型。设计时无需"过度慷慨"。

由于 TIMESTAMP 的存储大小较小,其合法范围也相应较窄。

MySQL 中的 2038 问题

TIMESTAMP 的时间范围从 1970-01-01 00:00:002038-01-19 03:14:17。但在使用TIMESTAMP时,不可避免的会让我们想起著名的"2038 年问题",它也被标记为Y2038、Y2K38 或"Epochalypse"(时间纪元末日)。

2038 年问题源于计算 Unix 时间的系统。Unix 时间是从 1970 年 1 月 1 日 00:00:00 UTC(Unix 纪元)开始的经过的秒数,这些系统使用带符号的 32 位整数来存储时间。

带符号的 32 位整数只能存储范围为 -2^312^31 - 1 的值。能够准确表示的最大时间戳对应于 Unix 纪元后 2^31 - 1 秒,即 2038 年 1 月 19 日 03:14:07 UTC。

因此,如果需要存储 2038 年以后的日期,那就应当避免使用 TIMESTAMP,而应该选择 DATETIME。

对于当前应用,仍有许多场景可以使用 TIMESTAMP。例如,当记录动作发生的时间时,例如以下列:

  • updated_at
  • created_at
  • deleted_at
  • archived_at
  • posted_at

这些列通常由应用程序填充,只需记录事件发生的当前时间。在未来十多年内,TIMESTAMP 完全能够满足此类需求。即便到了 2038 年,相信也会有解决方案的。

何时使用 DATETIME

鉴于 TIMESTAMP 的合法值范围较窄,当需要存储 1970 至 2038 年范围之外的日期时间时,通常使用 DATETIME。

如果该字段是用户输入内容,可能需要选择 DATETIME 或验证输入内容是否在 TIMESTAMP 的范围内。

DATETIME 与 TIMESTAMP 的时区处理

除了存储大小和合法范围不同之外,DATETIME 与 TIMESTAMP 的另一个区别在于它们对时区的处理方式。时区对几乎所有开发者来说都是难题,而在 MySQL 中也不例外。

DATETIME 的时区处理

DATETIME 不会对时区进行任何处理。如果你向数据库写入一个值 2029-02-14 08:47,那么无论服务器或连接的时区如何设置,你始终会得到 2029-02-14 08:47

TIMESTAMP 的时区处理

TIMESTAMP 则会尝试"帮助你"处理时区。根据 MySQL 文档:

MySQL 会将 TIMESTAMP 类型的数据从当前时区转换为 UTC 存储,并在查询时从 UTC 转为当前时区。(这不会发生在其他类型,例如 DATETIME。)默认情况下,每个连接的当前时区是服务器的时区。

我们通过小示例验证这一点。首先创建包含 DATETIME 和 TIMESTAMP 列的表:

sql 复制代码
CREATE TABLE timezone_test (
    `timestamp` TIMESTAMP,
    `datetime` DATETIME
);

然后将连接时区设置为 UTC 并插入数据:

sql 复制代码
SET SESSION time_zone = '+00:00';
​
INSERT INTO timezone_test VALUES ('2029-02-14 08:47', '2029-02-14 08:47');
​
SELECT * FROM timezone_test;
​
-- | timestamp           | datetime            |
-- |---------------------|---------------------|
-- | 2029-02-14 08:47:00 | 2029-02-14 08:47:00 |

目前看起来不错,插入和读取的值完全一致。接下来我们将时区改为 -05:00 并再次查询表:

sql 复制代码
SET SESSION time_zone = '-05:00';
SELECT * FROM timezone_test;
​
-- | timestamp           | datetime            |
-- |---------------------|---------------------|
-- | 2029-02-14 03:47:00 | 2029-02-14 08:47:00 |

你会发现 TIMESTAMP 列被调整了 5 小时,而 DATETIME 列保持不变。

在实践中,如果服务器和连接时区总是设置为 UTC,这通常不会有问题。但如果不是这样,你可能会惊讶地发现存入数据库的值与取出的值不完全相同。

小结

本篇文章多个维度的对比,科普了一下MySQL中日期、时间和时间戳等数据类型在具体使用时的区别,在每个类型的具体实践中,还有许多最佳实践,在使用时可以尝试更进一步深入的了解。

相关推荐
小小工匠2 小时前
基于 Spring AI 的多平台多模型动态切换实战
后端
lang201509282 小时前
MySQL 8.0性能优化终极指南
数据库·mysql·性能优化
咸菜一世3 小时前
scala中class的使用
后端
豆浆Whisky3 小时前
反射还是代码生成?Go反射使用的边界与取舍|Go语言进阶(11)
后端·go
Penge6663 小时前
MySQL 分页优化
后端
苹果醋33 小时前
数据结构其一 线性表
java·运维·spring boot·mysql·nginx
Elastic 中国社区官方博客3 小时前
在 Elasticsearch 中改进 Agentic AI 工具的实验
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
华仔啊3 小时前
前后端防重复提交的 6 种落地实现:从按钮禁用到 AOP 全自动防护
java·后端