タイトルのままなのですが、入力された内容が日付かどうか調べる時にハマったのでメモ。
タイトルの解説
月日を明示的に指定した、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
を返す関数。
' 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」が代入される、予想通り。
しかし、うるう年でなくても**isDate
はtrue
**になる。
なんでや。
' 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に
投入してみた。
' 年月日の単位を含めた文字列はどうか
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でも同じフォーマットをしてみる
- 文字列として一致するかどうか比較する
という対応にしてはどうだろうか。
' 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側が判断して補ってます」
という説明で納得してもらえる(んじゃないだろうか(希望))。