日付メンションから日付プロパティを設定するオートメーション: Notion Tips (134)

(11/14 修正: 10時前のタスクの場合に時刻が4桁にならずにエラーになっていました。修正しましたので、すでに実装された方はこちらのスクリプトに作り直してください)

はじめに

Notion Tips の第134回目は日付メンションから日付を自動設定するオートメーションの解説です。日付メンションについては、日付メンションの設定方法: Notion Tips (112) - hkob’s blog にて非常に少ない文字数で日付を設定できる Tips を紹介しました。一方、日付プロパティは設定が GUI でわかりやすいものの、どうしてもポインタを使った作業が必要となり慣れても操作が速くなりません。そこで、日付プロパティを日付メンションから設定できるオートメーションを作成しました。ちなみに、このオートメーションは基本的に日本と同じように YYYY 年 MM 月 DD 日のような並びで数値が並ぶ言語でしか利用できません。例えば、米国のように November のように表示されてしまう言語では使えません。

インターフェース設計

今回はタイトルの最後に日付メンションを追加した時に、その日付メンションを日付プロパティに設定します。ただし、タイトルプロパティの編集をトリガにしてしまうと、編集の途中でもオートメーションが発動してしまうので、末尾の「.」をつけた時というトリガにすることにしました。

オートメーションを作成する前に Formula で全てのパターンをテストします。日付メンションは少し前に内部のデータ構造が変更となり、表示が「今日」のようになっていても、内部の文字構造は 2024年11月3日のように文字列として保持されるようになっています。この性質を使って以下の全ての組み合わせから日付プロパティを作成します。

  1. 開始日付のみ
  2. 開始日付と時間
  3. 開始日付と終了日付
  4. ある一日の開始時間と終了時間
  5. 開始時刻と終了時刻 (日付が異なる)

日付作成確認の Formula では日付メンションから日付プロパティが作成されたことを確認しました。また、メンション除外確認では後段の日付メンションを取り除けたことを確認しました。作成した数式はオートメーションのところでほとんど同じ形で出てくるので、ここでは省略します。

日付作成確認の Formula

オートメーションを実装

設計が終われば実装するだけです。データベースに必要なのは名前と日付のプロパティだけです。残りの二つは動画用に用意したただのコメントです。

用意したデータベース

オートメーションは以下のようになっています。名前が「.」で終わった時にトリガーがかかり、日付を設定後に設定したメンションを消す処理が入っています。ここにある自分の値が、上の Formula でテスト作成した数式となります。

作成した mention to date オートメーション

数式はこのようになります。array のところに「 prop("トリガーページ").prop("名前")」に変更するだけで、それ以外は Formula と同じです。

オートメーションの数式

数式はかなり長いので、こちらに書き出します。

lets(
    /* array: スペース区切りの文字列 */
    array, prop("トリガーページ").prop("名前").split(" "),
    /* array_length: array の長さ */
    array_length, array.length(),
    /* arrow_index: → の位置 */
    arrow_index, array.findIndex(current == "→"),
    /* last_has_time: 最後の要素が時刻なら true */
    last_has_time, array.last().test(":"),
    /* JST offset */
    offset, "00+09:00",
    ifs(
        /* → がなく、時刻もなし: 日付なし または 日付単体 */
        arrow_index == -1 && last_has_time.not(),
          /* 日付の数値にマッチ: 3つの数字を得る */
            let(ymd, array.last().match("\d+"),
                /* 数値が 3 つあれば日付処理 */
                ymd.length() == 3 ?
                    /* y を 10000 倍、m を 100 倍する */
                    ymd.map(current.toNumber() * pow(100, 2-index))
                    /* 加算して 8 桁の数値を得る */
                    .sum()
                    /* 8桁の数値から日付を得る */
                    .parseDate()
                    /* 日付でなければ空の日付を得る */
                    : "".toNumber().fromTimestamp()
                ),
        /* → がなく、時刻あり: 日付時刻 */
        arrow_index == -1,
            /* 時刻は T の後ろに4桁を追加 */
            [
                array.at(-2).match("\d+").map(current.toNumber() * pow(100, 2-index)).sum(),
                (array.last().match("\d+").map(current.toNumber() * pow(100, 1-index)).sum() + 10000 + offset).substring(1)
            ].join("T").parseDate(),
        /* → の後ろが時刻のみ: 当日の時刻範囲 */
        array_length - arrow_index == 2 && last_has_time,
          let(
                ymd, array.at(-4).match("\d+").map(current.toNumber() * pow(100, 2-index)).sum(),
                [ymd, (array.at(-3).match("\d+").map(current.toNumber() * pow(100, 1-index)).sum() + 10000 + offset).substring(1)]
                    .join("T")
                    .parseDate()
                    .dateRange(
                        [ymd, (array.last().match("\d+").map(current.toNumber() * pow(100, 1-index)).sum() + 10000 + offset).substring(1)]
                            .join("T")
                            .parseDate()
                    )
            ),        
        /* 矢印の後ろが日付のみ: 日付範囲 */
        array_length - arrow_index == 2,
            array.at(-3).match("\d+").map(current.toNumber() * pow(100, 2-index))
                .sum().parseDate()
                .dateRange(
                    array.last().match("\d+").map(current.toNumber() * pow(100, 2-index))
                        .sum().parseDate()
                ),
        [
            array.at(-5).match("\d+").map(current.toNumber() * pow(100, 2-index)).sum(),
            (array.at(-4).match("\d+").map(current.toNumber() * pow(100, 1-index)).sum() + 10000 + offset).substring(1)
        ].join("T").parseDate()
            .dateRange(
                [
                    array.at(-2).match("\d+").map(current.toNumber() * pow(100, 2-index)).sum(),
                    (array.last().match("\d+").map(current.toNumber() * pow(100, 1-index)).sum() + 10000 +offset).substring(1)
                ]
                .join("T").parseDate()
            )
    )
)

もう一つのメンションを消す数式は以下のようになっています。

lets(
    /* array: スペース区切りの文字列 */
    array, prop("トリガーページ").prop("名前").split(" "),
    /* array_length: array の長さ */
    array_length, array.length(),
    /* arrow_index: → の位置 */
    arrow_index, array.findIndex(current == "→"),
    /* last_has_time: 最後の要素が時刻なら true */
    last_has_time, array.last().test(":"),
    remain,
        ifs(
            /* → がなく、時刻もなし: 日付なし または 日付単体 */
            arrow_index == -1 && last_has_time.not(),
              /* 日付の数値にマッチ: 3つの数字を得る */
                let(ymd, array.last().match("\d+"),
                    /* 数値が 3 つあれば日付処理 */
                    ymd.length() == 3 ? array_length - 1 : array_length
                ),
            /* → がなく、時刻あり: 日付時刻 */
            arrow_index == -1, array_length - 2,
            /* → の後ろが時刻のみ: 当日の時刻範囲 */
            array_length - arrow_index == 2 && last_has_time, array_length - 4,
            /* 矢印の後ろが日付のみ: 日付範囲 */
            array_length - arrow_index == 2, array_length - 3,
            array_length - 5
    ),
    array.slice(0, remain).join(" ")
)

動作確認

こちらも動作の様子を動画にして、X にポストしました。ちゃんと動いていることがわかるかと思います。

テンプレート配布

上の数式はかなり長いので、今回はテンプレートを用意しました。もし使ってみたい人がいればこちらから取得して試してみてください。

hkob.notion.site

おわりに

今回は、日付メンションから日付を自動設定するオートメーションを解説しました。日付メンションは非常に少ないタップ数で面倒な日付設定ができるので、日付ダイアログから編集するよりもかなり時短になります。お遊びのつもりで作ったのですが、意外と便利そうなので、私の Task データベースにも登録します。

hkob.notion.site