mk-mode BLOG

このブログは自作の自宅サーバに構築した Debian GNU/Linux で運用しています。
PC・サーバ構築等の話題を中心に公開しております。(クローンサイト: GitHub Pages

ブログ開設日2009-01-05
サーバ連続稼働時間
Reading...
Page View 合計
Reading...
今日
Reading...
昨日
Reading...

2038年問題!

[ pc_tips, プログラミング ] [ Ruby ]

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

こんばんは。

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

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

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

1
2
二進数 : 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言語で動いています。放っておくと完全に誤動作します。パソコンばかりのことでもありません。あらゆる電子機器について言えることです。

これは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) からの経過秒数(十進数・二進数)
  • 残り秒数(十進数・二進数)

を表示します。

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
#! /usr/local/bin/ruby
# coding: utf-8
#
#--------------------------------------------
# 2038年問題までの残り時間計算
# ( 2038年1月19日3時14分08秒 までの残り秒数 )
#--------------------------------------------
#
require 'time'

class Problem2038
  # 問題日時 ( JST )
  DT_2038 = "2038-01-19 12:14:08"

  def calc
    begin
      # 問題日時
      tm_2038  = Time.parse(DT_2038).strftime("%Y-%m-%d %H:%M:%S")
      sec_2038 = Time.parse(DT_2038).to_i

      # 現在日時
      tm_now   = Time.now.strftime("%Y-%m-%d %H:%M:%S")
      sec_now  = Time.now.to_i

      # 残り秒数計算
      sec_remain = sec_2038 - sec_now

      # 結果出力
      str =  "PROBLEM: #{tm_2038}\n"
      str << "         #{sprintf("%13s", comma(sec_2038))} "
      str << "( #{sprintf("%032d", sec_2038.to_s(2))} ) secs.\n"
      str << "NOWTIME: #{tm_now}\n"
      str << "         #{sprintf("%13s", comma(sec_now))} "
      str << "( #{sprintf("%032d", sec_now.to_s(2))} ) secs.\n"
      str << "REMAING:\n"
      str << "         #{sprintf("%13s", comma(sec_remain))} "
      str << "( #{sprintf("%032d", sec_remain.to_s(2))} ) secs."
      puts str
    rescue => e
      $stderr.puts "[EXCEPTION][#{self.class.name}.#{__method__}] #{e}"
      exit 1
    end
  end

private

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

obj_calc = Problem2038.new.calc

以上。

Comments