ExcelVBA で時間が正しく変換されない

2022年6月22日 2023年11月8日
カテゴリ: プログラミング
Excel ExcelVBA

あらすじ

ExcelVBA で時間が入力されたセルの値を受け取るとセルの表示形式が「文字列」でない場合浮動小数点数になりますよね。

たとえば A1 セルに 「9:45」 という時間の値が入力されている場合

Dim data as String
data = Cells(1,1).Value

この変数 data には 「9:45」 ではなく 「0.40625」 という値が格納されます。
これを ExcelVBA の処理で別のセルに入力させると 「9:45」 ではなく 「0.40625」 という値が入力されてしまいます。

Cells(1,2).Value = data

これは Excel では時間の値をシリアルタイムで管理しているからなのです。
セルの表示形式が 「時刻」 や 「ユーザ定義」 の場合、時刻は入力した通りに表示されるはずです。
「標準」 のセルに時間を入力すると表示形式は 「ユーザ定義」 になります。

で、時間を入力したセルの表示形式を 「標準」 に戻すと入力した時刻とは別の数値になりますよね。
たとえば 「9:45」 と入力したセルの表示形式を 「標準」 にするとセルの値は 「0.40625」 となります。
これがシリアルタイムです。

ExcelVBA でも同様のことが起きていて、時間を入力したセルの値はシリアルタイムで受け取ることになります。
このシリアルタイムを 「時刻」 形式の文字列に変換する関数に Format 関数があります。
Format 関数 (Visual Basic for Applications) | Microsoft Docs

Cells(1,2).Value = Format(data, "hh:nn")


前置きが長くなりましたが、ここからが本題です。

「9:36」 と入力されたセルの値を ExcelVBA で受け取り、Format 関数で 「時刻」 形式に変換してセルに入力すると、想定とは違う時刻になっていたのです。

Dim data as String
data = Cells(1,1).Value
Cells(1,2).Value = Format(data, "hh:nn")

確認したところ 「時:分」 の形式だ下記のパターンの場合に正しく変換できませんでした。

「1:12」 「2:24」 「3:36」 「4:48」 「6:00」 「7:12」 「8:24」 「9:36」 「10:48」 「12:00」 「13:12」 「14:24」 「15:36」 「16:48」 「18:00」 「19:12」 「20:24 」 「21:36」 「22:48」

「時:分:秒」 の形式だともっとたくさんパターンがありますが割愛します。

さて困りました。

対処方法

結論からいうと Format 関数に String 型の値を渡してしまったことが問題でした。

たとえば 「9:36」 のシリアルタイムは 「0.4」 です。
Format 関数は受け取った値が String 型かつ小数点以下が 2 桁以下の場合、変換するとその値をそのまま 「分」 と認識して変換してしまう仕様のようです。
つまり 「0.4」 を 「0時4分」 と認識して 「0:04」 と変換したのです。

対処方法としては Format 関数に渡すシリアルタイムの値を String 型でなく Double 型で渡せば正しく変換されます。

Dim data as Double
data = Cells(1,1).Value
Cells(1,2).Value = Format(data, "hh:nn")

時間の値を受け取る変数を Double 型にしてしまうのがいちばん手っ取り早いかとは思います。
これだけで事足りるプログラムであればこれで OK でしょう。

そもそもなんで String 型で受け取っていたかというとセルの入力チェックをしたかったからです。
時間以外のデータ (Double 型で受け取れないデータ) も入力されうる想定だったので、一旦 String 型で受け取って Double 型に変換できない場合は警告を出したりする処理にしたかったのです。

では、時間の値を String 型で受け取る場合はどうすればいいかというと、Format 関数に変数を渡す際に Double 型に変換してあげればよいのです。
Double 型に変換するには CDbl 関数を使用します。

Sub example()
    Dim data As String
    data = Cells(1, 1).Value

    ' 変数 data をチェックする処理
    Dim errFlg As Boolean
    On Error GoTo Err:
        errFlg = True
        data = CDbl(data) ' data の値が Double に変換できない場合エラーとなり Err: の処理に飛ぶ
        errFlg = False

    Err:
        If errFlg Then
            MsgBox "時刻の形式で入力してね"
            Exit Sub
        End If

    Cells(1, 2).Value = Format(CDbl(data), "hh:nn")
End Sub

(おまけ) シリアルタイムについて

Excel でいうところのシリアルタイムは 「0時0分0秒」 を 0 とし、「24時0分0秒」 を 1 として時刻の秒数を 24 時間の秒数 (86400) で割った値です。

たとえば 「9:36」 は秒に直すと

9 * 60 * 60 + 36 * 60 = 34560

シリアルタイムは

34560 / 86400 = 0.4

という感じで算出できます。

関連の記事

Excel のセルに名前を付けよう

ExcelVBAでランダムな文字列を生成する方法

ExcelVBA でランダムな整数を生成する方法

OpenPyXl でチェックボックスを押したい