您建议使用datetime或timestamp字段吗?为什么(使用MySQL)?
我在服务器端使用PHP。
您建议使用datetime或timestamp字段吗?为什么(使用MySQL)?
我在服务器端使用PHP。
当前回答
我总是使用Unix时间戳,只是为了在处理大量日期时间信息时保持理智,尤其是在调整时区、添加/减去日期等时。在比较时间戳时,这排除了时区的复杂因素,并允许您在服务器端处理(无论是应用程序代码还是数据库查询)中节省资源,因为您使用了较轻的算术,而不是较重的日期时间加减函数。
另一件值得考虑的事情:
如果您正在构建一个应用程序,那么您永远不知道您的数据可能会被如何使用。如果你最终不得不将数据集中的一堆记录与第三方API中的一堆项目进行比较,然后按照时间顺序排列,你会很高兴为你的行设置Unix时间戳。即使您决定使用MySQL时间戳,也要存储Unix时间戳作为保险。
其他回答
主要区别在于
时间戳上的索引-有效日期时间上的索引-不起作用
看看这篇文章,看看Datetime索引的问题
参考本条:
主要区别:
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”
也不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分钟内购买的用户)超出了这里的范围。
以下示例显示了在将时区更改为“america/new_york”(DATETIME不变)后,TIMESTAMP日期类型如何更改值。
mysql> show variables like '%time_zone%';
+------------------+---------------------+
| Variable_name | Value |
+------------------+---------------------+
| system_time_zone | India Standard Time |
| time_zone | Asia/Calcutta |
+------------------+---------------------+
mysql> create table datedemo(
-> mydatetime datetime,
-> mytimestamp timestamp
-> );
mysql> insert into datedemo values ((now()),(now()));
mysql> select * from datedemo;
+---------------------+---------------------+
| mydatetime | mytimestamp |
+---------------------+---------------------+
| 2011-08-21 14:11:09 | 2011-08-21 14:11:09 |
+---------------------+---------------------+
mysql> set time_zone="america/new_york";
mysql> select * from datedemo;
+---------------------+---------------------+
| mydatetime | mytimestamp |
+---------------------+---------------------+
| 2011-08-21 14:11:09 | 2011-08-21 04:41:09 |
+---------------------+---------------------+
我已经把我的答案转换成了文章,这样更多的人可以发现这个有用的MySQL:日期时间与时间戳数据类型。
TIMESTAMP始终采用UTC格式(即自1970-01-01以来的秒数,采用UTC格式),MySQL服务器会自动将其转换为连接时区的日期/时间。从长远来看,TIMESTAMP是一条可行的道路,因为您知道您的时间数据将始终采用UTC格式。例如,如果您迁移到其他服务器或更改服务器上的时区设置,您就不会把日期搞砸。
注意:默认连接时区是服务器时区,但这可以(应该)在每个会话中更改(请参见SET time_zone=…)。