我正在考虑使用 TIMESTAMP 来存储日期+时间,但我读到它有 2038 年的限制。与其大量提出我的问题,我更愿意将其分解成小部分,以便新手用户也易于理解。所以我的问题:
- 2038年问题到底是什么?
- 为什么会发生以及发生时会发生什么?
- 我们如何解决它?
- 是否有任何可能的替代方法来使用它,而不会造成类似的问题?
- 我们可以对使用 TIMESTAMP 的现有应用程序做些什么来避免所谓的问题,当它真正发生时?
提前致谢。
我已将其标记为社区 wiki,因此您可以随意编辑。
“2038 年问题(也称为 Unix Millennium Bug,Y2K38 类似于 Y2K 问题)可能会导致某些计算机软件在 2038 年之前或之前发生故障。该问题会影响所有将系统时间存储为签名 32 的软件和系统-bit 整数,并将此数字解释为自 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)以来的秒数将超过计算机 32 位有符号整数的最大值。
DATE
列类型。如果您需要更高的准确度,请使用DATETIME
而不是TIMESTAMP
. 请注意,DATETIME
列不存储有关时区的信息,因此您的应用程序必须知道使用了哪个时区。尽可能尝试使用大类型在数据库中存储日期:64 位就足够了——在 GNU C 和 POSIX/SuS 中,或者sprintf('%u'...)
在 PHP 或 BCmath 扩展中是 long long 类型。
所以 MySQL DATETIME的范围是 1000-9999,但 TIMESTAMP 的范围只有 1970-2038。如果您的系统存储了生日、未来的远期日期(例如 30 年的抵押贷款)或类似的,那么您已经遇到了这个错误。同样,如果这会成为问题,请不要使用 TIMESTAMP。
很少有 PHP 应用程序会在 2038 年出现,尽管很难预见,因为 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`
资源
使用 UNIX Timestamps 存储日期时,您实际上使用的是 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'
to'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 年之前被重写很多次 ^^ 所以也许,如果您不必处理未来的日期,您将不必使用当前版本的应用程序来处理该问题...
在 Google 上快速搜索即可:2038 年问题
http://en.wikipedia.org/wiki/Year_2038_problem有大部分细节
总之:
1) + 2) 问题是许多系统将日期信息存储为 32 位有符号整数,等于自 1970 年 1 月 1 日以来的秒数。可以像这样存储的最新日期是 2038 年 1 月 19 日星期二 03:14:07 UTC。发生这种情况时,int 将“环绕”并存储为负数,该负数将被解释为 1901 年的日期。那么究竟会发生什么,因系统而异,但足以说这可能对他们中的任何一个都没有好处!
对于只存储过去日期的系统,那我想你暂时不用担心!主要问题在于使用未来日期的系统。如果您的系统需要使用 28 年后的日期,那么您现在应该开始担心了!
3) 使用可用的替代日期格式之一,或移至 64 位系统并使用 64 位整数。或者对于数据库使用另一种时间戳格式(例如对于 MySQL 使用 DATETIME)
4)见3!
5)见4!!!;)
兄弟们,如果您需要使用 PHP 来显示时间戳,这是最好的 PHP 解决方案,无需更改 UNIX_TIMESTAMP 格式。
使用 custom_date() 函数。在其中,使用 DateTime。这是 DateTime 解决方案。
只要您将 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'];
可能不是一种有效的方法,但它有效。