グリッドコントロール/VBの2000年問題

VBの2000年問題

初音 玲 HATSUNE, Akira



まもなく1999年も終わり、20世紀最後の年である2000年がやってくる。「21世紀」という言葉に夢を抱き、こんなに区切りのいいときに生まれたことにワクワクした子供時代もあった。しかし、多くのソフトウェア技術者にとっては、Y2K(2000年問題)の解決に追われ、そのようなワクワクな気持ちも消えうせているかもしれない。
そしてまた、Visual Basicを開発に使っている人の中には「Y2K? 関係ないね」と思われている人もいるかもしれない。本当に関係ないのだろうか。どうせだったら、きっちり検証して、安心して新しい年を迎えようと思い立ったのが、この原稿の出発点だ。


Visual Basicのバージョンを再確認する

図1:Microsoft製品のリリース歴
図1:Microsoft製品のリリース歴

Visual Basic1.0の登場

 1991年6月にVisual Basic 1.0が発売されてから、Visual Basicは順調にバージョンアップを繰り返してきた。現在の最新バージョンは6.0だ。
 しかし、そのすべてのバージョンが日本語化されていたわけではない。たとえば、その記念すべきVisual Basic 1.0は英語版しか存在しない。確かにVisual Basic 1.0はまだまだWindowsのソフトを開発するには力不足なものだったが、現在のVisual Basicにも繋がるような斬新さは兼ね備えていた。私達がC言語とAPIで四苦八苦していたときに、それよりは遥かに手軽に画面がつくれていたのだから、ちょっと悔しい感じがする。実は、Visual Basicユーザーは、後年、この悔しさをもう一度味わうことになるのだ。

Visual Basic 2.0の登場と3.0

 1992年10月に待望の日本語Visual Basic 2.0が発売されたのだが、このVisual Basic 2.0は、障害が多くて使い物にならなかったために、Microsoftは、日本語版発売と前後して、世界的にはVisual Basic 3.0をリリースしている。
 しかし、このVisual Basic 3.0も日本語化されていないのだ。そのため、延々3年近く世界との格差の中に身を置くことになるのである。もちろん、その3年の間でも一部の先駆的な開発者はVisual Basic 3.0を輸入して日本語アプリを作成していた。
 それは、日本語化されていないのは開発環境だけであり、適切なフォントをfontプロパティに指定しておくことで、日本語が使えるアプリ自体を作成することができたからだ。もちろん、IMEの制御もプロパティを設定する方法ではできなかったが、それについてもAPIを使えば同等機能を実現することは可能だった。しかしながら、大多数の日本の開発者はVisual Basic 3.0の存在を知らずにVisual Basic 2.0で開発しつづけていた。
 Visual Basic 2.0は、発売時期がWindowsマシンの普及と時を同じくしており、数多くのWindowsアプリがVisual Basic 2.0で作られることになった。そして、それは現在でも「Visual Basic 2.0で作られたソフト資産」という形で存在しつづけている。

32bit化への改革〜Visual Basic 4.0

 Visual Basic 2.0の次に発売されたVisual Basic 4.0(1995年発売)の16bit版は、

2.0(3.0)→16bit版4.0→32bit版4.0

のように橋渡し的なバージョンであり、開発に使えるような品質や速度を確保するためには、それなりのテクニックや制限事項が存在した。そのため、16bitの時はVisual Basic 2.0を使うという選択をしたところも多いらしい。これが、現在まで「Visual Basic2.0で作られたソフト」が続いている要因のひとつだ。

コンパイラ搭載〜Visual Basic 5.0

 Visual Basic 4.0で32bit化を果たした後にどのような展開をみせるのかが注目されていたが、Visual Basic 5.0では、ついにインタープリタからコンパイラに実行ファイルの形式が変更になった。
 これで複雑なロジックを実装しても十分な処理速度が確保できるようになり、大規模アプリケーション構築の基礎ができた。もっとも、日本では、Visual Basic 2.0で大規模開発を行なっている事例もあるらしいので、やっと言語仕様が実情に追いついてきたとみるのが正しいのかもしれない。
 Visual Basic 5.0を語るうえで忘れてはいけないのが、SP(Service Pack)の存在だ。SPはバグの修正だけではなく機能の拡張なども伴っているときがある。
 その1例が、Visual Basic 5.0 SP2だ。このSP2は、ActiveXコントロールとして、コンパイルした実行ファイルや付属のActiveXコントロールが、マルチスレッド対応になる点が特徴だ。そのため、SP2より前とSP2以降では、同じVisual Basic 5.0でもまったく別物といってもよい。このSP2こそ後に発売されれるVisual Basic 6.0の一部の機能を先取りしたものなのだ。

そしてVisual Basic 6.0へ

 Visual Basic 6.0は、Visual Basic 5.0(SP2)に、イントラネット系の開発機能やADO、COBOL的な言語拡張を施したものだ。また、Windows 98対応という点も重要な使命だろう。

2000年問題(Y2K)を考える

 Visual Basicの2000年問題を調査する前に、なぜ、このようなことが起こったのかを明確にしておきたいと思う。
 ことの発端は、メモリやディスク資源の有効利用やキー入力などの省力化だったはずだ。

1999/01/01

とするよりも、

990101

としたほうが、バイト数で2バイト、キー入力で2打鍵分少なくて済む。たった2バイトだと侮ってはいけない。昔にしても今にしても計算機の性能ギリギリのシステムを要求されることはあるのだ。
 もちろん、昔のほうが限界のハードルが低かったので、ありとあらゆるものが性能ギリギリを要求していたことだろう。そのような状況下では、1データあたりの領域が少なく済めば、メモリ上やディスクに格納できる件数も増えるし、アクセス速度も2バイト分速くなり、その差がシステムの性能差を左右することになる。もちろん、伝票を大量に入力するのだって、“19990110”よりも“990110”のほうが速い。
 こういった事情から2000年問題の下地はでき上がっていった。そして、その根底には「そんな何十年も使われるわけないし、2000年になる頃にはほかのことをやっているかもしれないし、それよりかは… 」ということもあり、西暦2桁を4桁に変換して評価するというロジックすら組み込まれなかったこともある。

具体的な問題とは

 2000年問題の具体的な問題点は、

(1)1999年よりも2000年の方が過去と評価してしまう
(2)2000年をうるう年として処理できない

の2点が大きなところだろう。
 (1)については、

'99'>'00'

という不等式が成立してしまうことが原因である。これについては西暦を4桁に変換してから、

'1999'>'2000'

として判断すればよいはずだ。しかし、何年以降だったら19XXと変換して、何年以前だったら20XXと判断すればよいかは、どこにも決まりがあるわけではなく、各アプリケーションの作成者により決定される。
 そのため、そのようなシステム同士をネットワークで接続したときには、判断ポイントの違いにより動作不良に陥ることもあり得る。
 (2)は、見落とされがちな点だが、(1)よりも致命的な問題だろう。うるう年というのは、太陽の位置と暦のずれを調整する日数調整方法で、


ものだ。よって、2000年はうるう年なのだ。
 しかし、00年を1900年として認識していると、普通の年として処理してしまう。こうなってしまうと日数計算などの問題ではなく、2000年2月29日という日が存在しないことになってしまうのだ。

Visual Basicは大丈夫か?

 プログラムコードの中で明確に西暦2桁を4桁に変換しているのならば、その部分を見直すだけで済む。問題はVisual Basicの日付型変数や日付型関数での扱いである。
 Visual Basicでは日付はシリアル値と呼ばれるバイナリ値で管理されている。このシリアル値にさえ変換できていれば、2000年問題にぶつかることはない。
 しかし、いわゆる文字列として扱われる画面入力やテキストファイルからの読み込みなどでは差が生じる。そこで、リスト1のようなコードを使ってどのようにVisual Basicが判断するかを探ってみる。

リスト1:「サンプル1」のコード
Private Sub cmdCheck_Click()
  If IsDate(txtDate.Text) Then
    lblResult.Caption = "OK:" & _
     Format$(CVDate(txtDate.Text), "YYYY/MM/DD")
  Else
    lblResult.Caption = "NG"
  End If
End Sub

Visual Basic 3.0

 私はVisual Basic 2.0をインストールした環境をもっていないので、同時期に世界的に使われていたVisual Basic 3.0で確認した。
 西暦4桁を指定したときは、もちろん正常に動作した(図2)。しかし、西暦を2桁にすると正常に動作しない(図3)。これは、1900年と認識してしまうからだ(図4)。

図2:西暦4桁を指定したときは正常に動作した
図2:西暦4桁を指定したときは正常に動作した

図3:西暦を2桁にすると正常に動作しない
図3:西暦を2桁にすると正常に動作しない

図4:1900年と認識してしまう
図4:1900年と認識してしまう

 日付入力をテキストボックスにしているときは、そのテキストボックスのLostFocusイベントで4桁に変換して、フォーム共通の変数として日付型変数を確保して、そこに保存するのがよいだろう。そのときには、該当日付の論理的な構造を理解して4桁化することを心がける必要がある。
 基本的には扱っている年の最小値よりも小さかったら20を付加する。たとえば、1980年以降の日付しか扱う予定がないのならば、リスト2のようになる。

リスト2:扱っている年の最小値よりも小さかったら20を付加する(1980年の場合)
If left$(strDate,2) < "80" Then
  dtmDate = CDate("20" & strDate)
Else
  dtmDate = CDate("20" & strDate)
End If

 もちろん、日付を入力するようなVBXを使っているときには、そのVBXに依存することも忘れてはいけない。

16bit版Visual Basic 4.0

 作成したサンプル1は、16bit版のVisual Basic 4.0で読み込むと図5のような画面が表示されて、自動的にフォーマットの変換が行なわれる便利な機能が備わっている。

図5:16bit版のVisual Basic 4.0で読み込むと表示される画面
図5:16bit版のVisual Basic 4.0で読み込むと表示される画面1
図5:16bit版のVisual Basic 4.0で読み込むと表示される画面 2

 このようにして作成したサンプル2を実行すると、西暦2桁を指定するとやはり1900年として処理される(図6)。よって、もちろん00年2月29日を処理することはできない。

図6:西暦2桁を指定するとやはり1900年として処理される
図6:西暦2桁を指定するとやはり1900年として処理される

 つまり、16bit版Visual Basicアプリケーションでは、西暦を2桁で指定しているときには、2000年問題が発生する可能性を秘めているということだ。

32bit版Visual Basic 4.0

 サンプル2を32bit版のVisual Basic 4.0で読み込むためには、makファイルの拡張子をvbpにする必要がある。この拡張子の変更を行なえば、やはり自動的にフォーマットの変換が行なわれる。
 フォーマット変換後のサンプル3では、西暦2桁を2000年として認識してくれる(図7)。よって、2月29日の処理も問題なく動作する(図8)。

図7:西暦2桁を2000年として認識してくれる
図7:西暦2桁を2000年として認識してくれる

図8:2月29日の処理も問題なく動作する
図8:2月29日の処理も問題なく動作する 1
図8:2月29日の処理も問題なく動作する 2

Visual Basic 5.0

 32bitのVisual Basic 4.0と同様にVisual Basic 5.0でも無事に動作する。どうやら、32bit版のVisual Basicアプリでは当面心配は要らないようである。
 ただし、2000年問題とは異なるが、Visual Basic 4.0(32bit)→Visual Basic 5.0の変換ではフォームの定義が上手く変換されずに動作が不安定になることがあるので注意してほしい。
 この不安定な事象は、簡単なフォームではあまり発生しないが複雑なフォームで発生する可能性が高い。私は、Visual Basic 5.0で開いたVisual Basic 4.0のフォームを参考にしなから、新規にVisual Basic 5.0でフォームを作っていくことで、この不安定さを排除したバージョンアップ作業を無事やり遂げたことがある。参考にしてほしい。
 なお、2000年問題についてはVisual Basic 6.0でも同様に正常に動作する。

そのほかの2000年問題

 これまで見てきたように、西暦を4桁で入出力してさえいれば、Visual Basicの内部処理では問題が発生しないことがわかった。
 しかし、なぜか一部の人たちは日付型ではなく、文字列として日付を扱うのが好きなようで、String変数に日付を入れたり、データベースでも文字列フィールドに日付を入れていたりする。このとき西暦を4桁で扱っていればいいのだが、2桁しか確保していなかったとしたら、やはり問題は発生してしまうだろう。
 実は、そのほかにもDAOやADOなどのデータアクセスメソッドに関して、2000年問題が発生する可能性がマイクロソフトから報告されている。表1に簡単にまとめてみた。

表1:データアクセス関連の製品ガイド
製品名 バージョン 注意点 備考
ADO 1.5 OLE DBやDAO (MDAC)に依存  
2.0 OLE DBやDAO (MDAC)に依存  
DAO 3.0 西暦を4桁で指定することで、格納先の変換ロジックを回避することを推奨  
3.5 西暦を4桁で指定することで、格納先の変換ロジックを回避することを推奨  
3.6 西暦を4桁で指定することで、格納先の変換ロジックを回避することを推奨  
Jet 3.0 MSJT3032.dllを3.000.4513以上にする必要あり Access 95
3.5 Jet3.5用のSP2を適用する必要あり Access 97、VS6
4.0 Jet4.0用のSP1を適用する必要ある SQL Server 7
4.0SP1 対応済 Office2000、VB6.0(SP3)
MDAC 1.5 MSDADC.dllを1.50.9801(修正版1.5)にする必要あり  
2.0 MDAC 2.0用のSP1もしくはSP2を適用する必要あり  
2.1 対応済(ただし、SPはSP2まで提供されている)  
OLE DB 1.5 MDAC 2.0用のSP1を適用する必要あり  
2.0 MDAC 2.0用のSP1を適用する必要あり  
ODBC 2.5 データソースに依存 Office 95
3.0 データソースに依存 Office 97
3.5 データソースに依存 MDAC1.5、IIS
3.51 データソースに依存 MDAC2.0、VS6.0
OLEオートメーション   西暦2桁のときは、1930〜2029年として処理  
RDS 1.5 OLE DBに依存 IIS
2.0 OLE DBに依存 VS6.0

注:http://www.microsoft.com/japan/year2k/product/dataaccs.htmlより抜粋

2030年問題の予感

 表1にあるように、OLEオートメーションには、西暦を2桁で指定したときには、1930〜2029年として扱うというロジックが含まれている。つまり、

If left$(strDate,2) < "30" Then
  dtmDate = CDate("20" & strDate)
Else
  dtmDate = CDate("20" & strDate)
End If

ということになるだろう。
 これは、2029年から2030年にかけて、同様の問題が発生する可能性を示唆している。
「30年先じゃないか」と安心してはいけない。汎用機などで発生している2000年問題の一部は、1960年代や1970年代に開発された母体を拡張、改変していった過程で熟成されているものもあるのだ。今、この機会に日付は西暦4桁で日付型へとロジックに統一することをお勧めする。

最後に

 Visual Basicの2000年問題を確認してきたが、心当たりのある項目はあっただろうか。
 もしあったときは、ぜひ昔のソースを引っ張り出してきて動作確認したほうがよいだろう。そして、問題があるようならばプログラム提供先にその情報も提供したほうがよいだろう。いざとなったらプログラムを修正することで対応が可能なのだから…
 なお、Windows自体やOffice製品にも2000年問題は存在するので、次のMicrosoftのWebSiteでチェックすることをお勧めする。

http://www.microsoft.com/japan/year2k/

 自分で該当ソフトウェアの修正ができないこちらのほうが問題が大きいかもしれない。
 図1にあるように開発ツールとWindowsのバージョン、OfficeツールやIEのバージョンには関連性があるので、その整合性を確保しつつ、何が制限事項と残るのかを考慮していってほしいと思う。


VB Magazine ライブラリ| Visual Basic WorkGroup
int21 ホームページ| PCDN ホームページ

PCDN
Copyright (c) 1998 int21 CorporationAll Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp