PowerApps における入力日付の妥当性検証

# はじめに
今回の投稿では、PowerAppsでアプリを作成する際に課題になった、入力された日付の妥当性チェックについて記載します。
TextInputコントロールに対して入力された"2019/05/01"のようなテキストが、正しく実在する日付なのかを判定します。
("2019/05/32"や、"2019/02/29"をエラーにすることが目的)

## 問題点
入力された文字列に対して、月が1-12に含まれることの判定はそれほど難しくはありません。また、日が1-31に含まれることも同様に、単純なIf文で対応できます。
ただし、02/29や04/31などを防ぎたい場合、月と日の組み合わせで上限を設定する対応が必要な上に、閏年の計算も必要になり単純ではありません。

## 取りうる対応
単純には、「カレンダーコントロールで日付を選択させる」が、何も問題なく、PowerAppsの標準的な利用方法として正しいように思います。
ただし、カレンダーコントロールは、年を遡ることが大変なので、ある程度広い日付を取りうるような入力の場合には望ましくありません。
これに対しては、以前作成したカスタムカレンダーコンポーネントを利用することでも改善が可能です。(アプリは[こちら](https://powerusers.microsoft.com/t5/Community-Apps-Gallery/Custom-Calendar-component/m-p/239959))

# 解決方法
今回はカスタムカレンダーではなく、TextInputコントロールのみでどこまで対応できるのか、どうすれば対応できるのかを考えます。

主要な関数として、DateValue関数を利用しました。リファレンスは[こちら](https://docs.microsoft.com/en-us/powerapps/maker/canvas-apps/functions/function-datevalue-timevalue)です。
DateValue関数は、定められたいくつかのフォーマットの文字列を、Date形式に変換してくれるものです。

## DateValue関数利用時の注意点
DateValue関数は(主に)MM/DD/YYYY形式の入力をDate形式に変換してくれるわけですが、月の範囲等については設定されていません。
"13月"は"1年+1月"になりますし、もし30日までの月で31日と入力すれば、次の月の1日になります。
![Screen Shot 2019-05-05 at 10.01.23 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/91a09263-a6e9-929a-2100-20f90650deaf.png)

とはいえ、返す日付は存在してよい日付なので、結果自体は使えます。

## 入力検証
DateValue関数の返す結果は実在する日付なので、これと入力を比較することで、入力された"日付"の妥当性をチェックすることができます。
※ここでは簡単のために、入力はYYYY/MM/DD形式で行われるという前提で進めます。

まずは入力からMM/DD/YYYY形式に並び変えてDateValue関数の引数とし、適当な変数に設定します。

```
Set(tempDate,
    DateValue(
        Concatenate(
           Text(Value(Mid(TextInput.Text,6,2))), //Extract MM
            "/",
           Text(Value(Right(TextInput.Text,2))), //Extract DD
            "/",
           Left(TextInput.Text,4) //Extract YYYY
        )
    )
);
```

次に設定した変数の年、月、日をYear()、Month()、Day()関数で抽出し、比較します。

```
And(
    Year(tempDate)=Value(Left(TextInput.Text,4)),
    Month(tempDate)=Value(Mid(TextInput.Text,6,2)),
    Day(tempDate)=Value(Right(TextInput.Text,2))
)
```

これで、入力した文字列と、DateValue関数でフォーマットした結果が一致していれば"true"が返される判定ができました。

最終的にはTextInputのOnChangeに対して、上記を組み合わせた関数を設定しています。
![Screen Shot 2019-05-05 at 10.27.40 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/98de23e2-d52a-32d6-6559-7cdf472e7043.png)

```
Set(outDate,Blank());
Set(tempDate,
    DateValue(
        Concatenate(
            Text(Value(Mid(compInput1.Text,6,2))), //Extract MM
            "/",
            Text(Value(Right(compInput1.Text,2))), //Extract DD
            "/",
            Left(compInput1.Text,4) //Extract YYYY
        ),"en"
    )
);
If(
    And(
        Year(tempDate)=Value(Left(compInput1.Text,4)),
        Month(tempDate)=Value(Mid(compInput1.Text,6,2)),
        Day(tempDate)=Value(Right(compInput1.Text,2))
    )
    &&!IsBlank(tempDate),
    Set(outDate,tempDate);Set(InValidDate,false), //If input text is valid date, set output property
    Set(InValidDate,true) //else set IsError true
)
```

# おまけ
このような複雑な入力はComponentを使うのが良いと思います。
個人のOneDriveですが、コンポーネントのmsappを配置しました。
[ファイルを入手](https://1drv.ms/u/s!Arp1PnSG3hrVtVeLjajs3hz4UYv2?e=RAvSiq)

![Screen Shot 2019-05-05 at 10.35.06 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/6df161d4-42b1-2776-ca71-12c5ec1ccca8.png)

入力用のカスタムプロパティは２つ

- DefaultDate : インプットの初期値が必要であればDate形式で指定してください
- DateFormat : 1はYYYY/MM/DD, 2はMM/DD/YYYY, 3はDD/MM/YYYY形式としています。

出力のカスタムプロパティとしては２つ

- InputDate : 妥当性チェック後に、Date形式に変換した結果を返します。
- IsError : 不適切な入力の場合はtrueを返します。画面側でメッセージを表示する際に利用してください。

※英語のテナントでしか試していないので、もしかしたら日付のフォーマットでおかしくなるかも？フィードバックお願いします m(__)m