GoogleSpreadSheetで半角数字しか入力できない日付の入力規則を作成するのに試行錯誤した話

知り合いからちょっと相談ということで来た題字の相談。
なんとかできないものかとちょっと試行錯誤したので、忘備録としても残しておく。

#1,REGEXMATCH関数を使えば良いことぐらいはわかる
その先はわからん。調べ始めはそんな状態でした。（前提ですが、私は初学者です。）
こっから脱線。

REGEXといえば、いろんなプログラム言語でも、触っていれば出てくる「正規表現」のお話です。
正規表現そのものについては、世の中にたくさんの記事があるので、自分にあったもので調べてみると良いと思います。
私はここでなるほどなぁって見てました。
[正規表現とは？メタ文字とサンプル一覧](http://www-creators.com/archives/4278#i)

つまり、決まったパターンの文字列を探すときにこの正規表現を使うと便利なわけですね。

<details><summary>――余談――</summary>
正規表現はプログラムだと、ユーザーが入力してくるフォームの「バリデーション」とかで使っていたりします。
「バリデーション」っていうのは、データの精査といったところでしょうか。
いろんな入力フォームを見たことあると思いますが、例えば、ユーザー登録の時、「パスワードは半角英数が１種類以上混ざっていないとダメ」なんて言われた経験があるかと思います。
あれも、プログラムの裏では正規表現でのチェックで、弱いパスワードでの登録を弾いているんです。
これも気になったらググってみるべし。</details>

#2,入力してほしい正規表現パターンを考える。
今回は「半角での日付入力を強要してほしい」と言われたので、そこから条件を整えていきます。
詳しく話を聞くとこのような内容でした。

- 入力する日付は○○/○○の形（年は無し）
- /も入力させる
- 全角は絶対許さない。絶対ニダ。

ということらしいので、そこから導き出される正規表現の形は、

【半角数字が最低1桁、最大2桁】+【半角のスラッシュ】+【半角数字が最低1桁、最大2桁】

となります。
【半角数字が最低1桁、最大2桁】ってのは、それぞれ月の数字と日の数字です。
月は1月から12月まであるので、入力されうるのは1桁か2桁ですよね。
日も同様で1日から31日あるので、入力されうるのは1桁か2桁です。

さて、ここで形が決まったので、スプレットシートを開いて関数を入力していきます。

#3,使うのは入力規則
冒頭で書いたとおり、使うのはREGEXMATCH関数だけど、
今回は入力の制御をしたいから、セルにベタベタ関数を打つのではなく入力規則で関数を使う。

入力規則ってはのは、セルに対して決まった値を入れさせる機能です。
プルダウンで決まった物を選んでもらうときとかによく使われますが、このようなやや複雑なことをやろうとしたときにも使えます。

上のメニューから　[データ]->[データの入力規則]に進むと、画面が出てくるので、
条件に「カスタム数式」を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297780/6f52368c-1837-d042-f4b5-35cdab8aaa95.png)


ここに正規表現を入力する関数「REGEXMATCH」関数が登場します。
公式の案内ページはこれ
[Google REGEXMATCH](https://support.google.com/docs/answer/3098292?hl=ja)

REGEXMATCH(テキスト, 正規表現)と書けばいいとあるので、早速雑だけど組んで見る

```
=REGEXMATCH("A1","^\d{1,2}/\d{1,2}+$")
```
^ → 行頭の文字
\d{1,2} → 半角数字が1桁～2桁
/　→ スラッシュ
+　→ 繰り返す
$ → 行末の文字

分解するとこんな感じ。
これで終わり！と思いきや・・・・

#4,REGEXMATCH関数の罠
先程リンクを張ったページにしれっと小さく書いてあるのだが、
> この関数では、入力値にテキストを指定し（数値は不可）、出力としてテキストが返されます。数値を返す場合は、この関数と合わせて VALUE 関数をお使いください。入力値に数値を指定する場合、TEXT 関数を使用して数値をテキストに変換します。

なんと、数値入力はだめとある。
つまり、正規表現的にはOKでもREGEXMATCH関数的にこの書き方はNGなのだ。
ここでめっちゃハマった。

日付はなんやかんや言っても「数値」なので、ヘルプどおりに従うことにする。

```
=REGEXMATCH(TEXT("A1",0),"^\d{1,2}/\d{1,2}+$")
```

ということでTEXTを挟んでやったぞオラオラオラ！

エラー。無慈悲にもエラー。
なぜどうしてなにがいけないの・・・・

#5,日付の罠
ふと入力値を見てみる。今回は試しにA1に「1/1」と入力してみたが、
実際の関数欄を見るとそうは入力されていない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297780/531e63cd-24ae-7f00-4155-b72e2780e282.png)

これは、もしかして見た目上の入力こそ、1/1なんだけど、中身的には日付のシリアル値に変換されているのでは
と思い至った。
そしてそのシリアル値に対してREGEXMATCH関数が働いているので、引っかかっているのでは
と思って、関数を組み直してみる。


```
=REGEXMATCH(TEXT(TO_DATE(A1),"mm/dd"), "^\d{1,2}/\d{1,2}$")
```

[TO_DATE関数](https://support.google.com/docs/answer/3094239?hl=ja)を使ってみた。
> TO_DATE関数
値に数値または数値を含むセルへの参照を指定すると、TO_DATE 関数は、値を 12 月 30 日からの日数と解釈して日付に変換した値を返します。

らしいので、順番としてはこの様になる

1. A1に入れた日付（中身はシリアル値？）をTO_DATE関数で日付の形（○○○○/○○/○○）に直す
2. 直した物をTEXT関数でフォーマットする ("mm/dd")
3. それをREGEXMATCH関数にかける

以上。
一応これでエラーが無くなったので、終わりとした。


ちなみに、半角数字を\dと書いたが、別に

```
[:digit:]
```
と書いても良いらしい。
だから、

```
=REGEXMATCH(TEXT(TO_DATE(A1),"mm/dd"), "^[[:digit:]]{1,2}/[[:digit:]]{1,2}$")
```
これでも動いた。

このへんに公式の詳細があったので、ついでに記載。
[正規表現の構文](https://support.google.com/a/answer/1371415?hl=ja)

> 次の特殊文字を使用した正規表現は、メール処理の遅延を招く可能性があるため、サポートされていません。
*（アスタリスク）
+（プラス記号）

こんなふうに書いてたけど、スプレットシートは関係なかった。

#6,欠点
一度テキストに起こしているので、日付として有効かどうかのチェックができていない。
これは正規表現でやるより別の関数でやったほうがいいと思う。
つまり、13/45　（13月45日）とかが入力できてしまうし、
125 とか　525　とかそのへんのTO_DATE関数の日付の値として有効な値はそのまま入力できてしまうので、
これもまだまだ甘いんだろうなと思う。


一応要件であった、半角！は満たせたので、一応良しとしている。
多分もっっと単純な方法があるのかもしれない。わかったら追記します。