2038年問題!

Updated:


** 更新履歴 **
[2015-01-03 14:00] ソーススクリプト修正、Gist アップロード
[2020-01-30 20:00] ソーススクリプト整形

こんばんは。

今日は「2038年問題」についてです。

一般に C 言語では、 UNIX の仕様に基づいて時刻を 1970年01月01日00時00分00秒(UTC(世界標準時))からの経過秒数で表しています。そのうちの多くが、時刻を記録する領域として32ビット(4バイト)の符号付き整数を使用しています。すなわち、二進数で32桁分(10進数で2の32乗)の数が用意されていることになります。

但し、符号付き整数では、二進数の最上位(下位から32桁目)が 0 の場合は正の数、 1 の場合は負の数として扱う。このため、正の数として扱える上限の数は、

二進数 : 01111111 11111111 11111111 11111111
十進数 : 2,147,483,647 ( = 2の31乗 - 1 )

となる。

つまり、1970年01月01日00時00分00秒 から 21億4748万3647秒 経過した 2038年1月19日3時14分07秒(UTC)、日本時間(JST)では 同12時14分07秒 までの時刻は、正の数として正常に処理することが可能だが、これを1秒でも超えると最上位が 1 の負の数となり、プログラムが誤作動する。

こういうこと。

2038/01/19
03:14:05
01111111 11111111 11111111 11111101 2,147,483,645
2038/01/19
03:14:06
01111111 11111111 11111111 11111110 2,147,483,646
2038/01/19
03:14:07
01111111 11111111 11111111 11111111 2,147,483,647
2038/01/19
03:14:08
10000000 00000000 00000000 00000000 -2,147,483,648
2038/01/19
03:14:09
10000000 00000000 00000000 00000001 -2,147,483,647

勘違いしやすいのは、2038年1月19日3時14分07秒(UTC)を 1 秒越えたときは 0 でも -1 でもなく、 -2,147,483,648 になってしまうということ。 つまり、2038年01月19日03時14分08秒(UTC)は 1970年01月01日00時00分00秒(UTC)から 2,147,483,648 秒 過去ということ。

うちは C 言語で開発してないから関係ない、なんてことは言えない。使っているパソコン自体が殆ど C 言語で作成されたプログラムで動いている。放っておくとほぼ確実に誤動作する。(32ビット環境なら)
パソコンばかりのことでもない。あらゆる電子機器について言えること。

これは2000年問題より深刻な問題だと思う。 早急に(と言っても、17年後までに)64ビットの符号付整数で作り直すなどの対応が必要である。 無論、それまでには時代が変わってあらゆる事が今とは異なっていると思うが・・・

※ちなみに Ruby は 1.9.2 以降は2038年問題に対応しているようだ。

問題の時間までの残り秒数を計算するRubyスクリプト

Rubyで問題の時間までの残り秒数を計算する超簡単なスクリプトを作成してみた。
(2038-01-19 12:14:07 までは正常。2038-01-19 12:14:08 からが問題)
実行すると、

  • 問題日時 2038-01-19 12:14:08(JST)の 基準日時 1970-01-01 09:00:00(JST) からの経過秒数(十進数・二進数)
  • 現在日時の 基準日時 1970-01-01 09:00:00(JST) からの経過秒数(十進数・二進数)
  • 残り秒数(十進数・二進数)

を表示する。

File: problem_2038.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#! /usr/local/bin/ruby
#
# 2038年問題までの残り時間計算
# ( 2038年1月19日3時14分08秒 までの残り秒数 )
#
# date          name            version
# 2011.10.06    Masaru Koizumi  1.00 新規作成
# 2015.01.03    Masaru Koizumi  1.01 Shebang string 追加等
# 2020.01.30    Masaru Koizumi  1.02 ソース整形
#
# Copyright(C) 2011-2020 mk-mode.com All Rights Reserved.
#---------------------------------------------------------------------------------
# @param: [YYYYMMDDHHMMSS]
#---------------------------------------------------------------------------------
# 2019-01-15 01:34:08 で「残り 600,000,000 秒」
# 2020-02-01 00:14:08 で「残り 567,000,000 秒」
#++
require 'time'

class Problem2038
  DT_2038 = "2038-01-19 12:14:08"

  def initialize
    @tm_2038  = Time.parse(DT_2038).strftime("%Y-%m-%d %H:%M:%S")
    @sec_2038 = Time.parse(DT_2038).to_i
    tm = Time.now
    tm = Time.strptime(ARGV[0], "%Y%m%d%H%M%S") if ARGV[0]
    @tm_target  = tm.strftime("%Y-%m-%d %H:%M:%S")
    @sec_target = tm.to_i
  end

  def calc
    sec_remain = @sec_2038 - @sec_target
    str =  "PROBLEM: #{@tm_2038}\n"
    str << "         %13s"     % comma(@sec_2038)
    str << "( %032d ) secs.\n" % @sec_2038.to_s(2)
    str << " TARGET: #{@tm_target}\n"
    str << "         %13s"     % comma(@sec_target)
    str << "( %032d ) secs.\n" % @sec_target.to_s(2)
    str << "REMAING:\n"
    str << "         %13s"     % comma(sec_remain)
    str << "( %032d ) secs."   % sec_remain.to_s(2)
    puts str
  rescue => e
    $stderr.puts "[EXCEPTION][#{self.class.name}.#{__method__}] #{e}"
    exit 1
  end

private
  # カンマ挿入
  def comma(str)
    return str.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1,')
  end
end

Problem2038.new.calc

以上。





 

Sponsored Link

 

Comments