
日付プロパティをあれこれする ~ イチ(以下)から学ぶNotionAPI×Google Apps Script【Day.5】
こんばんは、ねここです。
基本的に在宅勤務ひきこもり野郎のぼくなのですが、今日から約1か月間、都心のオフィスに片道1時間半かけて出社することになり、初日から体力の限界と戦ってます。
通勤時の埼京線は人間が乗っていい車両ではない。
さて、昨日はマルチセレクトのタグを更新するところまで進められました。えらい。
今日は、個人的に最難関の予感がしている日時周りの操作をやっていこうと思います。
Notion内での日時情報の持ち方
日時をちゃんと取り扱うには、Notionの内部でどういう風に情報を持っているかを知る必要があります。
さっそくインテグレーションさんにお願いして、情報を持ってきてもらいます。

// ページタイトルの情報をもらう
title = res_parse["results"][i]["properties"]['名前']["title"][0]["plain_text"];
// 最終更新日時の情報をもらう
lastUpdate = res_parse["results"][i]["last_edited_time"];
console.log(title, "(" + lastUpdate + ")");

見比べてみると、Notion内部では日時のデータをUTC(協定世界時)で持っていることがわかります。まぁそうだろうとは思ってた。
この情報に、各ユーザーのタイムゾーンの設定を加算して表示する構造になっているようです。
….と思って、自分の設定を見てみたらですね。

なんとぼくは東アフリカにいたようですw
とすると、タイムゾーンの設定は参照していない?🤔
じゃあ、どこで見てるんだろうか、ということで設定をいろいろいじってみます。


言語設定にはもちろんタイムゾーン設定はありませんでした。
いろいろ調べてた中、「あっ」と思い立って、PC本体のタイムゾーン設定を変えてみました。


これだぁぁぁ!!!
なるほど、デバイスのタイムゾーン設定を反映してるんですね。
...…じゃあ、Notionの設定のタイムゾーンは何に使ってるの?🤔
(デバイスからタイムゾーン情報を取れないときにこれを参照する可能性はありそうですかね)。
少し寄り道しましたが、作成日時、最終更新日時はUTC(GMT+0)で格納していました。自動で格納するのでそのほうが都合いいのはとてもわかります。
では、日付プロパティはどうでしょう。
実はあまり意識しないところですが、日付プロパティにはタイムゾーンを設定できる箇所があります。

ここにタイムゾーン込みで日時を入れることで、内部でUTCに変換されて格納され…..


..…てませんねぇ🤔
タイムゾーンを含めた日付情報が格納されてました。



日付プロパティでタイムゾーンをUTCにして「3:45」に設定すると、Notionではデバイスのタイムゾーンが適用されて「12:45」と表示され、データベース内では設定時のタイムゾーンを含めた日時情報(2024-02-13T03:45:00.000+00:00)が格納されていました。
コードを書くとき、このあたりをあまり意識しなくても問題ないのかもですが、なんか時間周りの挙動が怪しいなと思ったらこのあたりから確認するのがよさそう、という知見をゲットです。
API経由で日付プロパティを読み込む
もうすでに上記でいろいろやってますがw、日付プロパティから日時を読み込むのを改めてやっていきます。
先頭のアイテムの日付プロパティに設定されている日時は、こんな感じで取得することができます。
res = getDBquery(filter);
res_parse = JSON.parse(res);
property_datetime = res_parse["results"][0]["properties"]['日付']["date"]["start"];
.…が、実はこれだけでは足りません。
Notionの日付プロパティは情報が入っていない状況も許容されます。プロパティを作った直後は何も情報入ってないですよね。
ですので、日付プロパティが空っぽのアイテムに対して上のコードが走るとエラーが返ってきます。

日付プロパティに何も情報を入れていない場合、dateにはnullが入っています。

日付情報が何もない(null)ので、dateの中のstartを参照しようとしてエラーが発生する、という感じですね。
ですので、日付が入っているのかどうかを先に確認しておく必要があります。
// 日付プロパティがnullじゃないなら(データが入っているなら)startの情報をもらう
if(res_parse["results"][i]["properties"]['日付']["date"] != null) {
p_date = res_parse["results"][i]["properties"]['日付']["date"]["start"];
} else { // 逆にnullだったら、空文字列をいれておく(nullいれちゃってもいいかもね)
p_date = "";
}
また、Notionの日付は終了日時を設定することもできます。


終了日を取得するときはendからもらってくることになります。
しかし、ここも気を付けないといけないところで、終了日を設定していないと、endにはnullが入ります。

ですので、endから情報をもらうときは、ここも先にnullではないことを確認しておく必要がありますね(未来の自分へ)。
API経由で日付プロパティに書き込む
読み込みは概ねできるようになったので、次は任意の日時を日付プロパティに書き込んでいこうと思います。
昨日作成したPATCH処理するスクリプトを改造していきます(別にイチから作るのがめんどくさいわけじゃないんだからね!)
// 更新対象の抽出するフィルタを設定する
filter = {
"filter": {
"property":"ジャンル",
"multi_select": {
"is_empty": true
}
}
};
// 書き込む内容を設定する
update = {
"properties": {
"日付": {
"date": {
"start": "2024-02-13T12:34:56.789+09:00"
}
}
}
};
ジャンルが未設定のページの日付プロパティに「2024/2/13 12:34:56.789(GMT+9)」の日時を設定します。

ところで、Notionは時間の最小単位は分で、秒以下は使用されない仕様になっています。なので今回ミリ秒まで埋めてみたわけですが、Notion側では分までしか保存されていませんでした。

秒以下の数値を設定してもNotion側で保存するときに捨てられるようですね。とりあえずAPIから登録するときに秒以下の扱いを気にしないでいいことがわかったのでよしですね。
次は、変数を使う形にして、終了日のプロパティも入れてみます。
// 書き込む内容を設定する
start_datetime = "2024-02-13T12:34:56.789+09:00";
end_datetime = "2024-02-13T13:59:00.000+09:00";
update = {
"properties": {
"日付": {
"date": {
"start": start_datetime,
"end": end_datetime
}
}
}
};

これもしっかり設定できましたね。
endに日付を入れるかどうか決まってない場合は、nullを入れれば大丈夫そうですね。
ということで、日付プロパティに任意の日付を設定することもできるようになりました。やったぜ。
さて。
以前の記事で書いたかもですが、ぼくは元々ソフトウェアテストをやっていた人でして。
こういうのをいじるとですね、想定外のパターンというのも試したくなるんですよねw
ということで「今後スクリプトを組むときに、意図せず発生する可能性があるパターン」を3つほど考えたので試してみます(もちろんバグがあるとは思ってないですが、どういう挙動をするかは興味ある)。
正常じゃないパターン1:終了日が開始日よりも古い
開始日から過去に向かうパターンです。異常系のテストケースを考えるとき、真っ先に書き出しそうなパターンですね。
// 書き込む内容を設定する
// 2024/2/13 12:34 ⇒ 2024/2/12 6:00
start_datetime = "2024-02-13T12:34:56.789+09:00";
end_datetime = "2024-02-12T06:00:00.000+09:00";
update = {
"properties": {
"日付": {
"date": {
"start": start_datetime,
"end": end_datetime
}
}
}
};

ちゃんとエラーが返ってきました。すばらしい。
終了日も扱うときは気を付ける必要がありそうですね。
正常じゃないパターン2:終了日にだけ日付を設定する
上の亜種ですが、start に null、end に日付を入れてみます。
きっと似たエラーが返ってくるでしょう。
// 書き込む内容を設定する
start_datetime = null;
end_datetime = "2024-02-12T00:00:00.000+09:00";
update = {
"properties": {
"日付": {
"date": {
"start": start_datetime,
"end": end_datetime
}
}
}
};

ちゃんとエラーが返ってきました。すばら。
これはまぁ実際にやってしまう可能性は低いとは思いますが、おかしなことにはならなさそうで安心です。
正常じゃないパターン3:開始日と終了日でタイムゾーン設定が異なる
開始日はGMT+9だけど、終了日はGMT+0、みたいなパターンです。
Notionで直接日付設定するときはそれぞれでタイムゾーンを設定することはできないので、どういう挙動を取るのかかなり興味があります。
ちゃんと動いても不思議ではないし、許容しなくても全然よし。
// 書き込む内容を設定する
// GMT+9だと 2024/2/13 12:00 ⇒ 2024/2/14 14:00
start_datetime = "2024-02-13T12:00:00.000+09:00";
end_datetime = "2024-02-14T07:00:00.000+02:00";
update = {
"properties": {
"日付": {
"date": {
"start": start_datetime,
"end": end_datetime
}
}
}
};
いざ….!


エラーは出ませんでしたが、終了日のタイムゾーンは無視され、2024/2/14 7:00(GMT+9)が設定されていますね。
念のため、開始日のほうをGMT+2、終了日のほうをGMT+9にしてみたところ、日時は設定そのまま、タイムゾーンだけGMT+2で設定されました。
// GMT+9だと 2024/2/13 12:00 ⇒ 2024/2/14 14:00
start_datetime = "2024-02-13T05:00:00.000+02:00";
end_datetime = "2024-02-14T14:00:00.000+09:00";


開始日と終了日をそれぞれ別に加工する場合、タイムゾーンを事前に揃えておかないと、終了日に思わぬ時間が設定される可能性がありますね。気を付けましょうね(未来の自分)。
読み込んだ日時を加工して書き込む
ここまでできたら、あとは読み込みと書き込みを繋げるフェーズです。
今回はページ作成日時を読み込んで、2時間半加算して、日付プロパティに設定する、というのをやってみます。

フィルタは空にして、この2件の作成日時を取得します。
title = res_parse["results"][i]["properties"]['名前']["title"][0]["plain_text"];
createdate = res_parse["results"][i]["created_time"];
console.log(title, "(" + createdate + ")");

これをそれぞれGASのDateオブジェクトに入れて、時間を操作して、日付文字列に戻して書き込む、という感じになりますね。
まずはDateオブジェクトに入れるところから。変数名が雑なのは、とりあえず気にしてはいけない(いまさら)。
title = res_parse["results"][i]["properties"]['名前']["title"][0]["plain_text"];
createdate = res_parse["results"][i]["created_time"];
createdate_Date = new Date(createdate);
console.log(title, "(" + createdate_Date + ")");

GASには直接Date型のデータを演算する方法がないようで、年月日時分をそれぞれ分けて足し引きしたりするなど、検索するといろんな手法が紹介されていました。
その中で、ぼくが直感的で扱いやすそうと思ったのが、getTime, setTimeを使う方法です。
(直感的かどうかは個人差あると思いますがw)
title = res_parse["results"][i]["properties"]['名前']["title"][0]["plain_text"];
createdate = res_parse["results"][i]["created_time"];
createdate_Date = new Date(createdate);
// 1分 = 60000ミリ秒
// 2時間半 = 150分
createdate_Date.setTime(createdate_Date.getTime() + (150*60000));
start_datetime = createdate_Date[i].toJSON();

両方とも2時間半プラスされました!
もちろん過去の日付にするのも、月をまたぐのも(加減する値を間違えなければ)簡単です。
// 21日前(3週間前)を指定
// 1日 = 60000ミリ秒 × 60分 × 24時間
createdate_Date.setTime(createdate_Date.getTime() + (21 * (24*60*60000)));

ということで、日付情報をもらい、加減して、改めて日付プロパティに書き込む、という処理をすることができました。
getTime, setTimeを使うの、めちゃくちゃ楽でいいな!
というところで今日はここまで。
昨日書いた、ぼくがやりたい目標を達成するのに必要な知見はわりと揃った気がするので、明日からは実際に使うスクリプトを構築していきたいと思います。
ほんとはもう少し日数掛かるかと思ってたけど、思ったよりも早くここまでいきついた….!
ところで、投稿のタイミング的に、完全に仕事中に書いてますよね?(自白)
今日のカバー画像
最初はカレンダーとか時計を睨みながら悩む子、という風にしたかったけどまったくそんな風になりそうな気配がなかったので、シンプルに考える子になりました。複雑な条件を設定するのはなかなか難しい。
NEXT