您建议使用datetime或timestamp字段吗?为什么(使用MySQL)?

我在服务器端使用PHP。


当前回答

这取决于应用程序。

考虑由用户在纽约的服务器上设置时间戳,以便在桑海进行约会。现在,当用户在桑海连接时,他从东京的镜像服务器访问相同的预约时间戳。他将在东京时间看到这一任命,而不是原定的纽约时间。

因此,对于表示用户时间(如约会或日程)的值,datetime更好。它允许用户控制所需的确切日期和时间,而不考虑服务器设置。设置时间是设置时间,不受服务器时区、用户时区或夏时制计算方式变化的影响(是的,它会变化)。

另一方面,对于表示系统时间的值,如支付事务、表修改或日志记录,始终使用时间戳。将服务器移动到另一个时区或在不同时区的服务器之间进行比较时,系统不会受到影响。

时间戳在数据库上也更轻,索引速度更快。

其他回答

也不DATETIME和TIMESTAMP类型在一般用例中基本上是断开的。MySQL将在未来改变它们。您应该使用BIGINT和UNIX时间戳,除非您有特定的理由使用其他时间戳。

特殊情况

以下是一些具体情况,在这些情况下,您的选择更容易,您不需要在这个答案中进行分析和提出一般建议。

仅限日期-如果您只关心日期(如下一个农历新年的日期,2022-02-01),并且您清楚了解该日期适用的时区(或不关心,如农历新年),则使用Date列类型。记录插入时间-如果您正在记录数据库中的行的插入日期/时间,并且您不担心应用程序在未来17年内会崩溃,那么继续使用默认值为CURRENT_TIMESTAMP()的TIMESTAMP。

为什么TIMESTAMP坏了?

TIMESTAMP类型以UTC时区存储在磁盘上。这意味着,如果您实际移动服务器,它不会损坏。很好✅. 但目前定义的时间戳将在2038年完全停止工作❌.

每次INSERT INTO或SELECT FROM TIMESTAMP列时,都会考虑客户端/应用程序服务器的物理位置(即时区配置)。如果移动应用程序服务器,则日期会中断❌.

(更新2022-04-29 MySQL在8.0.28中修复了这一问题,但如果您的生产环境位于CentOS 7或许多其他风格,那么您的迁移路径将需要很长时间才能获得此支持。)

为什么VARCHAR坏了?

VARCHAR类型允许以ISO8601格式明确地存储非本地日期/时间/两者,并且适用于2037年以后的日期。通常使用祖鲁时间,但ISO 8601允许对任何偏移进行编码。这不太有用,因为尽管MySQL日期和时间函数支持字符串作为输入,但如果输入使用时区偏移,则结果不正确。

VARCHAR还使用额外的存储字节。

为什么DATETIME中断?

DATETIME在同一列中存储DATE和TIME。除非时区被理解,并且时区没有存储在任何地方,否则这两个东西都没有任何意义❌. 您应该将预期的时区作为注释放在列中,因为时区与数据有着千丝万缕的联系。所以很少有人使用列注释,所以这是等待发生的错误。我从亚利桑那州继承了一台服务器,所以我总是需要将所有时间戳从亚利桑那州时间转换为另一个时间。

(更新2021-12-08我在多年的正常运行时间后重新启动了服务器,数据库客户端(带升级)重置为UTC。这意味着我的应用程序需要以不同的方式处理重置前后的日期。硬代码!)

DATETIME唯一正确的情况是完成以下句子:

您的2020年太阳新年正好从DATETIME(“2020-01-01 00:00:00”)开始。

DATETIMEs没有其他好的用途。也许你会想象一个特拉华州市政府的网络服务器。当然,这台服务器和所有访问这台服务器的人的时区都可以暗示在特拉华州,东部时区,对吧?错误的在这个千年里,我们都认为服务器存在于“云”中。因此,将您的服务器放在任何特定时区都是错误的,因为您的服务器总有一天会被移动。

注意:MySQL现在支持DATETIME文本中的时区偏移(谢谢@Marko)。这可能会使插入DATETIMEs更方便,但并不能解决数据的不完整和无用的含义,这一致命问题确定(“❌“)。

如何使用BIGINT?

定义:

CREATE TEMPORARY TABLE good_times (
    a_time BIGINT
)

插入特定值:

INSERT INTO good_times VALUES (
    UNIX_TIMESTAMP(CONVERT_TZ("2014-12-03 12:24:54", '+00:00', @@global.time_zone))
);

插入默认值(thx Brad):

ALTER TABLE good_times MODIFY a_time BIGINT DEFAULT (UNIX_TIMESTAMP());

或者,当然,这在你的应用程序中要好得多,比如:

$statement = $myDB->prepare('INSERT INTO good_times VALUES (?)');
$statement->execute([$someTime->getTimestamp()]);

选择:

SELECT a_time FROM good_times;

有一些过滤相对时间的技术(选择过去30天内的帖子,查找在注册后10分钟内购买的用户)超出了这里的范围。

在使用MySQL和PHP时,我总是使用Unix时间戳。这主要是因为PHP中的默认日期方法使用时间戳作为参数,因此不需要解析。

要获取PHP中当前的Unix时间戳,只需执行time();在MySQL中执行SELECT UNIX_TIMESTAMP();。

DATETIME类型用于包含日期和时间部分的值。MySQL以格式检索并显示DATETIME值。支持的范围为。'YYYY-MM-DD hh:MM:ss''1000-01-01 00:00:00''9999-12-31 23:59:59'

TIMESTAMP数据类型用于包含日期和时间部分的值。TIMESTAMP的范围从“1970-01-01 00:00:01”UTC到“2038-01-19 03:14:07”UTC。

mysql> SELECT col,
     >     CAST(col AT TIME ZONE INTERVAL '+00:00' AS DATETIME) AS ut
     >     FROM ts ORDER BY id;
+---------------------+---------------------+
| col                 | ut                  |
+---------------------+---------------------+
| 2020-01-01 10:10:10 | 2020-01-01 15:10:10 |
| 2019-12-31 23:40:10 | 2020-01-01 04:40:10 |
| 2020-01-01 13:10:10 | 2020-01-01 18:10:10 |
| 2020-01-01 10:10:10 | 2020-01-01 15:10:10 |
| 2020-01-01 04:40:10 | 2020-01-01 09:40:10 |
| 2020-01-01 18:10:10 | 2020-01-01 23:10:10 |
+---------------------+---------------------+

URL MySQL 8.0:https://dev.mysql.com/doc/refman/8.0/en/datetime.html

DATETIME不携带时区信息,并且将始终显示与会话有效的时区无关的时区,除非您明确更改它,否则默认为服务器的时区。但是,如果我使用NOW()之类的函数而不是“2020-01-16 12:15:00”之类的文字来初始化DATETIME列,是本地化到会话时区的当前日期和时间。

相比之下,TIMESTAMP确实隐式地携带时区信息:当您使用值初始化TIMESTAMP列时,该值在存储之前会转换为UTC。如果存储的值是文本,例如“2020-01-16 12:15:00”,则出于转换目的,它被解释为在会话的当前时区中。相反,当显示TIMESTAMP列时,它将首先从UTC转换为会话的当前时区。

什么时候使用一个或另一个?案例研究

一个社区剧团的网站正在播放几场正在售票的戏剧演出。这些演出的日期和时间将显示在下拉列表中,希望购买演出门票的客户将从中选择一个。数据库列performance_date_and_time为DATETIME类型是有意义的。如果演出地点在纽约,则有一种理解,即有一个隐含的时区(“纽约当地时间”),理想情况下,我们希望日期和时间显示为“2019年12月12日晚上8点”,而不考虑会议的时区,也不必麻烦进行任何时区转换。

另一方面,一旦2019年12月12日晚上8点的演出开始,我们可能不再想出售门票,因此不再在下拉列表中显示该演出。因此,我们想知道“2019-12-12 20:00:00”是否已发生。这将证明有一个TIMESTAMP列,将会话的时区设置为“America/New_York”,并将会话time_zone设置为“American/New_约克”,然后将“2019-12-12 20:00:00”存储到TIMESTAMP栏中。从此,我们可以通过独立于当前会话时区将此列与NOW()进行比较来测试性能是否已开始。

或者,为这两个单独的目的设置DATETIME和TIMESTAMP列可能是有意义的。或者不是。显然,任何一个都可以达到这两个目的。如果只使用DATETIME列,则在与NOW()进行比较之前,必须将当前时区设置为本地时区。如果只使用TIMESTAMP列,则必须在显示该列之前将会话时区设置为本地时区。

参考本条:

主要区别:

TIMESTAMP用于跟踪记录的更改,并在每次更改记录时进行更新。DATETIME用于存储不受记录更改影响的特定静态值。

TIMESTAMP也受不同时区相关设置的影响。DATETIME是常量。

TIMESTAMP在内部将当前时区转换为UTC进行存储,并在检索期间转换回当前时区。DATETIME无法执行此操作。

TIMESTAMP支持的范围:1970-01-01 00:00:01 UTC至2038-01-19 03:14:07 UTCDATETIME支持的范围:“1000-01-01 00:00:00”至“9999-12-31 23:59:59”