【Automation Anywhere】高度な日付処理 Meta Bot 2選

前回の記事「[Bot Storeで新しい機能を追加しよう！](https://qiita.com/Okura_/items/b926368524ade6e94dda)」でBot Store上から拡張機能を簡単に追加する方法を紹介しました。この方法を使って、さっそくよく使われる処理を追加していきたいと思います。

今回は、日付処理でよく使われるAA純正の2つのMeta Botを紹介します。

* [Change Date and Time Format](https://botstore.automationanywhere.com/bot/change-date-and-time-format/)
* [Calculate Business Days](https://botstore.automationanywhere.com/bot/calculate-business-days/)

# Change Date and Time Format
日付と時間の書式変更や足し算、引き算などでよく使われる53個のロジックが追加できます。リファレンスについては「[Automation Anywhere - BotStore紹介（Change Date and Time Format）](https://licensecounter.jp/engineer-voice/blog/articles/20190726_AutomationAnywhere-BotStore-ChangeDateAndTimeFormat.html)」に各コマンドの解説があるのでこちらに譲ります。

この記事では、やりたいことの逆引きで主要なロジックの紹介をします！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/ac786e7b-f1fe-3c2a-ce13-36a57cf0843a.png)

### はじめるまえに...標準機能でできること
まず、日付/時刻に関する標準機能のおさらいをしておきましょう。システム変数一覧の中に、日付/時刻に関する変数があります。(WorkbenchのVariable Manager、Show System Variablesから表示できます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/c1d98302-7416-dd21-f94c-d1985bc15fef.png)

これらの変数 (`$Year$`、`$Month$`、`$Day$`、`$Hour$`、`$Minute$`、`$Second$`、`$Millisecond$`、`$Date$`) は、それぞれ現在の日付/時刻に相当する値を返します。また、`$Date$`はシステム変数ダイアログボックスであらかじめ書式を指定しておくのですが、指定できる書式は以下の19種類のみです。

    mm/dd/yyyy hh:mm:ss AM/PM
    mm/dd/yyyy
    mm/dd/yyyy hh:mm AM/PM
    mm/dd/yyyy HH:mm:ss
    mm/dd/yyyy HH:mm
    dd/mm/yyyy
    dd/mm/yyyy hh:mm:ss AM/PM
    dd/mm/yyyy hh:mm AM/PM
    dd/mm/yyyy HH:mm:ss
    dd/mm/yyyy HH:mm
    m/dd yyyy
    m/d/yy
    yy/mm/dd
    yy-mm-dd
    dd-mmm-yy
    dddd,mmmm,dd,yyyy
    mmmm,dd,yyyy
    dddd,dd mmmm,yyyy
    dd mmmm,yyyy

色々と書式はあるのですが、特に日本でなじみの深い西暦や和暦の書式がありません。また、日付/時刻の演算を行うコマンドは標準では用意されていません。

これを前提に、今回の拡張機能を使ってみたいと思います。

### 日付/時刻の書式を変更したい
これはよくあるパターンですね。この場合は**FormatStringWithDateTime**を使いましょう。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**FormatStringWithDateTime**|**vStr:**元の日付/時刻文字列<br/>**vFormat:**日付/時刻フォーマット (.NETの[標準](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/standard-date-and-time-format-strings)および[カスタム](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-date-and-time-format-strings)形式で指定)|**vStr:**出力先の変数|日付/時刻の形式を指定した形式に変更します。|

```
例: Input: vStr="09/02/2019 09:00:00 PM", vFormat="yyyy/MM/dd HH:ss"
結果: 2019/09/02 21:00
```
このロジックでは時期/時代の書式指定子 "g", "gg" も扱えます。ただし、内容は西暦になってしまうようです[^1]。和暦に対応するには別のMeta Botをダウンロードする必要があります。詳しくは @RPAbot さんの記事「[Automation Anywhere で和暦と西暦を変換するには (令和対応)](https://qiita.com/RPAbot/items/90311d8647b0514c581d)」を参照してください。

また、入力に変数も使えます。

```
例: Input: vStr="%Date%", vFormat="ggyy/MM/dd hh:ss" (ただし%Date%="09/02/2019 09:00:00 PM")
結果: 西暦19/09/02 09:00
```

[^1]: 正確にいうと、OSの日付書式がデフォルトの「西暦 (日本語)」だと「西暦」、「和暦」だと「令和」という文字列になります。ただし、yyの部分にはいずれも2019が入ってしまったり、入力文字列によってはggMM/dd/yyという順番になってしまい、うまくいきません。

もう少し簡略的に日付/時刻フォーマットを指定しないロジックも存在します。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**GetDateTime**|**vStr:**元の日付/時刻文字列|**vStr:**出力先の変数|日付/時刻の形式を標準的な形式に変更します。|

出力結果の書式はOSの日付書式に依存する標準フォーマットとなります[^2]。

[^2]: OSの日付書式がデフォルトの「西暦 (日本語)」だと `yyyy/MM/dd HH:mm:ss` が指定されたとみなした出力を返します。OSの日付書式が「和暦」の場合は「令和」が先頭についたりしてうまくいかないようです。

```
例: Input: vStr="09/02/2019 09:00:00 PM"
結果: 2019/09/02 21:00:00
```

### 日付/時刻の文字列から日付/時刻の各要素を抜き出したい
以下のロジックを使います。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**Year**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「年」を得ます。|
|**Month**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「月」を得ます。|
|**Day**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「日」を得ます。|
|**DayOfWeek**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「曜日」を得ます。|
|**Hour**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「時」を得ます。|
|**Minute**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「分」を得ます。|
|**Second**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「秒」を得ます。|
|**TimeOfDay**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|抽出された「時刻」を得ます。|

出力結果の書式はOSの日付書式に依存する標準フォーマットとなります[^2]。

```
例: Input: vStr="09/02/2019 09:00:00 PM"
結果:
Year: 2019
Month: 9
Day: 2
DayofWeek: Monday
Hour: 21
Minute: 0
Second: 0
TimeOfDay: 21:00:00
```

### タイムゾーンを変更したい
以下のロジックを使います。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**LocalToUniversalTime**|**vStr:**日付/時刻文字列(ローカル時間)|**vStr:**出力先の変数|指定した日付/時刻のUTC協定世界時間を得ます。|
|**UniversalToLocalTime**|**vStr:**日付/時刻文字列(UTC協定世界時間)|**vStr:**出力先の変数|指定した日付/時刻のローカル時間を得ます。|

出力結果の書式はOSの日付書式に依存する標準フォーマットとなります[^2]。ローカル時間はOSのタイムゾーンの設定で指定されています。


### 2つの日付/時刻はどちらが先か判定したい、その時間差を知りたい
以下のロジックを使います。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**Compare**|**vStr1:**日付/時刻文字列1<br/>**vStr2:**日付/時刻文字列2|**vStr:**出力先の変数|日付/時刻vStr2がvStr1と比べて早いか比べます。<br/>vStr1&gt;vStr2 → vStr=Earlier<br/>vStr1&lt;vStr2 → vStr=Later<br/>vStr1=vStr2 → vStr=Equal|
|**Diff**|**vStr1:**日付/時刻文字列1<br/>**vStr2:**日付/時刻文字列2|**vStr:**出力先の変数|vStr1のvStr2と比べた経過時間を時刻文字列で得ます。vStr1が過去の場合はマイナスの値になります。|
|**DiffTotalDays**|**vStr1:**日付/時刻文字列1<br/>**vStr2:**日付/時刻文字列2|**vStr:**出力先の変数|vStr1のvStr2と比べた経過日数を小数で得ます。vStr1が過去の場合はマイナスの値になります。|
|**DiffTotalHours**|**vStr1:**日付/時刻文字列1<br/>**vStr2:**日付/時刻文字列2|**vStr:**出力先の変数|vStr1のvStr2と比べた経過時間を小数で得ます。vStr1が過去の場合はマイナスの値になります。|
|**DiffTotalMinutes**|**vStr1:**日付/時刻文字列1<br/>**vStr2:**日付/時刻文字列2|**vStr:**出力先の変数|vStr1のvStr2と比べた経過分数を小数で得ます。vStr1が過去の場合はマイナスの値になります。|
|**DiffTotalSeconds**|**vStr1:**日付/時刻文字列1<br/>**vStr2:**日付/時刻文字列2|**vStr:**出力先の変数|vStr1のvStr2と比べた経過秒数を小数で得ます。vStr1が過去の場合はマイナスの値になります。|
|**DiffTotalMilliseconds**|**vStr1:**日付/時刻文字列1<br/>**vStr2:**日付/時刻文字列2|**vStr:**出力先の変数|vStr1のvStr2と比べた経過ミリ秒数を得ます。vStr1が過去の場合はマイナスの値になります。|

```
例: Input: vStr1="09/02/2019", vStr2="2019/09/02 12:01:00 AM"
結果:
Compare: Later
Diff: -00:01:00
DiffTotalDays: -0.000694444444444444
DiffTotalHours: -0.0166666666666667
DiffTotalMinutes: -1
DiffTotalSeconds: -60
DiffTotalMilliseconds: -60000
```

### 日付/時刻のX日後/X時間後/X分後等を取得したい
以下のロジックを使います。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**AddYears**|**vStr:**日付/時刻文字列<br/>**vOffset:**追加する年数、減算はマイナスを指定。|**vStr:**出力先の変数|指定した日付/時刻のvOffset年後の日付/時刻を得ます。|
|**AddMonths**|**vStr:**日付/時刻文字列<br/>**vOffset:**追加する月数、減算はマイナスを指定。|**vStr:**出力先の変数|指定した日付/時刻のvOffset月後の日付/時刻を得ます。|
|**AddDays**|**vStr:**日付/時刻文字列<br/>**vOffset:**追加する日数、減算はマイナスを指定。|**vStr:**出力先の変数|指定した日付/時刻のvOffset日後の日付/時刻を得ます。|
|**AddHours**|**vStr:**日付/時刻文字列<br/>**vOffset:**追加する時間数、減算はマイナスを指定。|**vStr:**出力先の変数|指定した日付/時刻のvOffset時間後の日付/時刻を得ます。|
|**AddMinutes**|**vStr:**日付/時刻文字列<br/>**vOffset:**追加する分数、減算はマイナスを指定。|**vStr:**出力先の変数|指定した日付/時刻のvOffset分後の日付/時刻を得ます。|
|**AddSeconds**|**vStr:**日付/時刻文字列<br/>**vOffset:**追加する秒数、減算はマイナスを指定。|**vStr:**出力先の変数|指定した日付/時刻のvOffset秒後の日付/時刻を得ます。|
|**AddMilliseconds**|**vStr:**日付/時刻文字列<br/>**vOffset:**追加するミリ秒数、減算はマイナスを指定。|**vStr:**出力先の変数|指定した日付/時刻のvOffsetミリ秒後の日付/時刻を得ます。|

出力結果の書式はOSの日付書式に依存する標準フォーマットとなります[^2]。

```
例: Input: vStr="09/02/2019 09:00:00 PM", vOffset=25
結果:
AddDays: 2019/09/27 21:00:00
```

### 指定した日付の月初/年初/月末/年末の日付が知りたい
以下のロジックを使います。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**DayOfYear**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の年初からの日数を得ます。|
|**DaysInMonth**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の月初からの日数を得ます。|
|**FirstDayOfMonth**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の月の初日の日付文字列を得ます。|
|**FirstDayOfNextMonth**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の翌月の初日の日付文字列を得ます。|
|**FirstDayOfPreviousMonth**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の前月の初日の日付文字列を得ます。|
|**FirstDayOfQuarter**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の四半期の初日の日付文字列を得ます。|
|**FirstDayOfYear**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の年の初日の日付文字列を得ます。|
|**IsdaylightSavingTime**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付に夏時間があるかを得ます。(True/False。日本であれば、False)|
|**IsLeapYear**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の年がうるう年かを得ます。(True/False)|
|**LastDayOfMonth**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の月の末日の日付文字列を得ます。|
|**LastDayOfNextMonth**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の翌月の末日の日付文字列を得ます。|
|**LastDayOfPreviousMonth**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の前月の末日の日付文字列を得ます。|
|**LastDayOfQuarter**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の四半期の末日の日付文字列を得ます。|
|**LastDayOfYear**|**vStr:**日付/時刻文字列|**vStr:**出力先の変数|指定した日付の年の末日の日付文字列を得ます。|

出力結果の書式はOSの日付書式に依存する標準フォーマットとなります[^2]。

```
例: Input: vStr="09/02/2019 09:00:00 PM"
結果:
DayOfYear:245
DaysInMonth:30
FirstDayofMonth:2019/09/01 00:00:00
FirstDayOfNextMonth:2019/10/01 00:00:00
FirstDayOfPreviousMonth:2019/08/01 00:00:00
FirstDayOfQuarter:2019/07/01 00:00:00
FirstDayOfYear:2019/01/01 00:00:00
IsdaylightSavingTime:False
IsLeapYear:False
LastDayOfMonth:2019/09/30 00:00:00
LastDayOfNextMonth:2019/10/31 00:00:00
LastDayOfPreviousMonth:2019/08/31 00:00:00
LastDayOfQuarter:2019/09/30 00:00:00
LastDayOfYear:2019/12/31 00:00:00
```

### タイマー処理で経過時間を測りたい
以下のロジックを使います。

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**Now**|なし|**vStr:**出力先の変数|現在の日付/時刻を得ます。書式を除くと値はシステム変数`$Date$`と同一です。|
|**StartTimer**|なし|なし|タイマーを開始します。|
|**StopTimer**|なし|なし|タイマーを終了します。|
|**ElapsedHour**|なし|**vResult:**出力先の変数|StartTimer実行後からの現在までの経過時間、もしくはStopTimerが実行されていればそこまでの経過時間(時)を整数で返します。|
|**ElapsedMinutes**|なし|**vResult:**出力先の変数|StartTimer実行後からの現在までの経過時間、もしくはStopTimerが実行されていればそこまでの経過時間(分)を整数で返します。|
|**ElapsedSeconds**|なし|**vResult:**出力先の変数|StartTimer実行後からの現在までの経過時間、もしくはStopTimerが実行されていればそこまでの経過時間(秒)を整数で返します。|
|**ElapsedMilliseconds**|なし|**vResult:**出力先の変数|StartTimer実行後からの現在までの経過時間、もしくはStopTimerが実行されていればそこまでの経過時間(ミリ秒)を整数で返します。|
|**ElapsedTimeSpan**|なし|**vResult:**出力先の変数|StartTimer実行後からの現在までの経過時間、もしくはStopTimerが実行されていればそこまでの経過時間を`"hh:mm:ss.fff"`のフォーマットで返します。|

出力結果の書式はOSの日付書式に依存する標準フォーマットとなります[^2]。


# Calculate Business Days
休日情報をファイルで入力して営業日を休日を考慮した日数で返します。動作は、Meta Botインストール時に同時にインストールされるサンプルファイル `My Tasks\Bot Store\CalcBusinessDays\My Tasks\CalcBusinessDaysTest.atmx`を参照するのがお勧めです。`$AAApplicationPath$\Automation Anywhere\My Docs\Holidays.txt`には、向こう3年の日本の休日一覧がHolidays.txtとして生成されています。必要に応じてこのファイルを編集して休日を足していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/bd724ff8-8a24-1fd7-8d70-aa16b51c3163.png)

|ロジック名|Inputパラメータ|Outputパラメータ|説明|
|:--|:--|:--|:--|
|**CalcBusinessDays**|**vHolidayFilePath:**休日情報ファイルのパス。`$AAApplicationPath$\Automation Anywhere\My Docs\Holidays.txt`を指定します。<br/>**vEndDate:**終了日<br/>**vStartDate:**開始日|**vDays:**出力先の変数。|開始日から終了日までの営業日を休日を考慮した日数で返します。|
|**CalcDayOfWeek**|**YYYY:**年<br/>**DD:**日<br/>**MM:**月|**vDOW:**出力先の変数。|指定した日付の曜日を数字で得ます。(月曜日=1, 火曜日=2, ...)|
|**CalcDays**|**vDay:**日<br/>**vMonth:**月<br/>**vYear:**年|**vDays:**出力先の変数。|指定した日付の西暦0年からの経過日数を得ます。|

```
例:
Input:
vHolidayFilePath="$AAApplicationPath$\Automation Anywhere\My Docs\Holidays.txt"
vEndDate="20190510", vStartDate="20190331"
結果:
CalcBusinessDays: 24
```

```Holidays.txt
2019/01/01,元日
2019/1/2,
2019/1/3,
2019/1/14,成人の日
2019/2/11,建国記念の日
2019/3/21,春分の日
2019/4/29,昭和の日
2019/4/30,国民の休日
2019/05/01,国民の休日
2019/05/02,国民の休日
2019/5/3,憲法記念日
2019/05/04,みどりの日
2019/5/5,こどもの日
2019/5/6,振替休日
2019/7/15,海の日
2019/8/11,山の日
2019/8/12,振替休日
2019/9/16,敬老の日
2019/9/23,秋分の日
2019/10/14,体育の日
2019/10/22,即位礼正殿の儀
...
```