[Power BI] 誕生日から現在の年齢を計算する。Power QueryとJAPAN RUGBY LEAGUE ONE の選手プロフィールで。

# はじめに

DAXで誕生日から年齢を計算してみたのが下記記事。続いて、同じことをPower Queryを使用して計算してみます。

https://qiita.com/hanaseleb/items/5c209fc61c7bee7dcb77

## 準備

`実験`テーブルに`baseday`カラムを用意します。

```datastudio
let
    Source = List.Dates(#date(1984,02,05),20,#duration(1,0,0,0)),
    #"Converted to Table" = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "baseday"}})
in
    #"Renamed Columns"
```

本記事執筆は2022年2月13日です。つまり、この日付列を誕生日だとすると、9行目までが38歳と計算され、その後は37歳となるのが期待する結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/f4697406-338d-48c0-43cd-609e7a53488d.png)

## 注意すること

WEBをざっと検索すると、EXCELベースのPower Queryで下記のような数式が多数紹介されています。この数式、使用上の注意があるので簡単に書きます。

```datastudio
Number.RoundDown(Duration.Days(DateTime.Date(DateTime.LocalNow())-[baseday])/365)
```

### **1. DateTime.LocalNow()** の扱い

Power BIの場合、最後はPower BI Serviceにアップしてレポートを共有することになると思いますが、端末とクラウドの`Localtime`は計算が違います。安全に運用するのであれば、下記のように書き換えるのが推奨です。

```datastudio
Number.RoundDown(Duration.Days(DateTime.Date(DateTimeZone.SwitchZone(DateTimeZone.UtcNow(),9))-[baseday])/365)
```

以前、その点について記事も書いているので参考までに。

https://qiita.com/hanaseleb/items/891afb80c964cc6353ed

### **2_1. `÷365`** の考え方

上記の数式は**今日**から基準日（誕生日）までの日数を計算し、それを365日割り、小数点以下を切り捨てて年齢を求めています。ざっくりとした年齢であればいいのですが、**うるう年（leap year）は1年間が366日ある**ため、正確な計算結果にはなりません。

作成してある[baseday]に計算を適用すると、`2/13`が境にならず、`2/23`まで年齢が加算されてしまっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/a17ab88c-4af1-df7b-aee3-256cfbe12890.png)

### **2_2. `÷365.25`** の考え方

**うるう年（leap year）に対応するため**、4年間１周期と考え`/365.25`と書き換えた数式も散見されますが、やはりこちらもずれます。本来であれば、`2/13`がしきい値になるはずですが、１日ずれました。

※ずれる日付を探るため[baseday]の値を変えています。

```datastudio
Number.RoundDown(Duration.Days(DateTime.Date(DateTimeZone.SwitchZone(DateTimeZone.UtcNow(),9))-[baseday])/365.25)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/12d4ec2c-fc6a-4cf4-f9ee-fee37de3db97.png)

## 完成したもの １つ目

ということで、以上の注意点を踏まえてPower Queryを組み立てていきます。汎用性のあるようにカスタム関数として適用できるかたちで書いています。

DAX ver と同じ考え方で作成。誕生日と今日の日付を、

- 年
- 月
- 日

とバラバラにして、`今日の日付` と `今年の誕生日`を比較するという方針です。

:::note warn
2022-02-15 追記
修正前バージョンだと、誕生日がうるう年2月29日の人がいるとerrorがでます。年号を今年に変更して比較するロジックなので、今年の2月に29日が存在しない場合はErrorが返されるわけですね。error対策は複数考えられますが、今回は try~otherwise~を使ってエラー処理しています。
:::

```datastudio
修正前
(birthday as date) =>
    let
        today =
            DateTime.Date(
                DateTimeZone.SwitchZone(
                    DateTimeZone.UtcNow(),
                    9
                )
            ),
        birthday_year = Date.Year(birthday),
        birthday_month = Date.Month(birthday),
        birthday_day = Date.Day(birthday),
        today_year = Date.Year(today),
        compare_date =
            #date(
                today_year,
                birthday_month,
                birthday_day
            ),
        Age =
            if today > compare_date then
                today_year - birthday_year
            else
                today_year - birthday_year - 1
    in
        Age
```

`datastudio
(birthday as date) =>
    let
        today =
            DateTime.Date(
                DateTimeZone.SwitchZone(
                    DateTimeZone.UtcNow(),
                    9
                )
            ),
        birthday_year = Date.Year(birthday),
        birthday_month = Date.Month(birthday),
        birthday_day = Date.Day(birthday),
        today_year = Date.Year(today),
        compare_date =
            try
                #date(
                    today_year,
                    birthday_month,
                    birthday_day
                )
            otherwise #date(today_year, birthday_month, 28),
        Age =
            if today >= compare_date then
                today_year - birthday_year
            else
                today_year - birthday_year - 1
    in
        Age
`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/31ccb7b1-a6b1-5e2d-2109-e40058a8fc2a.png)

## 完成したもの ２つ目

`今日の日付`と`誕生日`を数値に変換して引き算をする・・というやり方。こちらも正確な値がでます。`IF`文を使用していないので、こちらのほうがすっきりしていますね。

**スペシャルサンクス @yugoes1021**

```datastudio
(birthday as date) =>
    let
        today =
            DateTime.Date(
                DateTimeZone.SwitchZone(
                    DateTimeZone.UtcNow(),
                    9
                )
            ),
        today_num = Int64.From(Date.ToText(today, "yyyymmdd")),
        date_num = Int64.From(Date.ToText(birthday, "yyyymmdd")),
        result = Number.ToText(today_num - date_num)
    in
        Int64.From(Text.Start(result, 2))
```

:::note warn
2022-02-15 追記
上記計算式のままだと、頭二桁を取得するので100歳以上に対応していません。
修正版は下記を参照してください。
:::

```datastudio
(birthday as date) =>
    let
        today =
            DateTime.Date(
                DateTimeZone.SwitchZone(
                    DateTimeZone.UtcNow(),
                    9
                )
            ),
        today_num = Int64.From(Date.ToText(today, "yyyyMMdd")),
        date_num = Int64.From(Date.ToText(birthday, "yyyyMMdd")),
        result = Number.ToText(today_num - date_num)
    in
        if Text.Length(result) <= 6 then
            Int64.From(Text.Start(result, 2))
        else
            Int64.From(Text.Start(result, 3))
```

## まとめ

誕生日を計算する**だけ**と思ったのが間違いでしたっ！奥が深い！
より良い方法がありましたら教えて下さい～！

https://app.powerbi.com/view?r=eyJrIjoiOGMzYTNmMTAtMGY1MC00OGEwLWExOGQtOTgyZDdhNDdiNTU5IiwidCI6IjdkMDZmZWVlLWI3MzYtNDE3Zi1hMGUyLTE3NTZjOTdiMDU5YyJ9&pageName=ReportSectiondfe751328414602d023b

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/06f01958-3f43-c5f2-0b0d-af71665ab486.png)