tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトル日付関数DateValueについて
記事No15072
投稿日: 2011/01/08(Sat) 09:56
投稿者おじん
VB6,WindowsXP
ヘルプではDateValueは1900/1/1のシリアル値を[1]とすると
ありますが、私のパソコンでは[2]が返ります。
なぜなのかわかりません。教えてください。
ちなみに、DateSerial(1900,1,1)も[2]となり、また、Excel
(2003)では[1]が返ります。
よろしくお願い致します。

[ツリー表示へ]
タイトルRe: 日付関数DateValueについて
記事No15073
投稿日: 2011/01/08(Sat) 11:51
投稿者花ちゃん
> VB6,WindowsXP
> ヘルプではDateValueは1900/1/1のシリアル値を[1]とすると
> ありますが、
どのヘルプのどこに書いてあった事でしょうか?

MSDN VB6.0 の DateValue 関数の解説には、VB5.0 の頃より現在でも下記内容かと。
http://msdn.microsoft.com/ja-jp/library/cc410224.aspx


>私のパソコンでは[2]が返ります。
下記のような事で見ていませんか?
CLng(DateValue("1900/1/1"))

> なぜなのかわかりません。教えてください。
> ちなみに、DateSerial(1900,1,1)も[2]となり、また、Excel
> (2003)では[1]が返ります。
Excel の DateValue 関数 と VB6.0 の DateValue 関数 は別かと。
Excel で =DATEVALUE("1901/01/01") を見て下さい。 367 になりませんか(VB6.0 と同じ)
因みに、Excel の場合、1900 年はうるう年としているようです。(バグ? 故意)

MSDN(ヘルプ) で DATE データ型 を調べて見て下さい。(下記一部抜粋)
http://msdn.microsoft.com/ja-jp/library/82ab7w69.aspx

DATE 型のデータは 8 バイトの浮動小数点数値で、実数部が日付、小数部が時刻を示します。日付は 1899 年 12 月 30 日の午前零時がゼロで、1 日ごとに 1 増加します。時刻は 1.0 が 24 時間の小数です。たとえば、次のように表されます。

日付と時刻 対応する数値
1899 年 12 月 30 日午前零時 0.00
1900 年 1 月 1 日午前零時 2.00
1900 年 1 月 4 日午前零時 5.00

これで、2 になる理由がご理解頂けたでしょうか?

[ツリー表示へ]
タイトルRe^2: 日付関数DateValueについて
記事No15074
投稿日: 2011/01/08(Sat) 14:01
投稿者おじん

事の発端は「シリアル値を求める」ことで、「シリアル値とは1900年1月1日を1とし、
その日からの通算日数と時刻を表す値のことです」とあり、DateValue("1900/1/1")
の値が2になったことで混乱しはじめました。
シリアル値=DateValue(ymd)の値-1と計算すればよかったようです。

ありがとうございました。

[ツリー表示へ]
タイトルRe^3: 日付関数DateValueについて
記事No15075
投稿日: 2011/01/08(Sat) 14:49
投稿者花ちゃん
> 事の発端は「シリアル値を求める」ことで、「シリアル値とは1900年1月1日を1とし、
> その日からの通算日数と時刻を表す値のことです」とあり、DateValue("1900/1/1")
> の値が2になったことで混乱しはじめました。

だから、それはどこに書いてあった事ですか?
Excel の関数での説明の中での事じゃないのですか?
(Excel では、1900/02/29 が存在するが VB系では、存在しない)

> シリアル値=DateValue(ymd)の値-1と計算すればよかったようです。

違いますよ、Excel の関数と VB6.0 VBA 最近のVB2010 とでは、1900/02/29 の
扱いが違うので、 1900/02/29 以降の値は、Excel もVB も同じになるので
-1 したら問題になりますよ。
Debug.Print CDate("2")            '1900/01/01
Debug.Print CDate(Val("2") - 1)   '1899/12/31

Debug.Print CDbl(DateValue("1900/02/28"))   '60
Debug.Print CDbl(DateValue("1900/03/01"))   '61

Excel
=DATEVALUE("1900/02/28")   '59
=DATEVALUE("1900/02/29")   '60
=DATEVALUE("1900/03/01")   '61

尚、前回の私の回答中、
>Excel の場合、1900 年はうるう年としているようです。(バグ? 故意)
は、調べた結果 Excel では、旧のバージョンとの互換性のため、1900 年を
うるう年として扱っているようです。(詳しくは下記参照)
(逆を言えば、1900/01/01 を 1 としないと計算が合わない)
http://support.microsoft.com/kb/214019/ja
http://support.microsoft.com/kb/214326/ja

[ツリー表示へ]
タイトルRe^4: 日付関数DateValueについて
記事No15076
投稿日: 2011/01/09(Sun) 06:24
投稿者おじん

> だから、それはどこに書いてあった事ですか?
ネット情報は個人の判断で、と言うことでしょうが、下記記事からです。
確かにExcel関連らしいです。
www3.tokai.or.jp/excel/kansu/hiduke.htm

> 違いますよ、
VB6では1900年が閏年と判断しない重大なバグあることでした。
これまで西暦年のことは気にしないテーマでしたのですが、最近、
天文の問題に興味が移り、その中にシリアス値とかユリウス日とか
日数計算が必要になりました。
便利な関数を見つけても「間違い」もあり気をつけないといけない
ことを知りました。

他にも色々と調べてアドバイスいただき有難う御座いました。

[ツリー表示へ]
タイトルRe^5: 日付関数DateValueについて
記事No15077
投稿日: 2011/01/09(Sun) 08:53
投稿者花ちゃん
> ネット情報は個人の判断で、と言うことでしょうが、下記記事からです。
> 確かにExcel関連らしいです。
> www3.tokai.or.jp/excel/kansu/hiduke.htm
VB6.0 を使っているならVB6.0 のヘルプ(MSDN)を見ましょう。
Excel の関数を使っているならExcelの関数のヘルプをVBAならVBAのヘルプを参照すべき
ではないでしょうか?
又、バージョンによっても仕様が変更される場合もありますので、正しい見方をしないと。

> > 違いますよ、
> VB6では1900年が閏年と判断しない重大なバグあることでした。

私が違いますよと言ったのは、貴方の発言の
>シリアル値=DateValue(ymd)の値-1と計算すればよかったようです。
の投稿に対してで、VB6.0 のバグとは言ってませんし、どこにも書いていないでしょう。
(この記事を見た人が誤解されると困るので敢えてもう一度)
貴方が、バグと言い切るならその根拠を示さないと。

うるう年の基準には、誤差を取り除くため 4で割り切れ400 で割り切れない年は
うるう年としないという規定により、1900 年はうるう年ではありませんので、VB6.0 の
方が正しいのであってバグではありません。

Excel でも、Excel 95 までのバージョンの Excel が日付データとして扱えるのは
1900 〜 2078 年であるため、うるう年に関する 100 および 400 の除外規則が適用される
年は 1900 年だけです。ただし Excel では、他のプログラム(Lotus 1-2-3 や Multiplan
等)との互換性のため、1900 年をうるう年として扱います。とあるように、バグではなく
仕様と言うべきかと思います。

但し、Excel では、1900/2/29 が存在すると言う事とExcel 2000等では 1900 年 3 月 1日
までの日付の曜日を計算する場合、関数は、誤った結果を返すと言う矛盾は生じますが。
(Excel でも1900/3/1 以前の曜日を求める必要があるならシステムの日付を使って独自に
計算すれば正しい曜日が得られるし、1900/3/1 以降なら問題は発生しない。)

私の回答に信憑性がないにしても先に紹介した下記、MS のサイトの記事は、今一度
よく読んでおいて下さい。

うるう年を判定する方法
http://support.microsoft.com/kb/214019/ja

出来れば原文(英文)を
http://support.microsoft.com/kb/214326/ja

[ツリー表示へ]
タイトルRe^5: 日付関数DateValueについて
記事No15078
投稿日: 2011/01/09(Sun) 14:14
投稿者魔界の仮面弁士
VB2〜VB6 の日付値(≠日付型)が用いる日付シリアルは「OLE オートメーション」で
使われる物であり、OADate とも呼ばれる実装です。これは表計算ソフトのそれとは異なり
「1899/12/30 からの経過日数」として算出されています。シリアル値でみると、

 1899/12/30         =>    0.0
 1899/12/31         =>    1.0
 1900/01/01         =>    2.0

という実装になっています。
整数部で日付を管理し、小数部(の絶対値)で時刻を管理しています。
1900年3月以降の日付においては、表計算ソフトの日付シリアルと同じ値なので
都合が良かったわけですね。(No.15075 でも解説されていますね)



> 天文の問題に興味が移り、その中にシリアス値とかユリウス日とか
(中略)
> VB6では1900年が閏年と判断しない重大なバグあることでした。
もしかしてユリウス暦と混同されていないでしょうか。

1900年は、ユリウス暦では閏年に当たりますがグレゴリオ暦では平年です。
VB6の標準カレンダーはグレゴリオ暦なので、VB6の実装は間違っていません。
(既に No.15077 でも解説されていますけれどね)

一方、Excel の日付シリアル実装がバグっているかというと、これも微妙な所があります。

かつて Lotus 社の「1-2-3」という当時トップシェアを誇る表計算ソフトにおいては、
シリアル値 0 を 1900/01/00 としていましたし、また、1900/02/29 という
本来は存在しない日付が扱われていました。

もちろん、1900/01/00 や 1900/02/29 が、正しい日付ではありませんから、
ある意味ではバグと言えます。

しかしこれは、当時の低性能コンピュータでは、メモリ効率や処理速度を優先するが故に、
実装をできる限り単純化して実装していたという事情が要因としてあります。
(1900年や2100年という日付が必要になるケースは稀ですので)

# その実装の是非については、後の Y2K 問題にも絡んでくることになるのですが、
# それはまた別の話として…。

かといって、後継バージョンでこの仕様を改めてしまうと、日付データに
ズレが生じるため、互換性という観点で問題があります。そのため、Excel などの
他社製品においては、その日付実装を「あえて」採用しているわけです。

ゆえに現行の表計算ソフトにおいては、この実装方法はバグであるというよりも、
過去の(限定的な)実装をあえて踏襲したわけで、その意味では「仕様」と言えます。
(そもそも、Excel で扱える日付範囲は、VB が扱える日付範囲よりも少ないです)


ただし Excel に話を限定したとしても、
> 事の発端は「シリアル値を求める」ことで、「シリアル値とは1900年1月1日を1とし、
という表現は(間違いではないにしても)、必ずしも正確では無かったりします。

なぜならば「シリアル値 0 を 1904/01/01 とするモード」も存在するからです。
http://support.microsoft.com/kb/214330/ja
http://developer.apple.com/jp/qa/ops/ops23.html


> 天文の問題に興味が移り、その中にシリアス値とかユリウス日とか
> 日数計算が必要になりました。
Visual Basic においては(VB6 も VBA も含めて)、日付は基本的に
グレゴリオ暦が採用されていますが、実は回教暦も使えるようになっています。

「Calendar =vbCalHijri」を実行することで回教暦に切り替わり、
グレゴリオ暦に戻すには「Calendar = vbCalGreg」を実行すれば OK です。
(回教暦/ヒジュラ暦は、日本ではイスラム暦と呼ばれる事も多いですね)

なので、
 Calendar = vbCalHijri
 Debug.Print IsDate("2000/02/30")    'True
 Debug.Print IsDate("2000/03/31")    'False
 Calendar = vbCalGreg
 Debug.Print IsDate("2000/02/30")    'False
 Debug.Print IsDate("2000/03/31")    'True
という結果になったりします。


回教暦は太陰暦の一種です。
西暦とは異なり、一か月は 29日 or 30日であり、一年は354日です。
また、日本の旧暦とは異なり、閏月や閏年といった補正も行われません。

[ツリー表示へ]