PHP&mySQL:2038年错误:什么? 如何解决?

我正在考虑使用TIMESTAMP来存储date和时间,但是我读到了2038年的限制。 我不想大量地提出我的问题,我更愿意把它分解成小部分,以便新手用户也很容易理解。 所以我的问题是:

  1. 2038年的问题究竟是什么?
  2. 为什么会发生,发生什么情况?
  3. 我们如何解决它?
  4. 是否有任何可能的替代使用它,这不会造成类似的问题?
  5. 对于使用TIMESTAMP的现有应用程序,我们可以做些什么来避免所谓的问题?

提前致谢。

我已经把这个标记为一个社区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位有符号整数的计算机最大值。


我们如何解决它?

  • 使用长数据types(64位就足够了)
  • 对于MySQL(或MariaDB),如果您不需要时间信息,请考虑使用DATE列types。 如果您需要更高的准确性,请使用DATETIME而不是TIMESTAMP 。 请注意, DATETIME列不存储有关时区的信息,因此您的应用程序将必须知道使用了哪个时区。
  • 其他在维基百科上描述的可能的解
  • 等待MySQL开发者修复十多年前报告的这个bug 。

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

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


即使我们还没有到2038年,哪些潜在的破解用例呢?

所以MySQL DATETIME的范围是1000-9999,但TIMESTAMP的范围只有1970-2038。 如果您的系统存储生日,未来的前瞻date(如30年抵押贷款)或类似的,你已经会遇到这个错误。 再次,不要使用TIMESTAMP,如果这将是一个问题。


对于使用TIMESTAMP的现有应用程序,我们可以做些什么来避免所谓的问题?

很less有PHP应用程序将在2038年左右,但很难预见,因为networking几乎不是传统的平台。

这是一个更改数据库表列以将TIMESTAMP转换为DATETIME 。 首先创build一个临时列:

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

资源

  • 2038年问题(维基百科)
  • 互联网将在30年内结束

使用UNIX时间戳存储date时,实际上使用的是32位整数,它保留自1970-01-01以来的秒数; 请参阅Unix时间

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

为了解决这个问题,你不能使用32位UNIX时间戳来存储你的date – 这意味着在使用MySQL时,你不应该使用TIMESTAMP ,而应该使用DATETIME (见10.3.1。DATETIME,DATE和TIMESTAMPtypes ) :

当您需要包含date和时间信息的值时,使用DATETIMEtypes。 支持的范围是'1000-01-01 00:00:00''9999-12-31 23:59:59'

TIMESTAMP数据types的范围为'1970-01-01 00:00:01' 00 '2038-01-19 03:14:07''2038-01-19 03:14:07'

为了避免/解决这个问题,你可以做的最好的事情就是不要使用TIMESTAMP ,而要使用DATETIME来包含date不在1970和2038之间的列。

但是有一点要注意的是:从统计angular度来看 ,你的申请在2038年之前可能会被重写很多次。所以,如果你将来不需要处理date的话,您将不必关心当前版本的应用程序的问题…

在Google上进行快速search将会成功: 2038年的问题

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

http://en.wikipedia.org/wiki/Year_2038_problem有大部分细节;

综上所述:

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

对于只存储date的系统,我想你不需要担心一会儿! 主要的问题在于未来使用date的系统。 如果你的系统需要在未来28年的date工作,那么你现在应该开始担心了!

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

4)见3!

5)见4! ;)

兄弟,如果你需要使用PHP来显示时间戳,这是最好的PHP解决scheme,而不是从UNIX_TIMESTAMP格式改变。

使用custom_date()函数。 在里面,使用DateTime。 这是DateTime解决scheme 。

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

因为我不想升级任何东西,所以我问我的后端(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']; 

可能不是一个有效的方式,但它的工作原理。

还有另外一种方法可以防止这种情况,我不会详细讨论,但是这个algorithm很简单,通过简化问题,实际上你可以在mysql中有4位数的时间,并使用中间值int。 而且仍然可以将其作为正确的时间发布,甚至在2038年之前。