2038年問題(Y2K38バグとも呼ばれます)は、2038-01-1903:14:07を過ぎた時間を処理するときに一部のコンピューターシステムで発生する可能性のある問題を指します。
UnixやUnixベースのシステムなど、多くのコンピュータシステムは、グレゴリオ暦を使用して時間を計算しません。 1970年1月1日からの秒数として時間を計算します。したがって、これらのシステムでは、時間は大きな数値(つまり、1970-01-01 00:00:00から経過した秒数)として表されます。これは通常、エポック時間、Unix時間、Unixエポック時間、またはPOSIX時間と呼ばれます。これを書いているとき、Unix時間は1560913841です。そして、この次の行を書いているとき、Unix時間は1560913879に増加しています。
2038年問題は、多くのシステムがこの数値を符号付き32ビットの2進整数として格納しているという事実が原因で発生します。符号付き32ビット整数の範囲は-2,147,483,648から2,147,483,647です。これは、表現できる最新のエポック時刻が2147483647であることを意味します。これは、2038年1月19日火曜日の03:14:07に発生します。
その後、結果はシステムに大きく依存します。多くのシステムでは、整数のオーバーフローが発生し、それ以降は折り返され、負の数として内部に格納されます。その結果、1秒後、時刻は2038年1月19日ではなく1901年12月13日と解釈されます。
ただし、使用しているアプリケーションによっては、結果が異なる場合もあります。オペレーティングシステムに問題がない場合でも、独自のコードに問題がある可能性があります。たとえば、Unix時間を返すカスタムコードを記述し、それを符号付き4バイト整数で格納すると、問題が発生します。このような場合、8バイト整数を使用するようにコードを書き直すだけで十分な場合があります。
このウェブサイトはすべてデータベースに関するものなので、ここにいくつかのデータベースの例があります。
例1-MySQL
MySQLでは、TIMESTAMP
データ型は、「1970-01-01 00:00:01.000000」UTCから「2038-01-1903:14:07.999999」までの日付/時刻をサポートします。したがって、このデータ型を使用するデータベースにはY2K38バグがあると言えます。
MySQLには、UNIX_TIMESTAMP()
と呼ばれる組み込み関数もあります。 ご想像のとおり、これはUnixタイムスタンプを返します。
UNIX_TIMESTAMP()
関数は、Unix時間に使用する日付(つまり、「1970-01-01 00:00:00」UTCから指定した時間までの秒数)を指定できるオプションの引数を受け入れます。引数値の有効な範囲は、TIMESTAMP
の場合と同じです。 データ型は、「1970-01-01 00:00:01.000000」UTCから「2038-01-1903:14:07.999999」UTCです。この関数に範囲外の日付を渡すと、0
が返されます。 。
この関数を使用して、「2038-01-19 03:14:07.999999」を過ぎた日付からUnix時間を返そうとすると、次のようになります。
SELECT UNIX_TIMESTAMP('2038-01-20') Result;
結果:
+--------+ | Result | +--------+ | 0 | +--------+
0
を取得します 日付引数がサポートされている範囲外であるためです。
関連するバグレポートが2005年にMySQLチームに対して提起されましたが(詳細の一部は異なっているように見えますが)、この記事の執筆時点ではまだ対処されていません。
TIMESTAMP
の制限に対処するために、同様の問題も発生しました。 データ型。これもまだ対処されていません。
例2– SQL Server
SQL Serverには現在、MySQLのUNIX_TIMESTAMP
に相当するものはありません。 働き。したがって、エポック時間を返す必要がある場合は、次のようにする必要があります。
SELECT DATEDIFF(SECOND,'1970-01-01', GETUTCDATE());
これは、2038年問題より前の日付には問題ありません。その日付以降は、DATEDIFF()
が原因で、問題が発生します。 関数は結果をintとして返します データ・タイプ。 int データ型の範囲は-2^31(-2,147,483,648)から2 ^ 31-1(2,147,483,647)です。
「2038-01-1903:14:07」より後のエポック時間を戻そうとすると、次のようになります。
SELECT DATEDIFF(SECOND,'1970-01-01', '2038-01-19 03:14:08') AS 'Result';
結果:
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
幸い、DATEDIFF_BIG()
もあります 関数。これは、結果を bigint として返すことを除いて、まったく同じことを行います。 データ型。
したがって、この問題を解決するために、前の例を次のように書き直すことができます。
SELECT DATEDIFF_BIG(SECOND,'1970-01-01 00:00:00', '2038-01-19 03:14:08') AS 'Result';
結果:
+------------+ | Result | |------------| | 2147483648 | +------------+
bigint データ型は8バイトを使用します( int の4バイトとは対照的) )、したがって、DATEDIFF_BIG()
に切り替えるかどうかを決定する必要があります 今または後で。アプリケーションが将来の日付を処理する場合は、後で実行するのが賢明かもしれません。