2038年问题- 64位(Linux操作系统,php,mysql)

3

select unix_timestamp('2038-01-19') 返回 2147472000

select unix_timestamp('2038-01-20') 返回 0

我已经调查了“2038年问题”。

我的 Linux 操作系统是 64 位的,安装的 MySQL 版本也是 64 位的。现在有什么解决方案吗?

mysql --version 返回 mysql Ver 14.14 Distrib 5.5.47, for Linux (x86_64) using readline 5.1
Php 也是 64 位的。

尝试过 BigInt,但没有起作用(返回相同的结果)。


选择unix_timestamp('2038-01-19')返回2147472000。 当选择unix_timestamp('2038-01-19')返回0时,你能告诉这两个查询的区别吗? - Alive to die - Anant
@Anant 那是个打字错误..谢谢 - Danyal Sandeelo
UNIX_TIMESTAMP() 函数需要一个日期作为参数。如果你传递的是字符串,那么程序会依赖于隐式类型转换。如果你事先明确了要操作的具体日期类型,那么你可能会得到一个更加清晰的解决方案。 - Álvaro González
@ÁlvaroGonzález 对于这个也返回0 -> 选择unix_timestamp(2011-01-20) - Danyal Sandeelo
unix_timestamp中的参数需要用引号传递,但是这应该是一个有效的日期格式。@ÁlvaroGonzález - Danyal Sandeelo
显示剩余7条评论
2个回答

6
简单来说,对于MySQL,应该将日期存储为DATETIME而不是TIMESTAMPTIMESTAMP只有4个字节的空间,因此无法存储超过1970年1月1日至2038年1月19日之间的秒数...
相反,DATETIME的范围是1000年1月1日至9999年12月31日...
另请参见这个完整的问题/答案:PHP&mySQL:Year 2038 Bug:是什么?如何解决? 更新: 我看到一个可能的替代方案,如果你不能更改字段类型,就是以不同的方式解释你的时间戳... 我的意思是:如果你的应用程序要跟踪的第一个事件是 - 比如 - 2000-1-1,你可以在后端(或在数据库内部的存储过程中)实现一个过滤器,在读取时添加(2000-1-1 - 1970-1-1)秒到你的时间戳中,并在写入时减去相同的时间... 这应该可以让你多存储30年...

我们在数据库中将其保留为INT类型,不想重新设计它。 - Danyal Sandeelo
4
我会尽力为您翻译:@DanyalSandeelo: 我不想下个月付房租。我猜我们俩都得不高兴。 耸肩 - Ignacio Vazquez-Abrams
@IgnacioVazquez-Abrams 应用程序处于UAT中。所有表格都以INT格式存储日期,我不能冒着错过截止日期的风险。尽管我知道,datetime可以解决这个问题。 - Danyal Sandeelo
1
MySQL的INT类型占用4个字节,所以物理上无法存储超过2,147,483,648秒,抱歉... - MarcoS
给出相同的输出 @MarcoS - Danyal Sandeelo
1
UNIX_TIMESTAMP 函数不需要参数或者只接受一个 date 参数... 详见 https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_unix-timestamp - MarcoS

4

MySQL文档通常对函数返回的数据类型非常模糊,UNIX_TIMESTAMP()也不例外。除非我们检查源代码,否则我们只能做出有根据的猜测。

日期和时间类型概述中,我们可以看到TIMESTAMP数据类型本身具有一个已记录的范围,不依赖于服务器架构:

范围是从“1970-01-01 00:00:01.000000”UTC到“2038-01-19 03:14:07.999999”UTC。 TIMESTAMP值存储为自纪元(“1970-01-01 00:00:00”UTC)以来的秒数。 TIMESTAMP无法表示值“1970-01-01 00:00:00”,因为它相当于自纪元以来的0秒,值0保留用于表示“0000-00-00 00:00:00”,即“零”TIMESTAMP值。

即使我们确保传递了正确的日期类型:

mysql> select
    ->  str_to_date('2038-01-20', '%Y-%m-%d'),
    ->  unix_timestamp(str_to_date('2038-01-20', '%Y-%m-%d'));
+---------------------------------------+-------------------------------------------------------+
| str_to_date('2038-01-20', '%Y-%m-%d') | unix_timestamp(str_to_date('2038-01-20', '%Y-%m-%d')) |
+---------------------------------------+-------------------------------------------------------+
| 2038-01-20                            |                                                     0 |
+---------------------------------------+-------------------------------------------------------+
1 row in set (0.01 sec)

我們仍然得到了0 ,這是一個關於錯誤的愚蠢函數標誌:

如果您向UNIX_TIMESTAMP()傳遞超出範圍的日期,它將返回0。

因此,可以安全地假定UNIX_TIMESTAMP()返回TIMESTAMP類型的值,因此不支持2038+。

簡而言之:您必須在其他地方計算時間戳(即,客戶端代碼)。由於有一個PHP標籤:

$t = new DateTime('2038-01-20', new DateTimeZone('UTC'));
var_dump( $t->format('U') );
2147558400

P.S. MariaDB,MySQL的分支,有相同的限制,但它文档得更好:

MariaDB中的时间戳最大值为2147483647,相当于2038-01-19 05:14:07。这是由于底层32位限制造成的。在超出此日期的日期上使用该函数将导致返回NULL。


2038年1月19日返回的值与2147472000相同。 - Danyal Sandeelo
我们需要知道时区以及您如何计算这个数字。您是指我的PHP片段吗?:-? - Álvaro González
我正在mysql客户端中执行这个sql查询。我购买了亚马逊的Linux实例。 - Danyal Sandeelo
如果您在Amazon MySQL服务中运行SELECT unix_timestamp(str_to_date('2038-01-19', '%Y-%m-%d'))并获得2147472000,那么这意味着您的服务器时区是UTC。这是某种问题吗?您是否有后续问题或其他疑问? - Álvaro González

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接