2038 年问题 - Y2K38 问题 - 您的许多应用程序将崩溃
2038 年问题 - Y2K38 问题。
先试试这个。
退出雅虎通或 Gmail 聊天。打开您的系统日期和时间设置。将年份更改为 2038 年以后的任何年份。您可以尝试将年份设置为 2040 年。现在尝试登录到任一聊天工具。它无法登录并会给出一些错误。惊讶吗!那么,问题出在哪里?
问题的根源实际上是一些(主要)应用程序存储其日期/时间数据类型的方式。使用 POSIX 时间表示法的程序将受到此问题的影响。结构体 `time_t` 是一种值类型,它将时间存储在一个 32 位有符号整数中。它将时间存储为自 **1970 年 1 月 1 日** 以来的秒数。因此,它能够表示在总共 **231** 秒内可以寻址的时间。据此,它可以存储的最新时间是 **2038 年 1 月 19 日星期二,UTC 时间 03:14:07**。在此时间之后,32 位有符号整数的符号位将被设置,它将表示一个负数。正如我所说,时间存储为自 1970 年 1 月 1 日以来的秒数,这个负数将被添加到根据 POSIX 标准计算时间中。但由于这是一个负数,它将通过从 1970 年 1 月 1 日减去这么多秒来计算时间,这最终将生成一个历史日期时间,这将导致应用程序失败。这个时间是 1901 年 12 月,被称为回绕日期。许多操作系统中用 C 编写的应用程序也将受到影响,因为时间的 POSIX 表示法在那里被广泛使用。下面的动画以更简单的方式可视化了实际情况。此错误通常表示为“Y2038”、“Y2K38”或“Y2.038K”错误。
在 C 程序中模拟此错误。
编译后模拟此错误的 ANSI C 程序如下。程序生成的输出也附加在代码下方。此代码已参考自 此处。
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <time.h> int main (int argc, char **argv) { time_t t; t = (time_t) 1000000000; printf ("%d, %s", (int) t, asctime (gmtime (&t))); t = (time_t) (0x7FFFFFFF); printf ("%d, %s", (int) t, asctime (gmtime (&t))); t++; printf ("%d, %s", (int) t, asctime (gmtime (&t))); return 0; }
输出
1000000000, Sun Sep 9 01:46:40 20012147483647, Tue Jan 19 03:14:07 2038-2147483648, Fri Dec 13 20:45:52 1901
上述程序是一个严格的 ANSI 程序,应该可以使用任何平台上的任何 C 编译器进行编译。现在让我们看一下 UNIX 和 Windows 2000 上的 perl 脚本。此脚本已参考自 此处。
#!/usr/bin/perl # # I've seen a few versions of this algorithm # online, I don't know who to credit. I assume # this code to by GPL unless proven otherwise. # Comments provided by William Porquet, February 2004. # You may need to change the line above to # reflect the location of your Perl binary # (e.g. "#!/usr/local/bin/perl"). # Also change this file's name to '2038.pl'. # Don't forget to make this file +x with "chmod". # On Linux, you can run this from a command line like this: # ./2038.pl use POSIX; # Use POSIX (Portable Operating System Interface), # a set of standard operating system interfaces. $ENV{'TZ'} = "GMT"; # Set the Time Zone to GMT (Greenwich Mean Time) for date # calculations. for ($clock = 2147483641; $clock < 2147483651; $clock++) { print ctime($clock); } # Count up in seconds of Epoch time just before and after the # critical event. # Print out the corresponding date in Gregorian calendar # for each result. # Are the date and time outputs correct after the critical # event second?
到目前为止,似乎只有少数操作系统不受 2038 年问题的影响。例如,此脚本在 Debian GNU/Linux(内核 2.4.22)上的输出
# ./2038.pl
2038 年 1 月 19 日星期二 03:14:01
2038 年 1 月 19 日星期二 03:14:02
2038 年 1 月 19 日星期二 03:14:03
2038 年 1 月 19 日星期二 03:14:04
2038 年 1 月 19 日星期二 03:14:05
2038 年 1 月 19 日星期二 03:14:06
2038 年 1 月 19 日星期二 03:14:07
1901 年 12 月 13 日星期五 20:45:52
1901 年 12 月 13 日星期五 20:45:52
1901 年 12 月 13 日星期五 20:45:52
安装了 ActivePerl 5.8.3.809 的 Windows 2000 Professional 的失败方式是它在关键秒数后停止显示日期
C:\>perl 2038.pl
2038 年 1 月 18 日星期一 22:14:01
2038 年 1 月 18 日星期一 22:14:02
2038 年 1 月 18 日星期一 22:14:03
2038 年 1 月 18 日星期一 22:14:04
2038 年 1 月 18 日星期一 22:14:05
2038 年 1 月 18 日星期一 22:14:06
2038 年 1 月 18 日星期一 22:14:07
我们有解决方案吗?
是的,当然,全世界已经提出了许多解决这个问题的方案。这里列出了一些。
1.) 将 `time_t` 结构重新定义为 64 位。
这不是一个解决方案,因为软件的二进制兼容性将在此处中断。依赖于时间二进制表示的程序将遇到麻烦。所以我们甚至无法考虑这个。
2.) 将 `time_t` 从 32 位有符号更改为 32 位无符号。
乍一看这似乎很好,但这只会将审判日推迟到 2106 年,因为它会通过添加另一个可用位来提供更大的范围。到那时你将面临同样的麻烦。所以这是一个可行的解决方案,但不是一个实用的解决方案。
3.) 从 32 位系统切换到 64 位系统。
大多数 64 位架构使用 64 位存储来表示 `time_t`。使用这种新的(有符号)64 位表示法,新的回绕日期不会在 2900 亿年之前出现。可以肯定地预测,到 2038 年,所有 32 位系统都将被淘汰,所有系统都将是 64 位系统。
谢谢。
Ruchit S.
http://www.ruchitsurati.net/