こんにちは、あんこ先生です。
PowerAppsでもExcelのように時間計算したいことありませんか?
たとえば、勤怠管理で労働時間を算出したり、休憩時間を控除したりといった具合です。
特に、24時間を超える場合やマイナス表示が難しいですよね。
そこで、「思うように時間計算したい」、「24時間を超えてもその通りに表示させたい」といった要望を叶えるために、関数を組み合わせて時間計算する方法を紹介します。
この記事を読むことで、時間に関する関数の使い方や計算方法を学べます。
日付計算はコチラの記事で詳しく紹介していますので、併せてお読みください。
Contents
関数を組み合わせて時間計算する方法
はじめに、時間計算をするための事前準備として、コントロールやコレクションの作り方を紹介します。
無くても困りませんが、理解を深めるためにはあった方が捗りますのでご利用ください。
時間選択用コントロールのかんたんな作り方
時間も分も連続した値なので、Sequence関数でかんたんに作成できます。
5分単位や二桁表示にする場合は、ForAll関数と組み合わせます。
あとはそのままドロップダウンのItemsプロパティに記述しましょう。
//時間
Sequence(24,0) //一桁表示
ForAll(Sequence(24,0),Text(Value,"00")) //二桁表示
//分
Sequence(60,0) //一桁表示
ForAll(Sequence(12,0,5),Text(Value,"00")) //二桁表示、5分単位
経過時間が24時間を超える場合は、日付情報も必要になります。
日付の選択を追加しておきましょう。
時間計算用コレクション
様々なパターンをコントロールで入力して試すには、いささか骨が折れます。
下記コードを適当なボタンに貼り付けてコレクションを作成しておきましょう。
当日内、日をまたぐ24時間超、過去に戻る時間変動を網羅しています。
ClearCollect(_時間計算,
{開始時間:Date(2022,5,6)&Time(9,0,0),終了時間:Date(2022,5,6)&" "&Time(18,30,0)},
{開始時間:Date(2022,5,6)&Time(9,0,0),終了時間:Date(2022,5,7)&" "&Time(12,00,0)},
{開始時間:Date(2022,5,6)&Time(9,0,0),終了時間:Date(2022,5,8)&" "&Time(18,00,0)},
{開始時間:Date(2022,5,6)&Time(18,0,0),終了時間:Date(2022,5,6)&" "&Time(08,30,0)},
{開始時間:Date(2022,5,6)&Time(18,0,0),終了時間:Date(2022,5,5)&" "&Time(12,00,0)},
{開始時間:Date(2022,5,6)&Time(18,0,0),終了時間:Date(2022,5,4)&" "&Time(08,30,0)}
)
時間計算に使用する関数とその使い方
Excelと異なり、PowerAppsにはシリアル値の概念がありません。
そのため、HourやMinute関数はデータ型が日付/時刻値の場合のみ整数値を返します。
任意の時刻値を作成│Time
Time関数は、時間、分、秒の値を個別に指定し日付/時刻値に変換します。
戻り値には日付がありませんので、必要な場合は別途Date関数と組み合わせて作成します。
時間、分、秒といった単位ごとの上限を超えた値は、次の単位に繰り上がります。
たとえば80分なら1時間と20分として処理されます。
ただし、戻り値に日付が含まれないため、時間部分はループします。
//時刻値の作成(直接指定)
Time(20,30,0)
//時刻値の作成(コントロールから)
Time(Drop時間,Drop分,0)
//日付も加える場合(時間は繰り上がらない)
Date(2022,5,7)&" "&Time(20,30,0)
文字列を時刻値に変換│TimeValue
TimeValue関数は、フォーマットが時刻の文字列(例 09:00)を日付/時刻値に変換します。
Time関数と同様、戻り値には日付がありません。
そのため、23:59を超える場合は、また0:00からカウントします。
//文字列を時刻値へ
TimeValue("9:00")
時間だけ抜き取る│Hour
Hour関数は、日付/時刻値の内、時間を0から23の整数で返します。
24を超える場合は、また0からカウントします。
そのため、24時間以上の計算では使用できません。
また、文字列や数値を直接参照できません。
Time関数やTimeValuede関数を経由しましょう。
//一周するパターン
Hour(Time(26,0,0))
分だけ抜き取る│Minute
Minute関数は、日付/時刻値の内、分を0から59の整数で返します。
60を超える場合は、また0からカウントします。
そのため、60分以上の計算では使用できません。
こちらも、文字列や数値を直接参照できません。
Time関数やTimeValuede関数を経由しましょう。
//一周するパターン
Minute(Time(0,80,0))
日付/時刻値を使った時間計算
日付/時刻値を使った時間計算にはDateAdd関数を使います。
DateAdd関数は、日付/時刻値に単位数を加算します。
結果は、新しい日付/時刻値です。
負の値を指定すれば、日付/時刻値から単位数を減算することもできます。
単位に指定できるのは、整数値のみです。
日付/時刻値は指定できません。
必要に応じてHour関数などを使って整数に変換しましょう。
時刻の加算│DateAdd
一度に指定できる単位は1種類のみです。
1時間30分加算させたい場合は、時間を分に変換して足した値(90分)を指定します。
//時間
DateAdd(DateTimeValue(Today()&" "&Time(10,00,0)),10,TimeUnit.Hours)
//分
DateAdd(DateTimeValue(Today()&" "&Time(10,00,0)),10,TimeUnit.Minutes)
時刻の減算│DateAdd
減算も一度に指定できる単位は1種類のみです。
1時間30分減算させたい場合は、時間を分に変換して足した値(-90分)を指定します。
//時間
DateAdd(DateTimeValue(Today()&" "&Time(20,00,0)),-10,TimeUnit.Hours)
//分
DateAdd(DateTimeValue(Today()&" "&Time(20,00,0)),-10,TimeUnit.Minutes)
時刻値同士の加算│DateAdd
加算減算する日付/時刻値を直接指定できないため、一度整数値に変換します。
図では時間単位で変換したため、2時間加算されています。
2時間30分を加算する場合は、分単位で変換する必要があります。
//時間のみ加算
DateAdd(DateTimeValue(Today()&" "&Time(21,00,0)),Hour(Time(2,30,0)),TimeUnit.Hours)
//分単位で加算
DateAdd(DateTimeValue(Today()&" "&Time(21,00,0)),
Hour(Time(2,30,0))*60+Minute(Time(2,30,0)),
TimeUnit.Minutes)
日付/時刻値間の経過時間計算
経過時間の計算にはDateDiff関数を使います。
DateDiff関数は、2つの日付/時刻値の差を指定した単位の整数値で返します。
指定した単位より小さい値はすべて切り捨てされます。
そのため、必要な情報のうち、最も小さい単位で取得する必要があります。
あとはTime関数で時刻値に変換すれば求める値が手に入ります。
正の経過時間│DateDiff
//時間単位では30分切り捨てられる
DateDiff(DateTimeValue(Today()&" "&Time(12,00,0)),DateTimeValue(Today()&" "&Time(14,30,0)),Hour)
//分単位だと秒以下が切り捨てられる
DateDiff(DateTimeValue(Today()&" "&Time(12,00,0)),DateTimeValue(Today()&" "&Time(14,30,0)),Minutes)
//分単位で抽出し、Time関数で時刻値に変換
Time(0,DateDiff(DateTimeValue(Today()&" "&Time(12,00,0)),DateTimeValue(Today()&" "&Time(14,30,0)),Minutes),0)
負の経過時間│DateDiff
過去にさかのぼる場合は、マイナスとして時間や分を算出できます。
ただし、Time関数はマイナス表示できないため日付/時刻値に戻すには工夫が必要です。
まず、抽出した分に-1をかけてプラスに戻してからTime関数でを時刻値に変換します。
その後、頭に”-“をつけることで表現できます。
計算結果は同じなので、表記の問題だけなら正の経過時間の頭に”-“をつけた方がかんたんです。
//一度プラスに戻し、頭にマイナスを加えて表現する
"-"&Time(0,DateDiff(DateTimeValue(Today()&" "&Time(14,30,0)),DateTimeValue(Today()&" "&Time(12,00,0)),Minutes)*-1,0)
//表記だけなら普通のやり方の頭にマイナスを加えて表現するとかんたん
"-"&Time(0,DateDiff(DateTimeValue(Today()&" "&Time(12,00,0)),DateTimeValue(Today()&" "&Time(14,30,0)),Minutes),0)
24時間超やマイナス表示の時間計算
時間表示│24時間超
24時間以上を表示させる場合は、日付情報が必須です。
まず、DateDiff関数で経過時間を分で取得します。
時間は、分を60で割ってInt関数で整数化して求めます。
見栄えを良くするため、Text関数で数値2桁表記にします。
分は、Mod関数を使い60で割った余りを求めます。
Text関数で”:”と数値2桁表記にします。
あとはそれをつなげるだけです。
//24時間を超える時刻に変換(テキスト型、以後Label経過時間に表示)
Text(Int(DateDiff(ThisItem.開始時間,ThisItem.終了時間,Minutes)/60),"00")
&Text(Mod(DateDiff(ThisItem.開始時間,ThisItem.終了時間,Minutes),60),":00"),
経過時間を日時に戻す
DateTimeValue関数を使うと今日を基準とした日付/時刻値に変換できます。
まず、DateDiff関数を使って、今日からの差分を抽出します。
//差分の抽出
DateDiff(Today(),DateTimeValue(Label正経過時間),Days)&"日と"&
Mod(DateDiff(Today(),DateTimeValue(Label正経過時間),Hours),24)&"時"&
Mod(DateDiff(Today(),DateTimeValue(Label正経過時間),Minutes),60)&"分"
時間表示│マイナス
マイナス表示させる場合も、日付情報が必須です。
まず、DateDiff関数で経過時間を分で取得します。
時間は、分を60で割ってInt関数で整数化して求めます。
見栄えを良くするため、Text関数で頭に”-“と数値2桁表記にします。
分は、Mod関数を使い60で割った余りを求めます。
Text関数で”:”と数値2桁表記にします。
あとはそれをつなげるだけです。
//24時間を超えるマイナス時刻に変換(テキスト型、以後Label経過時間に表示)
Text(Int(DateDiff(ThisItem.終了時間,ThisItem.開始時間,Minutes)/60),"-00")
&Text(Mod(DateDiff(ThisItem.終了時間,ThisItem.開始時間,Minutes),60),":00")
プラスかマイナスかではいちいち使い分けるのは面倒ですよね。
次のように終了時間の方が開始時間よりも大きいかで条件分岐させると便利です。
If(DateTimeValue(ThisItem.終了時間)>DateTimeValue(ThisItem.開始時間),
//正
Text(Int(DateDiff(ThisItem.開始時間,ThisItem.終了時間,Minutes)/60),"00")
&Text(Mod(DateDiff(ThisItem.開始時間,ThisItem.終了時間,Minutes),60),":00"),
//負
Text(Int(DateDiff(ThisItem.終了時間,ThisItem.開始時間,Minutes)/60),"-00")
&Text(Mod(DateDiff(ThisItem.終了時間,ThisItem.開始時間,Minutes),60),":00")
)
24時間超やマイナス表示を使った計算方法
24時間超やマイナス表示の時間計算もできます。
あらかじめ差分の値をLabel経過時間に格納している前提で解説します。
値が正か負のどちらかで多少処理が変わるので条件分岐しておきます。
正の場合は、Split関数で”:”を基準に時と分を分割します。
それぞれを分に変換し、DateAdd関数で加算します。
負の場合は、-表記を消すためにSubstitute関数を挟みます。
あとは同じように分に変換し、DateAdd関数で減算します。
If(DateTimeValue(ThisItem.終了時間)>DateTimeValue(ThisItem.開始時間),
//正
DateAdd(DateTimeValue(ThisItem.終了時間),
Index(Split(Label経過時間.Text,":"),1).Value*60+
Index(Split(Label経過時間.Text,":"),2).Value,
TimeUnit.Minutes),
//負
DateAdd(DateTimeValue(ThisItem.終了時間),
-(Index(Split(Substitute(Label経過時間.Text,"-",""),":"),1).Value*60+
Index(Split(Substitute(Label経過時間.Text,"-",""),":"),2).Value),
TimeUnit.Minutes)
)
関数を組み合わせて時間計算する方法のまとめ
今回は関数を組み合わせて時間計算する方法を紹介しました。
関数によって入出力のデータ型が日付/時刻値、数値や文字列とコロコロ変わるので理解しにくいですよね。
それでも図解やコードを含めて解説したため、狙った計算ができるようになったと思います。
この辺りを活用すれば、勤怠アプリや備品貸出アプリなどに応用できそうです。
早速既存アプリに取り込んでみましょう!
数値は全角でも融通利くけど、コロンが全角だとエラーになるので注意!