LoginSignup
3
1

More than 1 year has passed since last update.

VBAメモ:isDate曰く「”2月29日” は日付じゃないけど、"2/30" は日付」

Last updated at Posted at 2021-01-19

タイトルのままなのですが、入力された内容が日付かどうか調べる時にハマったのでメモ。

タイトルの解説

月日を明示的に指定した、isDate("2月29日")は基本falseだ。
実行したのがうるう年の場合だけ、trueを返す。
まったくもって期待通りである。

しかし、スラッシュ区切りのisDate("2/29")は、うるう年と関係なく、常にtrueだ。
そして、isDate("2/30")true、日付であると判断されてしまう。

「書式は "月/西暦下2桁" かな? 2030年2月ですね!」
と、2030/2/1 のつもりでtrue。…正しいけど、そんなのは期待してない。

困ったこと:セルの判断とVBAのisDateの判断が一致しない

Excelのセルは、入力された値が日付っぽいときに、日付扱いしてくれる。
セルが日付扱いするかどうかと、VBAでisDateがそのセルを日付だと思うかどうかは、必ずしも一致しない。

存在する日付の場合

セルの書式設定で、「yyyy-mm-dd」を設定、「3/1」と入力する。
当年の3月1日だと判断され、表示は「2021-03-01」。すごく妥当。

VBAのisDateは、引数の文字列が日付ならtrueを返す関数。

vba
' A1セルの中身は日付かな?
If isDate(Cells(1, 1)) Then           
    ' 日付だったら変数にフォーマットした文字列を投入
    dstr = Format(Cells(1, 1), "YYYY-MM-DD")  ' dstr = 2021-03-01
End If

ちゃんと今年の3/1だと分かってくれている。

うるう日の場合

同じセルに、「2/29」と入力してみる。

まずはセルの挙動。
入力したのが(パソコンのシステム日付が)うるう年、例えば2020年なら、「2020-02-29」になる。
うるう年でなければ、たとえ日付の書式設定をしていても、そのまま「2/29」と表示される。
日付じゃないと判断されているようだが、当年の2月29日は存在しないのだから、妥当だと言える。

次にVBAの挙動。
うるう年なら、先ほどのコードのdstrに「2020-02-29」が代入される、予想通り。
しかし、うるう年でなくてもisDatetrueになる。
なんでや。

vba
' A1セルの中身「2/29」は日付かな?
If isDate(Cells(1, 1)) Then           
    ' 日付だったら変数にフォーマットした文字列を投入
    dstr = Format(Cells(1, 1), "YYYY-MM-DD")  ' dstr = 2029-02-01 < どこから来た?
End If

isDate的にもFormat的にも、「2/29」は「2029年2月」だと思っているようだ。
不足している日付を1日として補って、2029年2月1日って日付ですね?、とか言う結果。
つまるところisDateは、「月 / 西暦下2桁」の書式を持ち出してきている。

日本人にあまり馴染みが少ないだけで、世界にはこの書式も存在する以上「不具合じゃなくて仕様」である。
が、思ってたんと違う。

日本人的には日付じゃない感じの入力をしてみる

入力 シートの判断 VBAの判断
2/0 (日付じゃない) 2000-02-01
0/2 (日付じゃない) 2000-02-01
2/30 (日付じゃない) 2030-02-01
30/2 1930-02-01 1930-02-01
13/30 (日付じゃない) (日付じゃない)
0/0 (日付じゃない) (日付じゃない)

セルが「2/30」は通さないのに「30/2」は通す、というのは何なんだろう。

VBAのisDateは、なんとか日付にならないかと精一杯やってくれる。
スラッシュの前後の数字がどちらも月にならない、となるとようやく諦めた。

isDateはどこまで許容するのか

他にもいろんな文字列をisDateに投入してみた。

vba
' 年月日の単位を含めた文字列はどうか
isDate("3月1日")        ' true  日本式も認識可能
isDate("2月29日")       ' false 今年はうるう年でないから。
                        ' うるう年に実行すれば true
isDate("2020年2月29日") ' true  年まで入ってれば明白に日付

' 月名を使う欧米式
isDate("28 feb")       ' true 2019-02-28
isDate("29 feb")       ' true 2029-02-01
isDate("30 feb")       ' true 2030-02-01
isDate("feb 30")       ' true 2030-02-01
isDate("feb 50")       ' true 1950-02-01

本当に、一生懸命やってくれてるんだけどね、isDateは。

日付じゃないものを、感覚的に正しくハネるにはどうすればいいか

使う人にとっては、コード側の仕様が妥当かどうかとか関係ない。
見えているシート(の印象)と違う結果が出たら、「不具合あるんだけど直してよ」となる。

つまりは、シート上の見た目と、VBAの判断に違いがあるのが問題なのである。

  • セルには日付の書式設定をする
  • VBAでも同じフォーマットをしてみる
  • 文字列として一致するかどうか比較する

という対応にしてはどうだろうか。

vba
' isDate的に、日付だと思う?
If isDate(Cells(tmpRow, tmpCol)) Then
    ' セルの値をそのまま日付として評価、セルと同じ書式にする
    d = Format(Cells(tmpRow, tmpCol), "YYYY-MM-DD")
    ' 一致しないということは、セルはそれを日付とみなしてない。
    If d <> Cells(tmpRow, tmpCol).text Then
        MsgBox "正しくない日付です。"
    End If
Else
    ' isDateにも拒否される、絶対日付じゃないやつ
    MsgBox "日付を入力してください。"
End If

「30/2」の入力は通ってしまうけど、「1930-02-01」がセルに表示されているので、見たままですよと。

日付として妥当かどうかはチェックしてます」
「部分的に入力した文字列が、具体的に何年何月何日なのかは、Excel側が判断して補ってます」
という説明で納得してもらえる(んじゃないだろうか(希望))。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1