PHP和mySQL:2038年错误:什么?怎么解决呢?


116

我当时想使用TIMESTAMP来存储日期和时间,但是我读到它有2038年的限制。与其大量提出问题,不如将其分解成小部分,以便新手用户也易于理解。所以我的问题是:

  1. 2038年的问题到底是什么?
  2. 为什么会发生,何时发生?
  3. 我们该如何解决呢?
  4. 是否有使用它的其他可能的选择,而不会引起类似的问题?
  5. 当使用TIMESTAMP的现有应用程序真正发生时,我们该怎么办呢?

提前致谢。


1
那仍然是28年。您是否还在使用1982年以来与计算机相关的任何技术?不太可能。不用担心,因为到2038年,它可能不再是问题。
戈登

24
戈登,我做了一个进行预测的应用程序。我每个月都会存多少钱,然后可以估算出自己何时会成为百万富翁。就我而言,这还不算28年,而且我敢肯定我不是现在唯一遇到此问题的人(我通过使用64位数字表示时间戳来解决此问题)。
EmilVikström2010年

2
@Emil通过使用64位整数,现在,你已经发现自己一个简单的解决方案,以一个具体的问题。不适用于每个人(或每个人都需要),但适用于您的用例。关键是,如果OP没有诸如预测之类的具体问题,那么这可能是一个有趣的话题,但他不必担心,因为从总体上解决此问题不是PHP(注意标记)问题。只是我的2c。
高登(Gordon)2010年

1
到2038年,解析字符串“ YYYY-MM-DD HH:MM:SS:mmmm ...”将是您梦dream以求的最便宜的操作。到2038年,32位将被淘汰。我怀疑Unix时间戳是否会存在,如果存在的话,我们的256位系统将处理的日期远远超过4096位系统在快乐时光中使用的年龄。
超级猫

8
戈登,现在已经9年了。TIMESTAMPS仍在使用。我们仍然使用28年前的技术。它称为万维网。
sfscs

Answers:


149

我已将其标记为社区Wiki,因此随时可以编辑。

2038年的问题到底是什么?

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


为什么会发生,何时发生?

超越时代3时14分07秒UTC在星期二,2038 1月19日将“环绕”并为负数,这些系统将在1901年12月13日,解释为一个时间,而不是在2038年内部存储这是由于自UNIX时代(1970年1月1日00:00:00 GMT)以来的秒数已超过计算机的32位带符号整数的最大值这一事实。


我们该如何解决呢?

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

是否有使用它的其他可能的选择,而不会引起类似的问题?

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


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

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


当使用TIMESTAMP的现有应用程序真正发生时,我们该怎么办呢?

在2038年,几乎没有PHP应用程序会出现,尽管很难预见,因为Web仍不是旧平台。

这是更改数据库表列以转换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`

资源资源


2
太棒了 对我来说,您的答案是最完整的。非常感谢。
Devner,2010年

7
在MySQL中,如果将来的版本将TIMESTAMP的基础存储数据类型更改为64位,则无需将代码更改为DATETIME,对吗?我只是认为不劝阻任何人使用TIMESTAMP是正确的事情,因为该数据类型确实有其用途。
pixelfreak

1
MySQL的带签名的BIGINT字段似乎可以很好地用于存储时间戳,并且我已经在MySQL 5.5上进行了一些本地测试以确认它可以正常工作。显然,使用签名比使用未签名更好,因为您也可以表示过去的日期。有什么理由不将BIGINT用于时间戳?
zuallauz

需要注意的一件事是,MySQL仅将时间戳字段的自动设置设置为当前日期/时间(CURRENT_TIMESTAMP)。希望该功能最终可以移植到所有日期类型。
2013年

5
绝对荒谬的是MySQL(和MariaDB)不使用64位整数在64位系统上存储时间戳。使用DATETIME是不是一个适当的解决方案,因为我们没有关于时区的想法。这是报告的方式早在2005年,但仍然没有锁定可用。
迈克(Mike)

14

当使用UNIX时间戳记存储日期时,实际上是使用32位整数,该整数保留了从1970-01-01开始的秒数;见Unix时间

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


要解决该问题,您一定不能使用32位UNIX时间戳来存储日期-这意味着,在使用MySQL时,您不应使用TIMESTAMP,而是DATETIME(请参见10.3.1。DATETIME,DATE和TIMESTAMP类型):

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年之前,您的应用程序已经被重写了好几次。^^因此,也许将来您不必处理日期,则您不必在当前版本的应用程序中解决该问题...


1
+1的信息。这里的尝试是从一开始就采用最佳实践,这样以后就不必担心该问题了。因此,尽管就目前而言,2038听起来似乎不是一个问题,但我只想遵循最佳实践,并从一开始就对我创建的每个应用程序都遵循最佳实践。希望有道理。
Devner,2010年

是的,这很有意义,我明白你的意思。但是“最佳实践”也可能意味着“能满足需求” —如果您知道时间戳已足够,则无需使用日期时间(例如,那些需要存储更多内存的日期时间;如果您有数百万条记录);; 我有一些应用程序在其中一些列(例如,包含当前日期的列)和其他一些日期(例如,包含过去/未来日期的列)中使用时间戳
Pascal MARTIN,2010年

我看到您的程序也很有意义,并且效果很好,特别是在涉及存储空间时。如果可以,请问您在应用程序中的TIMESTAMP列通常使用什么结构和长度?谢谢。
Devner'1

好吧,当我想使用TIMESTAMP时,因为/因为我的“日期/时间”数据适合1970年至2038年之间,因此,我使用MySQL TIMESTAMP数据类型。
Pascal MARTIN 2010年

谢谢(你的)信息。从您的答复中,我知道只需将列声明为TIMESTAMP就足够了,我们不需要为其提供任何长度(与int或var不同,我们在其中提供了长度)。我对吗?
Devner'1

7

在Google上进行快速搜索可以解决这个问题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年而不是2038年的日期
  3. 对于现有的CPU / OS组合,现有的文件系统或现有的二进制数据格式,没有简单的解决方案可以解决此问题

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点和第5点让我想起了“按引用致电”。那使我脸上露出了微笑。谢谢&+1。
Devner,2010年

1

兄弟,如果您需要使用PHP来显示时间戳,这是不更改UNIX_TIMESTAMP格式的最佳PHP解决方案。

使用custom_date()函数。在其中,使用DateTime。这是DateTime解决方案

只要您将UNSIGNED BIGINT(8)作为数据库中的时间戳记即可。只要您拥有PHP 5.2.0 ++


-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'];

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.