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中日期、时间和时间戳等数据类型在具体使用时的区别,在每个类型的具体实践中,还有许多最佳实践,在使用时可以尝试更进一步深入的了解。

相关推荐
ruleslol4 小时前
MySQL的段、区、页、行 详解
数据库·mysql
天若有情6734 小时前
校园二手交易系统实战开发全记录(vue+SpringBoot+MySQL)
vue.js·spring boot·mysql
while(1){yan}4 小时前
MyBatis Generator
数据库·spring boot·java-ee·mybatis
奋进的芋圆4 小时前
DataSyncManager 详解与 Spring Boot 迁移指南
java·spring boot·后端
それども4 小时前
MySQL affectedRows 计算逻辑
数据库·mysql
是小章啊5 小时前
MySQL 之SQL 执行规则及索引详解
数据库·sql·mysql
计算机程序设计小李同学5 小时前
个人数据管理系统
java·vue.js·spring boot·后端·web安全
富士康质检员张全蛋5 小时前
JDBC 连接池
数据库
yangminlei5 小时前
集成Camunda到Spring Boot项目
数据库·oracle
Echo娴5 小时前
Spring的开发步骤
java·后端·spring