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

我在服务器端使用PHP。


当前回答

在MySQL 5及以上版本中,TIMESTAMP值从当前时区转换为UTC进行存储,并从UTC转换回当前时区进行检索。(这仅适用于TIMESTAMP数据类型,而不适用于DATETIME等其他类型。)

默认情况下,每个连接的当前时区是服务器的时间。可以根据每个连接设置时区,如MySQL Server时区支持中所述。

其他回答

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为4字节,DATETIME为8字节。

http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html

但正如斯克罗尼德所说,它的下限是1970年。不过,这对未来可能发生的任何事情都很好;)

我建议不要使用DATETIME或TIMESTAMP字段。如果你想将一个特定的日子作为一个整体来表示(比如生日),那么就使用DATE类型,但是如果你要更具体一些,你可能会对记录一个实际的时刻感兴趣,而不是一个时间单位(日、周、月、年)。不要使用DATETIME或TIMESTAMP,而是使用BIGINT,只需存储自epoch以来的毫秒数(如果使用Java,则为System.currentTimeMillis())。这有几个优点:

避免供应商锁定。几乎每个数据库都以相对类似的方式支持整数。假设您想移动到另一个数据库。您想担心MySQL的DATETIME值之间的差异以及Oracle如何定义它们吗?即使在不同版本的MySQL中,TIMESTAMPS也具有不同的精度级别。直到最近,MySQL才支持时间戳中的毫秒。没有时区问题。这里有一些关于不同数据类型的时区的见解。但这是常识吗?你的同事会花时间学习吗?另一方面,很难将BigINT更改为java.util.Date。使用BigINT会导致很多时区问题被搁置一旁。无需担心范围或精度。你不必担心未来日期范围会缩短什么(TIMESTAMP只适用于2038年)。第三方工具集成。通过使用整数,第三方工具(例如EclipseLink)与数据库的接口很简单。并非所有第三方工具都会像MySQL一样理解“datetime”。如果您使用这些自定义数据类型,想尝试在Hibernate中确定是否应该使用java.sql.TimeStamp或java.util.Date对象?使用基本数据类型使第三方工具的使用变得微不足道。

这个问题与如何在数据库中存储货币值(即1.99美元)密切相关。你应该使用十进制,还是数据库的货币类型,或者最糟糕的是双精度?由于上面列出的许多相同原因,所有三种选择都很糟糕。解决方案是使用BIGINT将货币的价值存储为美分,然后在向用户显示价值时将美分转换为美元。数据库的工作是存储数据,而不是插入数据。您在数据库(尤其是Oracle)中看到的所有这些花哨的数据类型几乎没有增加任何内容,并使您开始进入供应商锁定状态。

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

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

根据我的经验,如果您想要一个插入仅发生一次的日期字段,并且不想对该特定字段进行任何更新或任何其他操作,请使用日期时间。

例如,考虑具有REGISTRATION DATE字段的用户表。在该用户表中,如果您想知道特定用户的上次登录时间,请使用时间戳类型的字段,以便更新该字段。

如果您是从phpMyAdmin创建表,则默认设置将在行更新发生时更新时间戳字段。如果时间戳字段未随行更新而更新,则可以使用以下查询使时间戳字段自动更新。

ALTER TABLE your_table
      MODIFY COLUMN ts_activity TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;