以下の内容はhttps://iori016.hatenablog.com/entry/2025/09/06/194939より取得しました。


Excelで日付が1日巻き戻る現象の解説と対策

最近私がお世話になっているDiscord内のExcel専用チャンネルで次のような質問がありました。

『「●/● 23:00」に「60分」を足しても日付が繰り上がらない』


この質問を追っていくと次のような事実が浮かび上がりました。

Excelのセルには日付が特定の範囲の場合、1日巻き戻るという不具合が存在する

今回はこの不具合について詳しく解説していきたいと思います。

ちなみにdiscordのチャンネルはこちらです

sites.google.com

#不具合の再現方法

・オートフィルの場合

まずは不具合の再現方法を紹介します。
Excelで次のような操作をしたとします。
・1行目に「2025/8/31 23:58」
・2行目に「2025/8/31 23:59」
・2つのセルを選択して、フィルハンドルを3行目にドラッグ(オートフィル)する

すると、3行目は「2025/9/1 0:00」になります。

ここまでは普通ですね。さてこの図ですが、何かおかしなところがないでしょうか?
よく見てみましょう。

よくみると、セルの日付が「2025/9/1」なのに、数式バーの日付が、「2025/8/31」になっていますね。

はい。この時点で不具合が発生しています。このセルをダブルクリックすると日付が数式バーの状態に変化し、エンターを押すと実際に1日巻き戻ってしまいます。

別解として、「23:59:58」と「23:59:59」のセルをオートフィルで延長した場合も同様の結果になります。

・数式の場合

次は数式で計算した場合です。次の操作を行ったとします
・1行目に「2025/8/31 22:00:00」
・2行目に「1行目のセル+TIME(1,0,0)+TIME(1,0,0)」
・3行目に2行目のセルをコピーして値として貼り付け

こちらもオートフィルと同様に不具合が発生します。
このセルもダブルクリックすると日付が数式バーの状態に変化し、エンターを押すと実際に1日巻き戻ってしまいます。
ただし、値として貼り付けなければ不具合は発生しない為数式のままであれば問題はありません。

VBAの場合

次はVBAの例を紹介します(こちらがDiscordの質問にあった内容を簡単にしたものです)
イミディエイトウィンドウで次のコードをコピーして実行してみてください。
(セルD1にしているのは数式バーに近くしたかっただけで特に意味はありません)

 Range("D1") = #2025/8/31 22:00:00# + TimeSerial(1,0,0) + TimeSerial(1,0,0)

上のコードの意味は8月31日の22時に1時間を2回足すという意味です。
当然、9月1日の0時になるはずですよね?
しかし、セルに書き出すとどういうわけか8月31日の0時になってしまいます。

Microsoftさんが気を利かせて夏休みを延長してくれたのですかね?

別解として
Range("D1") = DateAdd("h",1,DateAdd("h",1,#2025/8/31 22:00:00#))
とすると同様の不具合が発生します。
ただし、TimeSerial(1,0,0)を#1:00:00#に書き換えた場合は(この例では)不具合は発生しません。(最後の補足参照)

誤解が無いようにいいますと、VBA上ではちゃんと9月1日と計算されています。
今回の不具合はエクセルのセルの話でありVBAは無関係です。

#不具合の起きる条件

不具合の発生する範囲は日付の時間が
23:59:59.500~23:59:59.999
の範囲内に入っていると不具合が発生します。(日付は問わない)
「.500」というのは500msecという意味です。
より厳密に書くとシリアル値の小数部が
0.999994212961609~0.999999999999999
の範囲に入るとバグになるようです。
(499msecと500msecの間に境界があるようです)

実際、日付はシリアル値の整数部が5桁あるので、小数部は10桁にまで桁落ちして
0.9999942129~0.9999999999
の範囲内がバグルる範囲と思えばよさそうです。
(※0.9999942130とするとぎりぎりバグ範囲が入りきりなさそうなので0.9999942129にしました)


しかしここで疑問が残ります。今までの不具合再現方法の例はすべて「2025/9/1 0:00:00」を出力しようとしていました。
しかし、「0:00:00」のシリアル値の小数部は0なので当然、
0.9999942129~0.9999999999
の範囲には入りません。
実はここで問題になるのが浮動小数点誤差です。
オートフィルで1分ずつや1秒ずつ足して「0:00:00」になる場合や
ExcelのTime関数やVBAのTimeSerial関数で1時間ずつ足して「0:00:00」になる場合、
浮動小数点誤差が入り、わずかに「0:00:00」を下回ります。これは1msecにも満たないほんのわずかな誤差ですが、この誤差のせいで、小数部が「0.9999999999」になってしまい不具合範囲に入ってしまうのです。

#対策

対策としては、時間がバグ範囲に入る場合、四捨五入して次の日にしてしまうという手があります。VBAの関数で書くと次のようになります。

Public Function BugFixDate(ByVal dt As Date)
    Rem 機能
    Rem 日付がエクセルのセルのバグ範囲に入る場合、次の日にする
    Dim Dbl小数部 As Double
    '1msecが1.15740E-08であるから、round関数で10で桁落ちさせても問題ない
    If Round(dt - Int(dt), 10) = 0 Then
        '条件式の意味が分かりづらいが、Round(a) = Round(b)→Round(a-b) =0という変形を行っている
        Dbl小数部 = 1
    Else
        Dbl小数部 = dt - Int(dt)
    End If

    If Round(Dbl小数部 - 0.999994212961608, 15) >= 0 And Round(Dbl小数部 - 1, 10) <= 0 Then
        '0.999994212961609から1がバグ範囲、(念のために0.999994212961608からにしている)
        If Round(dt, 5) <> Int(dt) Then 'dtがぴったり当日の場合を除いて次の日にする
            dt = Int(dt) + 1  '500msecにすると不具合範囲に入るので、次の日にする
        End If
    End If
    BugFixDate = dt
End Function

Excelのシートで使う場合はユーザー定義関数として使ってもいいですし、VBAで使用する場合はとりあえず時間をこの関数で変換しておけば間違いないです。
ただし、Excelのシートからユーザー定義関数として呼び出す場合は、ものすごく計算がものすごく遅くなる可能性があるのでご注意ください。
Lambda式とか書ける人はLambdaにしてみるといいかもしれません。

 

#補足

VBA

#2025/8/31 22:00:00# + TimeSerial(1,0,0) + TimeSerial(1,0,0)

#2025/8/31 22:00:00# + #1:00:00# + #1:00:00#

のdouble型の8バイトのデータをWindows APIで調べてみてみて、以下サイトで実際の数値に変換してみました。

tools.m-bsys.com


#2025/8/31 22:00:00# + TimeSerial(1,0,0) + TimeSerial(1,0,0)
の場合、VBA上では2025/9/1(シリアル値45901)と表示されていましたが、
内部データを調べると45900.99999999999となっていることがわかります。


#2025/8/31 22:00:00# + #1:00:00# + #1:00:00#
の場合、VBA上で(シリアル値45901)となっていて、内部データも正しく45901になっていることがわかりました。

したがって、TimeSerial関数を使うよりは#1:00:00#という表記を使用したほうが正確に計算ができそうです。




以上の内容はhttps://iori016.hatenablog.com/entry/2025/09/06/194939より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14