PHP& mySQL:2038年Bug:它是什么?怎么解决?

时间:2010-01-06 11:36:22

标签: php mysql year2038

我在考虑使用TIMESTAMP存储日期+时间,但我读到它有2038年的限制。我没有大量提出问题,而是倾向于将其分解成小部分,以便新手用户也能轻松理解。所以我的问题:

  1. 2038年的问题究竟是什么?
  2. 为什么会发生以及发生时会发生什么?
  3. 我们如何解决?
  4. 是否有任何可能的替代品使用它,这不会造成类似的问题?
  5. 当真正发生时,我们可以对使用TIMESTAMP的现有应用程序做些什么来避免所谓的问题呢?
  6. 提前致谢。

6 个答案:

答案 0 :(得分:123)

我已将此标记为社区维基,因此您可以随意编辑。

2038年的问题究竟是什么?

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


为什么会发生以及发生时会发生什么?

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


我们如何解决?

  • 使用长数据类型(64位就足够了)
  • 对于MySQL(或MariaDB),如果您不需要时间信息,请考虑使用DATE列类型。如果您需要更高的准确度,请使用DATETIME而不是TIMESTAMP。请注意DATETIME列不存储有关时区的信息,因此您的应用程序必须知道使用了哪个时区。
  • Other Possible solutions described on Wikipedia
  • 等待MySQL开发人员修复十年前报道的this bug

是否有任何可能的替代品使用它,这不会造成类似的问题?

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


即使我们还没有在2038年,有哪些可能会破坏用例?

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


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

很少有PHP应用程序在2038年仍然存在,但很难预见因为网络还不是传统平台。

以下是更改数据库表列以将TIMESTAMP转换为DATETIME的过程。它首先创建一个临时列:

# 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`

<强>资源

答案 1 :(得分:12)

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

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


要解决该问题,您不能使用32位UNIX时间戳来存储日期 - 这意味着,在使用MySQL时,您不应使用TIMESTAMP,而应使用DATETIME(请参阅10.3.1. The DATETIME, DATE, and TIMESTAMP Types ):

  

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

     

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


您可以对应用程序执行的(可能)以避免/解决该问题的最佳方法是不使用TIMESTAMP,而DATETIME用于必须包含日期的列不是在1970年到2038年之间。

但是有一个小小的说明:有一个非常高的可能(从统计上讲)你的应用程序将在2038年之前被重写几次^^所以也许,如果你不喜欢将来不得不处理日期,您不必在当前版本的应用程序中处理该问题...

答案 2 :(得分:7)

快速搜索Google可以解决问题:Year 2038 problem

  1. 2038年的问题(也称为Unix Millennium Bug,Y2K38类似于Y2K问题)可能会导致某些计算机软件在2038年之前或之后失败
  2. 该问题会影响将系统时间存储为带符号的32位整数的所有软件和系统,并将此数字解释为自1970年1月1日00:00:00 UTC以来的秒数。最新时间可以是代表这种方式的是2038年1月19日星期二03:14:07 UTC。超越这一时刻的时间将“环绕”并在内部存储为负数,这些系统将在1901年而不是2038年解释为日期
  3. 对于现有的CPU / OS组合,现有文件系统或现有二进制数据格式,此问题没有简单的解决方法

答案 3 :(得分:2)

http://en.wikipedia.org/wiki/Year_2038_problem包含大部分细节

总结:

1)+ 2)问题是许多系统将日期信息存储为32位有符号int,等于自1970年1月1日以来的秒数。可以像这样存储的最新日期是2038年1月19日星期二03:14:07 UTC。当发生这种情况时,int将“环绕”并存储为负数,这将被解释为1901年的日期。那么究竟会发生什么,因系统而异,但足以说它对任何一个都不好!

对于只存储过去日期的系统,我想你不需要担心一段时间!主要问题是将来使用日期的系统。如果您的系统需要使用未来28年的日期,那么您现在应该开始担心了!

3)使用可用的备用日期格式之一,或移至64位系统并使用64位整数。或者对于数据库使用替代时间戳格式(例如,对于MySQL使用DATETIME)

4)见3!

5)见4 !!! ;)

答案 4 :(得分:1)

Bros,如果您需要使用PHP来显示时间戳,这是最好的PHP解决方案,而不会改变UNIX_TIMESTAMP格式。

使用custom_date()函数。在其中,使用DateTime。 Here's the DateTime solution

只要你有UNSIGNED BIGINT(8)作为数据库中的时间戳。 只要你有PHP 5.2.0 ++

答案 5 :(得分:-1)

由于我不想升级任何东西,我让我的后端(MSSQL)代替PHP来完成这项工作!

$qry = "select DATEADD(month, 1, :date) next_date ";
$rs_tmp = $pdo->prepare($qry);
$rs_tmp->bindValue(":date", '2038/01/15');
$rs_tmp->execute();
$row_tmp = $rs_tmp->fetch(PDO::FETCH_ASSOC);

echo $row_tmp['next_date'];

可能不是一种有效的方法,但它有效。