1903


VBSでの日付の加算について、どのような仕様になっているのか知っている方がいたら教えてください。

[code]

    ' (1)
    x = #10:10:10#
    MsgBox CStr(x + x) ' 20:20:20
    
    ' (2)
    x = #1899-12-29 00:00:01#
    y = #1899-12-30 00:00:01# ' #00:00:01#と同じ
    MsgBox CStr(x + y) ' 1899/12/29
    MsgBox CStr(y + x) ' 同上
    
    ' (3)
    x = #1899-12-29 00:00:01#
    MsgBox CStr(x + x) ' 1899/12/28 0:00:02
    
    ' (4)
    x = #1899-12-29 00:00:01#
    y = #1899-12-31 00:00:01#
    MsgBox CStr(x + y) ' 0:00:00
    
[/code]

(1)はいいでしょう。`x + (y - 基準日)`で求まります。

(2)は、加算する一方のみが基準日である1899/12/30 00:00:00よりも前の場合に計算方法が切り替わることを表しています。
基準日を「またぐ」場合とみなせ、`x < 基準日 && 基準日 <= y`の場合は`x - (y - 基準日)`、`y < 基準日 && 基準日 <=
x`の場合は`y - (x - 基準日)`となっているようです。

(3)は、加算する両方が基準日よりも前の場合にも、やはり計算方法が切り替わることを表しています。 (1)と同じ計算方法になっているようです。

(4)は、基準日より前の時間の流れ方と、基準日より後の時間の流れ方が異なっていることを表しています。基準日からどれくらい離れているかの量が、x(-23:59:59)とy(+24:00:01)とで違うにもかかわらず、結果が0:00:00になっています。
この結果と、(2)を整合させるための条件が分かりません(予想では30日というのが特別扱いされている・・・？未検証)。

この他にも、上で挙げた条件以外の条件が絡んできそうで悩んでいます。
「ここに仕様があるよ」という意見と、「こういう条件があるよ」というのを知っている方は教えてください。 お願いします。

追記: 既存の加算を使ったコードを挙動を変更することなく移植する必要があるため、DateAddを使えばいい、という話ではありません。


CC BY-SA 3.0
2014-12-17T08:52:39.173
1897
2022-05-27T01:06:59.260
2022-05-27T01:06:59.260
3060
4482
question
vbs
VBSでのDate同士の加算の仕様を教えてください


当方 VBS にはあまり詳しくないので細かい用語は間違っているかもしれませんが… これは Date を `CDbl` を通して内部表現を見ればわかります。

[code]

    WScript.Echo CDbl(#1899-12-29 00:00:00#)
    WScript.Echo CDbl(#1899-12-29 12:00:00#)
    WScript.Echo CDbl(#1899-12-30 00:00:00#)
    WScript.Echo CDbl(#1899-12-30 12:00:00#)
    WScript.Echo CDbl(#1899-12-31 00:00:00#)
    
[/code]

基準時の周りを 12 時間間隔で `double` に変換して表示します。結果は:

[code]

    -1
    -1.5
    0
    0.5
    1
    
[/code]

です。 `#1899-12-29 12:00:00#` の値がそれ以前の `#1899-12-29 00:00:00#` よりも **小さく**
なっているところに注目してください。

Date の内部表現は、 `<基準日からの日数>.<その日の 00:00:00 からの時間>` つまり、

  * 整数部が基準日からの日数(基準日以前は負数になる)
  * 小数点部がその日の 00:00:00 からの時間 (24時間=1.0に相当)

という風になっています。ですから、基準日以前の内部表現の小大と日時としての前後は一致しません。

問題の日付を内部表現であらわすと：

  * `x = #1899-12-29 00:00:01#` は `x = -1.00001157407407` つまり、基準日-1 の一秒目
  * `y = #1899-12-31 00:00:01#` は `y = 1.00001157407407` つまり、基準日+1 の一秒目

Date を無理やり `+` で足す場合はこの内部表現で加算を行い、その後 Date に戻す操作になります。ですから、 `x + y` は `0`
となり、上の結果となります。


CC BY-SA 3.0
2014-12-17T09:33:32.940
1903
2014-12-17T10:16:48.290
2014-12-17T10:16:48.290
898
898
1897
answer


ダブるかもしれませんが。

[OLEオートメーション日付の仕様はこちらに書いてある](http://msdn.microsoft.com/ja-
jp/library/system.datetime.tooadate.aspx)ように、年月日は基準日からの差分ですが、時分秒（の絶対値）は当日時刻を表すのですね。

[code]

    ' (1)
    x = 0.42372685185 ' #10:10:10#
    MsgBox CStr(CDate(x + x)) ' 20:20:20
    
    ' (2)
    x = -1.00001157407 ' #1899-12-29 00:00:01#
    y =  0.00001157407 ' #1899-12-30 00:00:01# ' #00:00:01#と同じ
    MsgBox CStr(CDate(x + y)) ' 1899/12/29
    MsgBox CStr(CDate(y + x)) ' 同上
    
    ' (3)
    x = -1.00001157407 ' #1899-12-29 00:00:01#
    MsgBox CStr(CDate(x + x)) ' 1899/12/28 0:00:02
    
    ' (4)
    x = -1.00001157407 '#1899-12-29 00:00:01#
    y =  1.00001157407 '#1899-12-31 00:00:01#
    MsgBox CStr(CDate(x + y)) ' 0:00:00
    
[/code]


CC BY-SA 3.0
2014-12-17T09:52:43.180
1907
2014-12-17T15:05:59.210
2014-12-17T15:05:59.210
3196
3196
1897
answer


直接の仕様は見つけれなかったのですがVBScriptの日付型はOLE Automationの日付型だと思われます。

以下のリンクでの VT_DATE に該当します。

[Date/Time Formats and Conversions | Microsoft
Docs](https://docs.microsoft.com/en-us/archive/blogs/joshpoley/datetime-
formats-and-conversions)

それで計算の結果についてはcamlspotterさんの指摘の通りになるのだと思います。


CC BY-SA 4.0
2014-12-17T09:56:35.193
1908
2022-05-27T00:18:15.587
2022-05-27T00:18:15.587
3060
4619
1897
answer


既に解決はなされているようですが、

VBSのDateのdouble表現と、.NETのDateTime.ToOADate()メソッド（double)を軽く比較してみました。

単純に出すと指数部分の差もあって完全にはチェックできていませんが、 同じ値が算出されているようでしたので、
このメソッドの.NETのソースもなにかの助けになるかもしれません。

  * DateTime#ToOADateが呼び出している TicksToOADateのソース <http://referencesource.microsoft.com/#mscorlib/system/datetime.cs,a35750f25307e8ac>

定数などシンボルをクリックすれば定義位置に飛びます。

* * *

ちなみにこんなコードで比較しました。

vbscript

[code]

    d= #1899-12-29 00:00:01#
    
    for i=0 to 10000
      wscript.echo(cdbl(d))
      d= dateadd("n", 1, d)
    next
    
[/code]

C#

[code]

    var d = new DateTime(1899, 12, 29, 0, 0, 1);
    
    for (var i = 0; i < 10001; i++)
    {
        Console.WriteLine(d.ToOADate());
        d = d.AddMinutes(1);
    }
    
[/code]


CC BY-SA 3.0
2014-12-17T12:14:08.037
1918
2014-12-17T12:14:08.037
728
1897
answer


正式な仕様はこちら

<http://msdn.microsoft.com/en-us/library/9e7a57cf(v=vs.84).aspx>

所詮はAutomationのVariant型だろと思えばこちら

<http://msdn.microsoft.com/ja-
jp/library/windows/desktop/ms221627(v=vs.85).aspx>

後者側にあるように、仕様として1900年1月1日を"2.0"とし、これを基準として整数部を日付、小数点以下が時刻以下をDoubleで表す仕様なので、この基準に従って書かれているように計算の仕方が変わっていると思います。


CC BY-SA 3.0
2014-12-18T02:56:02.017
1963
2014-12-18T04:15:12.293
2014-12-18T04:15:12.293
2362
2362
1897
answer