你会推荐使用日期时间或时间戳字段,为什么(使用 MySQL)?
我在服务器端使用 PHP。
MySQL 中的时间戳一般用于跟踪记录的更改,并且经常在每次记录更改时更新。如果要存储特定值,则应使用日期时间字段。
如果您的意思是要在使用 UNIX 时间戳或原生 MySQL 日期时间字段之间做出决定,请使用原生格式。您可以通过这种方式在 MySQL 中进行计算,
如果您想使用 PHP 对其进行操作,则在查询记录时("SELECT DATE_ADD(my_datetime, INTERVAL 1 DAY)")
将值的格式更改为 UNIX 时间戳很简单。("SELECT UNIX_TIMESTAMP(my_datetime)")
在 MySQL 5 及更高版本中,TIMESTAMP值从当前时区转换为 UTC 进行存储,并从 UTC 转换回当前时区进行检索。(这仅适用于 TIMESTAMP 数据类型,而不适用于其他类型,例如 DATETIME。)
默认情况下,每个连接的当前时区是服务器的时间。可以在每个连接的基础上设置时区,如MySQL Server Time Zone Support中所述。
我总是将 DATETIME 字段用于除行元数据(创建或修改日期)之外的任何内容。
如MySQL 文档中所述:
当您需要包含日期和时间信息的值时,使用 DATETIME 类型。MySQL 以 'YYYY-MM-DD HH:MM:SS' 格式检索和显示 DATETIME 值。支持的范围是“1000-01-01 00:00:00”到“9999-12-31 23:59:59”。
...
TIMESTAMP 数据类型的范围为 '1970-01-01 00:00:01' UTC 到 '2038-01-09 03:14:07' UTC。它具有不同的属性,具体取决于 MySQL 版本和服务器运行的 SQL 模式。
您很可能会达到一般使用的 TIMESTAMP 的下限——例如存储生日。
下面的示例显示了TIMESTAMP
日期类型在更改time-zone to 'america/new_york'
whereDATETIME
不变后如何更改值。
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: Datetime Versus Timestamp Data Types。
主要区别在于 DATETIME 是常量,而 TIMESTAMP 受time_zone
设置影响。
因此,只有当您拥有(或将来可能拥有)跨时区同步集群时才重要。
简而言之:如果我在澳大利亚有一个数据库,并转储该数据库以同步/填充美国的数据库,则 TIMESTAMP 将更新以反映新时区中事件的实时时间,而 DATETIME 将仍然在 au 时区反映事件发生的时间。
在 Facebook 中应该使用 TIMESTAMP 的地方使用 DATETIME 的一个很好的例子,他们的服务器永远无法确定跨时区发生的时间。有一次我进行了一次对话,其中时间说我是在消息实际发送之前回复消息。(当然,如果发布时间而不是同步时间,这也可能是由于消息传递软件中的时区转换错误造成的。)
我在语义基础上做出这个决定。
当我需要记录(或多或少)固定时间点时,我会使用时间戳。例如,当一条记录插入数据库或发生某些用户操作时。
当可以任意设置和更改日期/时间时,我使用日期时间字段。例如,当用户可以保存以后更改约会时。
我建议既不使用 DATETIME 也不使用 TIMESTAMP 字段。如果您想将特定的一天作为一个整体来表示(例如生日),请使用 DATE 类型,但如果您比这更具体,您可能对记录实际时刻而不是单位感兴趣时间(日、周、月、年)。不要使用 DATETIME 或 TIMESTAMP,而是使用 BIGINT,并简单地存储自纪元以来的毫秒数(如果您使用的是 Java,则为 System.currentTimeMillis())。这有几个优点:
这个问题与您应该如何在数据库中存储货币价值(即 1.99 美元)密切相关。您应该使用 Decimal,还是数据库的 Money 类型,或者最糟糕的是 Double?由于上面列出的许多相同原因,所有这 3 个选项都很糟糕。解决方案是使用 BIGINT 以美分存储货币的价值,然后在向用户显示价值时将美分转换为美元。数据库的工作是存储数据,而不是解释数据。您在数据库(尤其是 Oracle)中看到的所有这些花哨的数据类型几乎没有什么作用,并让您开始锁定供应商。
TIMESTAMP
是 4 个字节,而DATETIME
.
数据库上的时间戳也更轻,索引速度更快。
DATETIME
当您需要包含日期和时间信息的值时使用该类型。MySQL 以格式检索和显示DATETIME
值。YYYY-MM-DD HH:MM:SS
支持的范围是1000-01-01 00:00:00
to 9999-12-31 23:59:59
。数据类型的TIMESTAMP
范围为1970-01-01 00:00:01 UTC
to 2038-01-09 03:14:07 UTC
。它具有不同的属性,具体取决于 MySQL 版本和服务器运行的 SQL 模式。
DATETIME
是恒定的,而TIMESTAMP
受time_zone
设置影响。
TIMESTAMP 是 4 个字节,而 DATETIME 是 8 个字节。
http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html
但是就像 scronide 说的那样,它确实有 1970 年的下限。不过,这对于未来可能发生的任何事情都很好;)
取决于应用程序,真的。
考虑为在桑海约会的用户设置一个时间戳到纽约的服务器。现在,当用户在 Sanghai 连接时,他从东京的镜像服务器访问相同的约会时间戳。他将在东京时间看到约会,与原来的纽约时间有偏差。
因此,对于代表用户时间的值(如约会或日程安排),日期时间更好。它允许用户控制所需的确切日期和时间,而不管服务器设置如何。设置时间是设置时间,不受服务器时区、用户时区或夏令时计算方式变化的影响(是的,它确实会改变)。
另一方面,对于表示系统时间的值,如支付交易、表修改或日志记录,始终使用时间戳。将服务器移动到另一个时区,或者在不同时区的服务器之间进行比较时,系统不会受到影响。
数据库上的时间戳也更轻,索引速度更快。
任何最近的前端框架(Angular 1/2、react、Vue 等)都可以轻松自动地将您的 UTC 日期时间转换为本地时间。
此外:
(除非您可能会更改服务器的时区)
AngularJs 示例
// back-end: format for angular within the sql query
SELECT DATE_FORMAT(my_datetime, "%Y-%m-%dT%TZ")...
// font-end Output the localised time
{{item.my_datetime | date :'medium' }}
此处提供的所有本地化时间格式: https ://docs.angularjs.org/api/ng/filter/date
两者都不。对于通用用例, DATETIME
andTIMESTAMP
类型从根本上被破坏了。MySQL 将来会改变它们。您应该使用BIGINT
UNIX 时间戳,除非您有特定的理由使用其他东西。
以下是一些特定情况,您的选择更容易,您不需要此答案中的分析和一般建议。
仅日期- 如果您只关心日期(例如下一个农历新年的日期,2022-02-01)并且您清楚地了解该日期适用的时区(或者不关心,如案例农历新年)然后使用DATE
列类型。
记录插入时间——如果您正在记录数据库中行的插入日期/时间,并且您不关心您的应用程序将在未来 17 年内中断,那么请继续使用TIMESTAMP
默认值CURRENT_TIMESTAMP()
.
TIMESTAMP
坏了?该TIMESTAMP
类型以 UTC 时区存储在磁盘上。这意味着,如果您实际移动服务器,它不会中断。这很好✅。但目前定义的时间戳将在 2038 年完全停止工作❌。
每次您INSERT INTO
或列时,都会考虑您的客户端/应用程序服务器的物理位置(即时区配置)SELECT FROM
。TIMESTAMP
如果您移动应用程序服务器,那么您的日期就会中断 ❌。
VARCHAR
坏了?该VARCHAR
类型允许以 ISO 8601 格式明确存储非本地日期/时间/两者,并且适用于 2037 年以后的日期。通常使用 Zulu 时间,但 ISO 8601 允许对任何偏移量进行编码。这不太有用,因为虽然 MySQL 日期和时间函数确实支持字符串作为任何预期日期/时间/两者的输入,但如果输入使用时区偏移量,则结果不正确。
还VARCHAR
使用额外的存储字节。
DATETIME
坏了?ADATETIME
将 aDATE
和 a存储TIME
在同一列中。除非了解时区,否则这些事情都没有任何意义,并且时区不存储在任何地方❌。您应该将预期的时区作为注释放在列中,因为时区与数据有着千丝万缕的联系。很少有人使用专栏评论,因此这是等待发生的错误。我从亚利桑那州继承了一台服务器,因此我总是需要将所有时间戳从亚利桑那时间转换为另一个时间。
(更新 2021-12-08 我在运行多年后重新启动了服务器,并且数据库客户端(带有升级)重置为 UTC。这意味着我的应用程序需要以不同的方式处理重置前后的日期。硬编码!)
唯一DATETIME
正确的情况是完成这句话:
您的 2020 年太阳能新年正好从
DATETIME("2020-01-01 00:00:00")
.
s没有其他好的用途DATETIME
。也许您会想象特拉华州一个市政府的 Web 服务器。当然,这个服务器的时区和所有访问这个服务器的人都可以暗示在特拉华州,使用东部时区,对吧?错误的!在这个千禧年,我们都认为服务器存在于“云”中。因此,在任何特定时区考虑您的服务器总是错误的,因为您的服务器总有一天会被移动。
注意:MySQL 现在支持文字中的时区偏移DATETIME
(感谢@Marko)。这可能使插入DATETIME
s 对您来说更方便,但不能解决数据的不完整和无用的含义,这个致命问题在上面标识 ("❌")。
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))
);
或者当然这从您的应用程序中要好得多,例如:
$statement = $myDB->prepare('INSERT INTO good_times VALUES (?)');
$statement->execute([$someTime->getTimestamp()]);
选择:
SELECT a_time FROM good_times;
过滤相对时间的技术(选择过去 30 天内的帖子,查找在注册后 10 分钟内购买的用户)超出了这里的范围。
TIMESTAMP 始终采用 UTC(即自 1970-01-01 以来经过的秒数,采用 UTC),您的 MySQL 服务器会自动将其转换为连接时区的日期/时间。从长远来看,TIMESTAMP 是可行的方法,因为您知道您的时间数据将始终采用 UTC。例如,如果您迁移到其他服务器或更改服务器上的时区设置,您不会搞砸日期。
注意:默认连接时区是服务器时区,但这可以(应该)在每个会话中更改(参见 参考资料SET time_zone = ...
)。
字段是字段的timestamp
特例datetime
。您可以创建timestamp
具有特殊属性的列;它可以设置为在创建和/或更新时更新自身。
在“更大”的数据库术语中,timestamp
有几个特殊情况的触发器。
什么是正确的完全取决于你想做什么。
DATETIME、TIMESTAMP 和 DATE 之间的比较
那 [.fraction] 是什么?
资料来源:
值得注意的是,在 MySQL 中,您可以在创建表列时使用以下内容:
on update CURRENT_TIMESTAMP
这将更新您修改行的每个实例的时间,有时对于存储的最后编辑信息非常有帮助。这仅适用于时间戳,但不适用于日期时间。
在使用 MySQL 和 PHP 时,我总是使用 Unix 时间戳。主要原因是 PHP 中的默认日期方法使用时间戳作为参数,因此不需要解析。
要在 PHP 中获取当前的 Unix 时间戳,只需执行time();
并且在 MySQL 中执行SELECT UNIX_TIMESTAMP();
.
根据我的经验,如果您想要一个仅插入一次的日期字段,并且您不想对该特定字段进行任何更新或任何其他操作,请使用date time。
例如,考虑一个user
带有REGISTRATION DATE字段的表。在该user
表中,如果您想知道特定用户的上次登录时间,请使用时间戳类型的字段,以便该字段得到更新。
如果您从phpMyAdmin创建表,则默认设置将在发生行更新时更新时间戳字段。如果您的时间戳字段未随行更新而更新,您可以使用以下查询使时间戳字段自动更新。
ALTER TABLE your_table
MODIFY COLUMN ts_activity TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
timestamp 数据类型存储日期和时间,但采用 UTC 格式,而不是像 datetime 那样采用当前时区格式。当您获取数据时,时间戳再次将其转换为当前时区时间。
因此,假设您在美国并从具有美国时区的服务器获取数据。然后您将根据美国时区获得日期和时间。时间戳数据类型列总是在其行更新时自动更新。因此,跟踪上次更新特定行的时间会很有用。
有关更多详细信息,您可以阅读博文Timestamp Vs Datetime。
我总是使用 Unix 时间戳,只是为了在处理大量日期时间信息时保持理智,尤其是在执行时区调整、添加/减去日期等时。在比较时间戳时,这排除了时区的复杂因素,并允许您在服务器端处理(无论是应用程序代码还是数据库查询)中节省资源,因为您使用轻量级算法而不是较重的日期时间加/减职能。
另一件值得考虑的事情:
如果您正在构建一个应用程序,您永远不知道您的数据可能会被如何使用。如果您最终不得不将数据集中的一堆记录与第三方 API 中的一堆项目进行比较,然后将它们按时间顺序排列,您会很高兴拥有行的 Unix 时间戳。即使您决定使用 MySQL 时间戳,也要存储一个 Unix 时间戳作为保险。
主要区别:
TIMESTAMP 用于跟踪记录的更改,并在每次记录更改时更新。DATETIME 用于存储不受记录更改影响的特定静态值。
TIMESTAMP 也受不同 TIME ZONE 相关设置的影响。DATETIME 是常数。
TIMESTAMP 在内部将当前时区转换为 UTC 进行存储,并在检索期间转换回当前时区。DATETIME 不能这样做。
TIMESTAMP 支持范围:'1970-01-01 00:00:01' UTC 到 '2038-01-19 03:14:07' UTC DATETIME 支持范围:'1000-01-01 00:00:00' 到 '9999 -12-31 23:59:59'</p>
就我而言,我每次都将 UTC 设置为所有内容的时区:系统、数据库服务器等。如果我的客户需要另一个时区,那么我会在应用程序上对其进行配置。
我几乎总是更喜欢时间戳而不是日期时间字段,因为时间戳隐含地包含时区。因此,由于来自不同时区的用户将访问应用程序并且您希望他们查看其本地时区的日期和时间,因此与将数据保存在日期时间字段中相比,此字段类型更容易做到.
另外,在将数据库迁移到具有另一个时区的系统的情况下,我会更有信心使用时间戳。更不用说在计算两个时刻之间的差异时可能存在的问题,并且两者之间的总和时间变化并且需要 1 小时或更短的精度。
所以,总而言之,我看重时间戳的这个优点:
出于所有这些原因,我尽可能选择 UTC 和时间戳字段。而且我避免头痛;)
当您对表执行 UPDATE 语句时,请注意时间戳更改。如果您有一个包含 'Name' (varchar)、'Age' (int) 和 'Date_Added' (timestamp) 列的表,并且您运行以下 DML 语句
UPDATE table
SET age = 30
那么“Date_Added”列中的每个值都将更改为当前时间戳。
+---------------------------------------------------------------------------------------+--------------------------------------------------------------------------+
| TIMESTAMP | DATETIME |
+---------------------------------------------------------------------------------------+--------------------------------------------------------------------------+
| TIMESTAMP requires 4 bytes. | DATETIME requires 8 bytes. |
| Timestamp is the number of seconds that have elapsed since January 1, 1970 00:00 UTC. | DATETIME is a text displays 'YYYY-MM-DD HH:MM:SS' format. |
| TIMESTAMP supported range: ‘1970-01-01 00:00:01′ UTC to ‘2038-01-19 03:14:07′ UTC. | DATETIME supported range: ‘1000-01-01 00:00:00′ to ‘9999-12-31 23:59:59′ |
| TIMESTAMP during retrieval converted back to the current time zone. | DATETIME can not do this. |
| TIMESTAMP is used mostly for metadata i.e. row created/modified and audit purpose. | DATETIME is used mostly for user-data. |
+---------------------------------------------------------------------------------------+--------------------------------------------------------------------------+
我发现 TIMESTAMP 能够根据当前时间自动更新自身而无需使用不必要的触发器,这一点非常有用。但这只是我,尽管 TIMESTAMP 就像人们所说的那样是 UTC。
它可以跨不同时区跟踪,因此如果您需要显示相对时间,例如,UTC 时间就是您想要的。
日期时间与时间戳:
TIMESTAMP 用于跟踪记录的变化,并在每次记录发生变化时更新。
DATETIME 用于存储不受记录更改影响的特定静态值。
TIMESTAMP 也受不同 TIME ZONE 相关设置的影响。DATETIME 是常数。
TIMESTAMP 在内部将当前时区转换为 UTC 进行存储,并在检索期间将其转换回当前时区。
DATETIME 不能这样做。
TIMESTAMP 为 4 个字节,DATETIME 为 8 个字节。
TIMESTAMP 支持范围:'1970-01-01 00:00:01' UTC 到 '2038-01-19 03:14:07' UTC DATETIME 支持范围:'1000-01-01 00:00:00' 到 '9999 -12-31 23:59:59'</p>
datetime
在遇到与时区相关的许多问题和错误后,我停止在我的应用程序中使用。恕我直言,使用timestamp
比datetime
大多数情况下要好。
当你问什么时候?答案类似于'2019-02-05 21:18:30',没有完成,没有定义答案,因为它缺少另一部分,在哪个时区?华盛顿?莫斯科?北京 ?
使用不带时区的日期时间意味着您的应用程序只处理 1 个时区,但是时间戳为您提供了datetime
在不同时区显示相同确切时间点的灵活性。
以下是一些会让您后悔使用datetime
并希望您将数据存储在时间戳中的案例。
为了让您的客户感到舒适,您希望根据他们喜欢的时区向他们展示时间,而不是让他们进行数学运算并将时间转换为他们有意义的时区。您只需要更改时区,您的所有应用程序代码都将相同。(实际上,您应该始终在应用程序开始时定义时区,或者在 PHP 应用程序的情况下进行请求处理)
SET time_zone = '+2:00';
您更改了您所在的国家/地区,并继续维护数据,同时在不同的时区查看数据(不更改实际数据)。
datetime
= 应用程序支持 1 个时区(用于插入和选择)
timestamp
= 应用程序支持任何时区(用于插入和选择)
这个答案只是为了强调时间戳在时区方面的灵活性和易用性,它没有涵盖任何其他差异,如列大小或范围或分数。
Timestamp 和 Datetime 之间的另一个区别是 Timestamp 您不能将默认值设置为 NULL。
我更喜欢使用时间戳,以便将所有内容保持在一种通用的原始格式中,并在 PHP 代码或 SQL 查询中格式化数据。在某些情况下,它可以在您的代码中派上用场,让一切都在几秒钟内完成。
ATIMESTAMP
需要 4 个字节,而 aDATETIME
需要 8 个字节。
我喜欢 Unix 时间戳,因为您可以转换为数字而只需担心数字。另外,您可以添加/减去并获取持续时间等。然后以任何格式将结果转换为日期。此代码找出文档中的时间戳与当前时间之间经过的时间(以分钟为单位)。
$date = $item['pubdate']; (etc ...)
$unix_now = time();
$result = strtotime($date, $unix_now);
$unix_diff_min = (($unix_now - $result) / 60);
$min = round($unix_diff_min);
当您有来自不同国家和不同时区的访问者时,TIMESTAMP 很有用。您可以轻松地将 TIMESTAMP 转换为任何国家时区
ADATETIME
不携带任何时区信息,并且始终显示与会话有效的时区无关的相同时区,除非您明确更改它,否则默认为服务器的时区。但是,如果我使用诸如 之类的函数而不是诸如 之类的文字来初始化DATETIME
列,那么存储的值当然将是本地化到会话时区的当前日期和时间。NOW()
'2020-01-16 12:15:00'
TIMESTAMP
相比之下, A确实隐式携带时区信息:当您TIMESTAMP
使用值初始化列时,该值会在存储之前转换为 UTC。如果要存储的值是诸如 之类的文字'2020-01-16 12:15:00'
,则出于转换目的,它会被解释为处于会话的当前时区中。相反,当TIMESTAMP
显示一列时,它将首先从 UTC 转换为会话的当前时区。
何时使用其中一种?案例研究
一个社区剧团的网站正在展示它正在售票的几场戏剧表演。这些表演的日期和时间将出现在一个下拉列表中,希望购买表演门票的客户可以从中选择一个。performance_date_and_time
将数据库列作为一种DATETIME
类型是有意义的。如果演出在纽约,则理解为涉及隐含时区(“纽约当地时间”),理想情况下,我们希望日期和时间显示为“2019 年 12 月 12 日晚上 8:00”无论会话的时区如何,也不必费心进行任何时区转换。
另一方面,一旦 2019 年 12 月 12 日晚上 8 点表演开始,我们可能不再想出售它的门票,因此不再在下拉菜单中显示该表演。因此,我们希望能够知道“2019-12-12 20:00:00”是否发生。这将争论有一个TIMESTAMP
列,将会话的时区设置为“America/New_York”,set session time_zone='America/New_York'
然后存储'2019-12-12 20:00:00'
到该TIMESTAMP
列中。此后,我们可以通过将此列与NOW()
当前会话时区的独立性进行比较来测试性能是否已经开始。
或者为这两个不同的目的设置一个DATETIME
和一个列可能是有意义的。TIMESTAMP
或不。显然,任何一个都可以同时达到两个目的。如果您只使用一DATETIME
列,则必须将当前时区设置为您的本地时区,然后再与NOW()
. 如果您只使用一TIMESTAMP
列,则必须在显示该列之前将会话时区设置为您的本地时区。
BIGINT
我只是在存储 UTC 时使用 unsigned ...
然后仍然可以在 PHP 中将其调整为本地时间。
用DATETIME
选择FROM_UNIXTIME( integer_timestamp_column )
。
显然应该在该列上设置一个索引,否则将没有任何进展。
这里的很多答案都建议存储为时间戳,以防您必须代表明确定义的时间点。但是,如果您按照惯例将它们全部存储在 UTC 中,您也可以使用 datetime 时间点。
到目前为止没有提到的是,DEFAULT CURRENT_TIMESTAMP 仅适用于 Timestamp,但不适用于 DateTime 类型的字段。
这与只能使用 DateTime 而不能使用 Timestamp 的 MS Access 表相关。
timestamp
是计算机通过网络时间协议 (NTP)记录的事件的当前时间。
datetime
是在您的PHP配置中设置的当前时区。
如果您想保证您的应用程序在 2038 年 2 月无法运行,请使用 TIMESTAMP。有关支持的日期范围,请参阅您的 REFMAN。