PHP和mySQL:2038年Bug:它是什么?如何解决?

2022-08-30 06:59:05

我正在考虑使用TIMESTAMP来存储日期+时间,但我读到它有2038年的限制。与其批量提问,我更喜欢将其分解成小部分,以便新手用户也易于理解。所以我的问题:

  1. 2038年的问题到底是什么?
  2. 为什么会发生,当它发生时会发生什么?
  3. 我们如何解决它?
  4. 除了使用它之外,是否有任何可能的替代方法,这些替代方案不会造成类似的问题?
  5. 当所谓的问题真正发生时,我们可以对使用TIMESTAMP的现有应用程序做些什么来避免它?

提前致谢。


答案 1

我已将其标记为社区wiki,因此请随时在闲暇时进行编辑。

2038年的问题到底是什么?

“2038年的问题(也称为Unix Millennium Bug,Y2K38,类比Y2K问题)可能会导致一些计算机软件在2038年之前或之后失败。此问题会影响所有将系统时间存储为有符号 32 位整数的软件和系统,并将此数字解释为自 1970 年 1 月 1 日 00:00:00 UTC 以来的秒数。


为什么会发生,当它发生时会发生什么?

2038 年 1 月 19 日星期二 03:14:07 UTC 之后的时间将“环绕”并在内部存储为负数,这些系统将将其解释为 1901 年 12 月 13 日而不是 2038 年的时间。这是因为自 UNIX 时代(1970 年 1 月 1 日 00:00:00 GMT)以来的秒数将超过计算机对 32 位有符号整数的最大值。


我们如何解决它?

  • 使用长数据类型(64 位就足够了)
  • 对于MySQL(或MariaDB),如果您不需要时间信息,请考虑使用列类型。如果需要更高的精度,请使用 而不是 。请注意,列不存储有关时区的信息,因此应用程序必须知道使用了哪个时区。DATEDATETIMETIMESTAMPDATETIME
  • 维基百科上描述的其他可能的解决方案
  • 将 Mysql 升级到 8.0.28 或更高版本

除了使用它之外,是否有任何可能的替代方法,这些替代方案不会造成类似的问题?

尽可能使用大类型在数据库中存储日期:64位就足够了 - GNU C和POSIX / SuS中的长长类型,或者PHP或BCmath扩展。sprintf('%u'...)


即使我们还没有到2038年,一些潜在的突破性用例是什么?

因此,MySQL DATETIME的范围为1000-9999,但TIMESTAMP的范围仅为1970-2038。如果您的系统存储出生日期,未来的远期日期(例如30年抵押贷款)或类似日期,那么您已经会遇到此错误。同样,如果这将是一个问题,请不要使用TIMESTAMP。


当所谓的问题真正发生时,我们可以对使用TIMESTAMP的现有应用程序做些什么来避免它?

到2038年,很少有PHP应用程序仍然存在,尽管很难预见,因为Web还不是一个传统平台。

下面是更改要转换为 的数据库表列的过程。它从创建一个临时列开始:TIMESTAMPDATETIME

# rename the old TIMESTAMP field
ALTER TABLE `myTable` CHANGE `myTimestamp` `temp_myTimestamp` int(11) NOT NULL;

# create a new DATETIME column of the same name as your old column
ALTER TABLE `myTable` ADD `myTimestamp` DATETIME NOT NULL;

# update all rows by populating your new DATETIME field
UPDATE `myTable` SET `myTimestamp` = FROM_UNIXTIME(temp_myTimestamp);

# remove the temporary column
ALTER TABLE `myTable` DROP `temp_myTimestamp`

资源


答案 2

当使用UNIX时间戳来存储日期时,您实际上使用的是32位整数,该整数保持自1970-01-01以来的秒数计数;参见 Unix Time

这个32位数字将在2038年溢出。这就是2038年的问题。


要解决这个问题,你一定不能使用32位的UNIX时间戳来存储你的日期 - 这意味着,在使用MySQL时,你不应该使用,但是(参见10.3.1。日期时间、日期和时间戳类型):TIMESTAMPDATETIME

当您需要同时包含日期和时间信息的值时,将使用该类型。支持的范围是 到 。DATETIME'1000-01-01 00:00:00''9999-12-31 23:59:59'

数据类型的范围为 UTC 到 UTC。TIMESTAMP'1970-01-01 00:00:01''2038-01-19 03:14:07'


为了避免/修复该问题,您可以对应用程序做的(可能)最好的事情是不使用 ,但对于必须包含日期不在1970和2038之间的列。TIMESTAMPDATETIME

不过,有一个小注意事项:很有可能(从统计学上讲)您的应用程序在2038年之前已经重写了好几次^^因此,如果您将来不必处理日期,则不必处理该应用程序的当前版本的问题...


推荐