ダウンロード方法
基本的な機能
- 予定を立てられる
- 予定日までの期間を自動で計算して表示してくれる
- 予定時刻の範囲も自動で計算して表示してくれる
- タスク(To do)を立てられる
- Googleカレンダーと相互同期できる
.jpg)
メリット
特殊記号による爆速作成
特殊: #, ##, ###, n, m, i, 月~日, +(プラス), -(マイナス)
カレンダーとタスクでは記号を使って予定日時を決定できます。
「t」や「~」といった時間や範囲を追加する基本的な記号はもちろんのこと。
特別な意味を持った記号を使うことで速攻で予定を立てたり、 相対日時を前もってボタンに登録したりできます。
① 2 / 4
② 204
① # 1
② i 1
① 2 / 9
② 209
① ## 0 + 1
② 日
GoogleカレンダーにNotionのページをひもづけ
GカレとNotionのいいとこどり!


NotionのページのリンクがGoogleカレンダーの説明欄に貼られているさま。
同期ボタンを押すだけで自動的にリンクが付与される。
上: Notionのページ
下: Googleカレンダーのイベント
こんな感じでNotionとGoogleカレンダーを同期させることができます!
同期されたNotionのページには、目的地や持っていくものを書いたり、予定の詳細を書いたり、はたまた予定の感想を書いても今日の日記を書いたりしてもよし
そのほかNotionの高度な機能も存分に利用できるため、メンションやリレーションで今日会う人の情報や行く場所の情報を既存のデータベースから取り出したり、写真や見出しなどの装飾を施したりなど何でもできる!
実質的にGoogleカレンダーのイベントの説明欄に、カスタマイズし放題の説明欄がついてくるって感じなのです!
期限のない予定の管理
カレンダーでは扱いづらい無期限の予定を自動仕分け!


自動的に猶予ビューに振り分けられるから通常の予定も邪魔しないさま。
上: Notionの各ページ
下: Notionの猶予ビュー
※猶予ビューには無期限の予定および残り7日を切っていない予定が出現します。
より扱いやすい通知機能
マルチセレクトからお気に入りの通知次期を選ぶだけ!

マルチセレクトからリマインド時刻を選択できるさま
図: Notionのマルチセレクト選択画面
直近ビューと直近ソート
実質的な二重通知で絶対に予定を見逃さない!!

直近ビューないの予定が開始時期の近い順に並べられているさま
図: Notionの直近ビュー(看板型)
同期のメリット
Googleカレンダーと同期する理由はリマインドにあった!!
NotionのカレンダービューやNotionカレンダーでもカレンダーの機能は使えるのだから、あえて連携させる必要を感じない人もいると思います。
ではなぜ連携させるのか。
それは主にリマインダーのためです。
Notionのfomulaプロパティで作成した予定日のカレンダーからは直接リマインドをかけられません。
この場合、Notionカレンダーと連携しても同じくリマインドはできません。
なので! Googleカレンダーに予定を普通に作っちゃってリマインドしようということなのです! GoogleカレンダーならNotionカレンダーと違い、リマインドのほかにもほかのカレンダーアプリと連携しやすい利点もあるですね!
次の2つの条件に当てはまる人は必ずしもGoogleカレンダーを連携させる必要はないと思います
✅ 常にNotionを使い、最低でも1週間に1回はNotionを開く人。
※リンクドビューで自分のHomeのページに直近ビュー(期限が7日以下に迫った予定)を貼っていればリマインドとほぼ一緒。
✅ Googleカレンダーないしは他のサードパーティー製のカレンダーも使う予定がない人
※Notionカレンダー or Notionのデータベースのカレンダービューで十分だよね。
前準備
Notion
テンプレートのダウンロード
まずは自分のNotionに今回のテンプレートをダウンロードしてください。
このボタンを押してダウンロードします
ダウンロード
テンプレートの初期設定
1. ダウンロードしたテンプレート(Calendar & Tasks)を開く。

2. 画像のようなページが出てきたら、作成ボタンとなっているところを押す
3. 直近で右クリックし、ビューのリンクをコピーする。
4. ホームのページを開く
5. 貼り付けたい場所を選んでペースト
6.「リンクドデータベース」を選択する

7. C&T DBとなっている部分の右の「・・・」を押す
8.「タイトルを非表示」を選択
9.「+」→「+ 空のビュー」→「C&T DB」→「猶予」で新規ビュー追加
10.「+」→「+ 空のビュー」→「C&T DB」→「直近完了」で新規ビュー追加
Notion API の設定
1.「C&T DB」を開く
2. 右上の「・・・」を押す。
3.「接続」を押す。
4. 自分のインテグレーションを選択する。
GASの設定
1. Google Apps Script を開く
2. 新規プロジェクトを作成する(名前: Notion🔁Gカレンダー)
3. 下のコードを「コード.gs」に貼り付ける
const NOTION_TOKEN=PropertiesService.getScriptProperties().getProperty("NOTION_TOKEN"),NOTION_DATABASE_ID=PropertiesService.getScriptProperties().getProperty("NOTION_DATABASE_ID"),DBUrl=https://api.notion.com/v1/databases/${NOTION_DATABASE_ID}/query,Gcid="primary",NOTIFICATION_EMAIL=PropertiesService.getScriptProperties().getProperty("NOTIFICATION_EMAIL");function Try_main(){main()}function main(){let e=querySync(!1);if(e.length<=0){console.log("!Tempリストが空なので今回の同期はありません");return}e.forEach(e=>{let t=e.mark;if("from GC"===t){let n=e.eventID,r=e.remind,i=e.title,l=e.start.includes("T")?e.start:${e.start}T00:00+0900,a=e.end.includes("T")?e.end:${e.end}T00:00+0900,s=brakeDate(l,a),{ID:o,syncState:c,bell:$,Link:g}=writeNotion(t,e.ID,l,r,i,s,null,null);if("through"===c)return;writeGC(t,o,null,$,g,i,l,a,!1,c,n)}if("from Notion"===t){let u=e.pageID,p=e.URL,d=e.ID,m=e.content,f=e.junkDate,D=e.remind,h=e.make,=e.pastDate,{startDate:T,endDate:I}=createDate(f,h),y=writeGC(t,d,,D,p,m,T,I,!1!==,null,null);writeNotion(t,d,null,null,null,null,y,u)}})}function Try_CD(){Try_main()}function createDate(e,t){let n=e,r,i,l,a,s="1000-01-00",o=1,c=new Date(t);t=new Date(t),n=(n=(n=n.replace(/\n/g,"")).replace(/[\s::;:/]/g,"")).replace(/0/g,"0").replace(/1/g,"1").replace(/2/g,"2").replace(/3/g,"3").replace(/4/g,"4").replace(/5/g,"5").replace(/6/g,"6").replace(/7/g,"7").replace(/8/g,"8").replace(/9/g,"9");let $=n.substring(0,1),g="曜日"===n.substring(1,3)?Number(n.substring(3,4)):"曜日"===n.substring(1,2)?Number(n.substring(2,3)):Number(n.substring(1,2)),u=!!"月火水木金土日".includes($);if(r=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=n.replace(/ま|m|m|M/g,"M")).replace(/月/g,"##?-5").replace(/火/g,"##?-4").replace(/水/g,"##?-3").replace(/木/g,"##?-2").replace(/金/g,"##?-1").replace(/土/g,"##?-0").replace(/日/g,"##?+1-0")).replace(/#/g,"#")).replace(/時|じ|た|t|t|T|※|\/g,"T")).replace(/#T/g,"T#")).replace(/来|ら|n|n|N/g,"N")).replace(/今|い|i/g,"I")).replace(/間|あ|き|~|〜|a|A|k|k|K|K|x|x|X|X/g,"~")).replace(/~T/g,"~#0T")).replace(/####/g,"ERROR")).replace(/###/g,"【M】#")).replace(/##/g,"【W】#")).replace(/I/g,"I#")).replace(/I##/g,"I#")).replace(/~0/g,"~#0")).replace(/#0\./g,"【小数保持】")).replace(/\(-/g,"【負数保持】")).replace(/#000000000/g,"ERROR").replace(/#00000000/g,"#7").replace(/#0000000/g,"#6").replace(/#000000/g,"#5").replace(/#00000/g,"#4").replace(/#0000/g,"#3").replace(/#000/g,"#2").replace(/#00/g,"#1").replace(/#/g,"#0")).replace(/プ|足|+/g,"+")).replace(/マ|引|-|ー|—/g,"-")).replace(/\+/g,"【計算】+")).replace(/-/g,"【計算】-")).replace(/【小数保持】/g,"#0.")).replace(/【負数保持】/g,"-")).replace(/\(|\)/g,"")).replace(/か|カ|c|c|C/g,"C")).replace(/?/g,g)).replace(/T/g,"\nT").split("\n"),i=n.replace(/~/g,"\n~").split("\n"),a=n.replace(/-/g,"-\n").split("\n"),!0===u&&(l=null==r[1]?${i[1]}:${r[1]},n=${a[0]}+${a[1]}.substring(0,1)+l),"M"===(n=${n}.replace(/undefined/g,"").replace(/NaN/g,""))||"MM"===n||"MMM"===n)return{startDate:s,endDate:o};if(0===(n=n.replace(/MMMM/g,"ERROR").replace(/MMM/g,"【M】#1【計算】+").replace(/MM/g,"【M】#0【計算】+").replace(/M/g,"【M】#(-1)【計算】+")).length)return o=8,{startDate:s,endDate:o};n=(r=n.split("")).includes("コメント")||1===r.length?r[0]:${r[0]}~${r[1]}.replace(/~~/g,"~");let p=${n.replace(/\\d|#|【M】|【W】|T|N|I|~|\\+|-|【計算】|\\.|\\(|\\)|\\/|?/g,"")};if(0!==p.length&&!p.includes("undefined")||${r[0]}.length-${r[0]}.replace(/-/g,"").length>2||${r[0]}.length-${r[0]}.replace(/\+/g,"").length>2||${r[1]}.length-${r[1]}.replace(/-/g,"").length>1||${r[1]}.length-${r[1]}.replace(/\+/g,"").length>1)return console.log("無効なインプット"),{startDate:s,endDate:o};n.includes("I")&&(n=n.replace(/I#0T/,"I#0T#").replace(/I/,"I#").replace(/##/g,"#")),${(r=n.split("~"))[0]}.includes("【M】")&&${r[0]}.includes("【W】")&&(n=n.replace(/【W】/,"/【W】"));let d=n.split("/"),m;${r[0]}.includes("【M】")&&${r[0]}.includes("【W】")&&(n=d[1],m=!0);let f=${d[0]};i=${(r=n.split("~"))[0]}.split("T");let D=${i[1]}.replace(/#/g,""),h=!!${i[1]}.includes("#");l=${i[0]}.replace(/N/g,"").split("【計算】");let =${l[0]},T=Number(l.filter(e=>e.includes("+")).join("").replace("+","")),I=Number(l.filter(e=>e.includes("-")).join("").replace("-",""));i=${r[1]}.split("T");let y=${i[1]}.replace(/#/g,""),M=!!${i[1]}.includes("#");l=${i[0]}.replace(/N/g,"").split("【計算】");let N=${l[0]},b=Number(l.filter(e=>e.includes("+")).join("").replace("+","")),E=Number(l.filter(e=>e.includes("-")).join("").replace("-",""));n.includes("N")&&t.setFullYear(t.getFullYear()+1);let w=t.getHours(),S=t.getMinutes();r=.replace(/#|【M】|【W】|I/g,"").split("-");let L=Number(r[0])-Number(r[1]||0);r=D.replace(/#|【M】|【W】|I/g,"").split("-");let R=Number(r[0])-Number(r[1]||0);r=N.replace(/#|【M】|【W】|I/g,"").split("-");let H=Number(r[0])-Number(r[1]||0);r=y.replace(/#|【M】|【W】|I/g,"").split("-");let O=Number(r[0])-Number(r[1]||0),A=["日","月","火","水","木","金","土"],v=A[t.getDay()],k=["土","日","月","火","水","木","金"],P=7-k.indexOf(v);"土"===v&&(P-=7),"日"===v&&(P-=6),i=(r=f.replace(/N/g,"").split("【計算】"))[0].replace(/【M】|#|I/g,"").split("-");let C=Number(i[0])-Number(i[1]||0),G=Number(r.filter(e=>e.includes("+")).join("").replace("+","")),x=Number(r.filter(e=>e.includes("-")).join("").replace("-","")),U=new Date(t.getFullYear(),t.getMonth(),1);U.setMonth(U.getMonth()+C+1),U.setDate(U.getDate()+(1-(G||0)+(-x||0)));let j=A[U.getDay()],F=7-k.indexOf(j);"土"===v&&(F-=7),"日"===v&&(F-=6);let B;switch(!0){case m:U.setDate(U.getDate()+F+7L),B=U;break;case .includes("【M】#"):(B=new Date(t.getFullYear(),t.getMonth(),1)).setMonth(B.getMonth()+L+1),B.setDate(B.getDate()-1);break;case .includes("【W】#"):(B=t).setDate(B.getDate()+P+7*L);break;case .includes("#"):(B=t).setDate(B.getDate()+L);break;case .includes("/")&&.length<6:case .length<5:B=t.getFullYear()+;break;default:B=}.includes("#")&&(B.setDate(B.getDate()+(T||0)+(-I||0)),B=Utilities.formatDate(B,Session.getScriptTimeZone(),"yyyyMdd"));let Z=B.substring(0,4),W=4===B.substring(4).length?B.substring(4,6):"0"+B.substring(4,5),q=1===(r=B.substring(Z.length+W.replace(/0/,"").length)).length?"0"+r:r,Y=4===D.length?D.substring(0,2):"0"+D.substring(0,1);r=10>Number(Y)?D.substring(1):D.substring(2);let K=1===r.length?"0"+r:r,z=Y+":"+K;B.includes("/")&&(W=(r=B.substring(4).split("/"))[0],q=r[1],1===W.length&&(W="0"+W),1===q.length&&(q="0"+q));let V=Number(D)&&!1===h?${Z}-${W}-${q}T${z}+0900:${Z}-${W}-${q}T00:00+0900;yotei=new Date(V),!0===u&&yotei<=c&&yotei.setDate(yotei.getDate()+7);let X=new Date(yotei);switch(!0){case N.includes("【M】#"):(X=new Date(yotei.getFullYear(),yotei.getMonth(),1)).setMonth(X.getMonth()+H+1),X.setDate(X.getDate()-1);break;case N.includes("【W】#"):X.setDate(X.getDate()+P+7H);break;case N.includes("#"):X.setDate(X.getDate()+H);break;case N.includes("/")&&N.length<6:case N.length<5:X=yotei.getFullYear()+N;break;case""==!N||N:X=N;break;default:sendError("スイッチエラー")}(N.includes("#")||X===yotei)&&(X.setDate(X.getDate()+(b||0)+(-E||0)),X=Utilities.formatDate(X,Session.getScriptTimeZone(),"yyyyMdd"));let J=X.substring(0,4),Q=4===X.substring(4).length?X.substring(4,6):"0"+X.substring(4,5),ee=1===(r=X.substring(J.length+Q.replace(/0/,"").length)).length?"0"+r:r,et=4===y.length?y.substring(0,2):"0"+y.substring(0,1);r=10>Number(et)?y.substring(1):y.substring(2);let en=1===r.length?"0"+r:r;X.includes("/")&&(Q=(r=X.substring(4).split("/"))[0],ee=r[1],1===Q.length&&(Q="0"+Q),1===ee.length&&(ee="0"+ee)),r=!0===M?${J}-${Q}-${ee}T00:00+0900:Number(y)&&!1===M?${J}-${Q}-${ee}T${et+":"+en}+0900:X.includes("undefined")?${V}.replace(/T\d\d:\d\d/,"T23:59"):${J}-${Q}-${ee}T23:59+0900;let er=new Date(r);return _.includes("I")&&(yotei.setHours(yotei.getHours()+w),yotei.setMinutes(yotei.getMinutes()+S)),!0===h&&yotei.setMinutes(yotei.getMinutes()+60R),!0===M&&(er.setHours(er.getHours()+yotei.getHours()),er.setMinutes(er.getMinutes()+yotei.getMinutes()),er.setMinutes(er.getMinutes()+60*O)),yotei-er==51846e4&&er.setDate(yotei.getDate()),r=new Date(er+"+0900"),yotei>r.setDate(r.getDate()+1)&&er.setFullYear(er.getFullYear()+1),yotei instanceof Date&&(s=yotei,o=er),console.log(s,"+\n",o),{startDate:s,endDate:o}}function brakeDate(e,t){e=new Date(e),t=new Date(t);let n=t-e==864e5,r;return(e=Utilities.formatDate(e,Session.getScriptTimeZone(),"yyyyMddTHmm"))===(t=Utilities.formatDate(t,Session.getScriptTimeZone(),"yyyyMddTHmm"))||n?${e}:${e}~${t}}function Try_fN(){Try_main()}function getNotion(){let e=toNotion(DBUrl,{property:"Recent",checkbox:{equals:!0}},null,"");if(!e||"function"!=typeof e.getContentText){console.log("!1時間以内に更新された予定がなかったためスキップします");return}let t=JSON.parse(e.getContentText()),n=t.results.map(e=>{let t=e.properties.ID||"",n=e.properties.remind.multi_select;return{pageID:e.id,URL:https://www.notion.so/${e.id.replace(/-/g,"")},ID:${t.unique_id?.prefix}-${t.unique_id?.number},content:e.properties["内容"].title[0]?.text.content||void 0,junkDate:e.properties.input?.rich_text[0]?.text.content||"",remind:""==${n}?"# デフォルト":n.map(e=>e.name).join("【*M】"),make:e.properties["作成日"]?.created_time||void 0,update:e.properties["更新"]?.last_edited_time||void 0}});return n}function writeGC(e,t,n,r,i,l,a,s,o,c,$){let g=CalendarApp.getCalendarById(Gcid);if("from GC"===e){if(!1!==$){Calendar.Events.remove(Gcid,$),writeGC(e="third sync",t,null,r,i,l,a,s,!1,c,$);return}let u=new Date(a),p,d;if(r.includes("リマインドなし"))d=${t} ${i} ==================== ${c};else{r=createRemind(u,r);let m=r.includes("メール通知"),f=!r.includes("only");d=${t} ${i} ${r=formatDes_R(u,r,m,f)}==================== ${c}}let D=g.getEventsForDay(u);D.forEach(e=>{let n=e.getDescription();n.includes(t)&&(e.setDescription(d),p=e.getLastUpdated(),console.log(${t}の再同期が正常に完了しました),saveList(t,p,!1,c))});return}let h=new Date(a),=new Date(s),T=!0===o?new Date(n):null,I=6e4>=Math.abs((-h)%864e5-864e5)&&0===h.getHours()&&(23===.getHours()&&59==.getMinutes()||0==.getHours()&&0==.getHours());!0===I&&23===.getHours()&&.setMinutes(.getMinutes()+1);let y=r.length-r.replace(/#/g,"").length,M="false"==r?0:(r.length-r.replace(/【\*M】/g,"").length)/4+1,N=M-y,b=r.includes("メール通知"),E=!r.includes("only"),w=!r.includes("リマインドなし"),S="false"==r||0===N&&!0==w||r.includes("デフォルト");if(!0===w&&N>0?(r=createRemind(h,r),w=!0):w=!1,!1===o){let L=🌠${t} 新規予定を作成します🌠;return c=createEvent(I,L,t,i,e,g,h,,l,r,w,S,b,E,c)}let R=g.getEventsForDay(T);if(R.length<=0){let H=🌠${t} 保存されている日付でイベントを見つけられなかったため再作成します🌠;return c=createEvent(I,H,t,i,e,g,h,,l,r,w,S,b,E,c)}let O=R.map(n=>{let a=n.getDescription();return!!a.includes(t)&&(c=writeEvent(I,n,t,i,e,g,h,,l,r,w,S,b,E,c),!0)});if(!${O}.includes("true")){let A=🌠${t} 保存されているIDのイベントをが存在しなかったため再作成します🌠;c=createEvent(I,A,t,i,e,g,h,,l,r,w,S,b,E,c)}return c}function createEvent(e,t,n,r,i,l,a,s,o,c,$,g,u,p,d){let m=formatDes_R(a,c,u,p),f=d,D;if(option={get description(){return${n} ${r} ${m}==================== ${f}}},!0===e){console.log(${t}(終日)),"from Notion"===i&&(f=createSync());let h=l.createAllDayEvent(o,a,s,option);cotrolRemind(h,c,$,g,u,p),D=h.getLastUpdated()}else{console.log(${t}(時間指定)),"from Notion"===i&&(f=createSync());let =l.createEvent(o,a,s,option);cotrolRemind(,c,$,g,u,p),D=.getLastUpdated()}return saveList(n,D,a,f),f}function writeEvent(e,t,n,r,i,l,a,s,o,c,$,g,u,p,d){let m=formatDes_R(a,c,u,p),f=d,D;option={get description(){return${n} ${r} ${m}==================== ${f}}};let h=t.isAllDayEvent();if(e!==h){t.deleteEvent();let =🌠${n} 予定の形式が変わったため、既存の予定を削除して新たな予定を作成します🌠;return createEvent(,n,r,i,l,a,s,o,c,$,g,u,p)}return!0===e&&(t.setTitle(o),s.setDate(s.getDate()+1),t.setAllDayDates(a,s),"from Notion"===i&&(f=createSync()),t.setDescription(option.description),cotrolRemind(t,c,$,g,u,p),D=t.getLastUpdated(),console.log(🌠${n} 既存の予定(終日) を上書きしました🌠)),!1===e&&(t.setTitle(o),t.setTime(a,s),"from Notion"===i&&(f=createSync()),t.setDescription(option.description),cotrolRemind(t,c,$,g,u,p),D=t.getLastUpdated(),console.log(🌠${n} 既存の予定(時間指定) を上書きしました🌠)),saveList(n,D,a,f),f}function createRemind(e,t){return t=(t=(t=t.replace(/(【\M】)|[^a-zA-Z0-9一-龠ぁ-んァ-ンー0-9#:]/g,(e,t)=>t||"")).split("【M】").filter(e=>!e.includes("#"))).map(t=>{let n=0;if(!t.includes(":")){let r=[...t.matchAll(/(\d+)(分|時間|日)/g)];for(let i of r){let l=Number(i[1]),a=i[2];"分"===a&&(n+=l),"時間"===a&&(n+=60l),"日"===a&&(n+=1440l)}}if(t.includes(":")){t=t.replace(/当日/,"0日前").replace(/前日/,"1日前");let s=t.match(/(\d+)(日前)(\d{1,2}):(\d{2})/);if(s){let o=Number(s[1])-1,c=24-Number(s[3]),$=60-Number(s[4]),g=new Date(e),u=new Date(e);g.setMinutes(g.getMinutes()+1440o+60c+$),g.setMinutes(g.getMinutes()+60u.getHours()+u.getMinutes()),n=(g-u)/6e4,n-=60}}return n})}function formatDes_R(e,t,n,r){if(${t}.includes("リマインドなし")||t&&t.length<1)return"";if(n=!0===n?"\uD83D\uDC8Cあり\n":"",!1===r&&(n=n.replace(/あり/,"のみ")),"# デフォルト"==${t})return==================== ${n}🔔デフォルト通知 ;t.sort((e,t)=>t-e);let i=["日","月","火","水","木","金","土"],l=t.map(t=>{let n=new Date(e);n.setMinutes(n.getMinutes()-t);let r=i[n.getDay()];return Utilities.formatDate(n,Session.getScriptTimeZone(),🔔M月dd日(${r}) HH:mm)});return==================== ${n}${l=${l}.replace(/,/g,"\\n")} }function cotrolRemind(e,t,n,r,i,l){e.removeAllReminders(),!0===r&&(console.log("デフォルトのリマインドを追加します"),e.addPopupReminder(30),!0===i&&(console.log("デフォルトのリマインドを追加します(メール通知型)"),e.addEmailReminder(30))),!1!==n&&t.forEach(t=>{(!0!==i||(console.log(メール通知のリマインド (${t}分前) を追加します),e.addEmailReminder(t),!1!==l))&&(console.log(ポップアップ通知のリマインド (${t}分前) を追加します),e.addPopupReminder(t))})}function Try_tN(){Try_main()}function getGC(){let e={timeMin:new Date().toISOString(),singleEvents:!0,orderBy:"updated"},t=Calendar.Events.list(Gcid,e),n=t.items.map(e=>({eventID:e.id,reminders:e.reminders.overrides||!1,summary:e.summary||"無題",start:e.start.dateTime||e.start.date||"不明",end:e.end.dateTime||e.end.date||"不明",description:e.description||"なし",update:e.updated}));return n}function writeNotion(e,t,n,r,i,l,a,s){let o=!1!==t?Number(t.replace(/ID-/g,"")):0;if("from Notion"===e){let c={properties:{同期:{rich_text:[{text:{content:a}}]}}};syncToNotion(t,c,s),console.log(${t}の再同期が正常に完了しました);return}let $;r=formatRemind(n,r);let g="あああ",u={parent:{database_id:NOTION_DATABASE_ID},properties:{内容:{title:[{text:{content:i}}]},input:{rich_text:[{text:{content:l}}]},remind:{multi_select:r},get 同期(){return{rich_text:[{text:{content:g}}]}}}};if(!1===t){g=a=createSync();let{Bangou:p,URL:d,update:m,bell:f}=syncToNotion(t,u,"");t=p,g=m,r=f,$=d,console.log(🌠Notionに新規ページを作成しました(${t})🌠)}else{let D=toNotion(DBUrl,{property:"ID",number:{equals:o}},null,""),h=JSON.parse(D.getContentText());if(0==h.results.length)return console.log(🌠保存されているIDのページが見つかりませんでしたので無視します(${t})🌠),throughList(t),a="through",$=null,{syncState:a,bell:null,Link:$};{s=h.results[0].id,delete u.parent,g=a=createSync();let{Bangou:,URL:T,update:I,bell:y}=syncToNotion(t,u,s);t=,g=I,r=y,$=T,console.log(🌠Notionの既存ページ(${t})を上書きしました🌠)}}saveList(t,g,n,a);let M=r;return{ID:t,syncState:a,bell:M,Link:$}}function formatRemind(e,t){if(!1===t)return t=[{name:"# リマインドなし"}];{if(!Array.isArray(t))return[{name:t}];let n=[];t.forEach(t=>{t.method.includes("email")&&n.push({name:"# contain: メール通知"}),n.push({name:brakeRemind(e,t.minutes)})}),t=n}let r=${t.map(e=>e.name)},i=r.length-r.replace(/#/g,"").length,l=t.length-i;return l>2&&r.includes("contain")&&(t.push({name:"# only: メール通知"}),t=t.filter(e=>!e.name.includes("contain"))),t}function brakeRemind(e,t){let n=new Date(e),r=new Date(e);r.setMinutes(r.getMinutes()-t);let i=n.getDate()-r.getDate(),l=Math.floor(t%1440/60),a=Math.floor(t%60),s=!1;return(r.getMinutes()%15==0||r.getMinutes()%10==0)&&(s=!0),!0===s&&(e.includes("T00:00")||t>180&&t%360!=0)?((t= ${i} 日前 ${r.getHours()}:${r.getMinutes()}.replace(/1 日前/,"前日")).includes(":00")||(t=t.replace(/:0/,":00")),t.includes(":60")&&(t=t.replace(/:6/,":0")),t=t.includes(" 0 日前 ")?t.replace(/ 0 日前 /,"当日 "):t.replace(/ /,"")):((t= ${i} 日 ${l} 時間 ${a} 分 前).includes(" 0 日 ")&&(t=t.replace(/ 0 日/,"")),t.includes(" 0 時間 ")&&(t=t.replace(/ 0 時間/,"")),t.includes(" 0 分 ")&&(t=t.replace(/ 0 分/,"")),t=t.replace(/ 前/,"前").replace(/ /,"当日 00:00")),e.includes("T00:00")&&""===t&&(t="0 分前"),t}function syncToNotion(e,t,n){let r=toNotion(DBUrl,null,t,n),i=JSON.parse(r.getContentText());e=i.properties.ID||"",e=${e.unique_id?.prefix}-${e.unique_id?.number};let l=https://www.notion.so/${i.id.replace(/-/g,"")},a=i.properties["更新"]?.last_edited_time||void 0,s=i.properties.remind.multi_select,o=""==${s}?"# デフォルト":s.map(e=>e.name).join("【M】");return{Bangou:e,URL:l,update:a,bell:o}}function Try_c(){Try_main()}function saveList(e,t,n,r){e=${e},t=new Date(t),!1!==n&&(n=new Date(n));let i=loadList(),l=i.find(t=>t.ID===e);l?(l.update=t,!1!==n&&(l.startDate=n),l.syncStatus=r):i.push({ID:e,update:t,startDate:n,syncStatus:r});let a=JSON.stringify(i);return PropertiesService.getScriptProperties().setProperty("ID_LIST",a),!!l&&l}function loadList(){let e=PropertiesService.getScriptProperties().getProperty("ID_LIST"),t=e?JSON.parse(e):[];return t}function putOutList(e){let t=loadList(),n=t.filter(t=>t.ID!==e),r=JSON.stringify(n);PropertiesService.getScriptProperties().setProperty("ID_LIST",r)}function cleanList(){let e=loadList(),t=e.filter(e=>/^ID-\d+$/.test(e.ID)),n=JSON.stringify(t);PropertiesService.getScriptProperties().setProperty("ID_LIST",n)}function pastDelList(){let e=loadList(),t=e.filter(e=>new Date(e.startDate)>new Date),n=JSON.stringify(t);PropertiesService.getScriptProperties().setProperty("ID_LIST",n);let r=throughList(0),i=r.filter(e=>new Date(e.end)>new Date),l=JSON.stringify(i);PropertiesService.getScriptProperties().setProperty("THROUGH_LIST",l)}function throughList(e,t){let n=PropertiesService.getScriptProperties().getProperty("THROUGH_LIST"),r=n?JSON.parse(n):[];if(0===e)return r;r.includes(e)||r.push({ID:e,end:t});let i=JSON.stringify(r);PropertiesService.getScriptProperties().setProperty("THROUGH_LIST",i)}function hateAdd(e,t){let n=e.find(e=>e.ID===t);return!!n&&n}function Try_mt(){Try_main()}function querySync(e){let t=loadList(),n=throughList(0),r=[],i=[],l=getGC();0===l.length?console.log("!Googleカレンダーに今より未来の予定がなかったのでスキップします"):l.forEach(e=>{let n=e.description?.match(/ID-\d+/)?.[0]||!1,i=!!n&&hateAdd(t,n);if(i&&e.update<=i.update){console.log("!Googleカレンダーの更新がなかったためスキップします");return}r.push({mark:"from GC",eventID:!1===n&&e.eventID,ID:n,pastDate:!1!==i&&i.startDate,remind:e.reminders,title:e.summary,start:e.start,end:e.end})});let a=getNotion();return a.forEach(a=>{if(!a.content||!a.junkDate)return console.log("日付(input)または内容が空です!どちらも埋める必要があります!"),null;let s=hateAdd(t,a.ID),o=new Date(a.update),c=new Date(s.update);if(c.setSeconds(0,0),s&&o<c)return console.log("!Notionの更新がなかったためスキップします"),null;let $=r.find(e=>e.ID===s.ID)||!1;if($){if(n.some(e=>e.ID===a.ID)){console.log(${a.ID} はスルーリストにより省かれました);return}if(!0===e){let{startDate:g,endDate:u}=createDate(a.junkDate,a.make),p=l.map(e=>e.description?.match(/ID-\d+/)?.[0]==a.ID?e:null);p=p.filter(e=>null!==e);let d={title:p[0].summary,start:p[0].start,end:p[0].end,remind:p[0].reminders,update:p[0].update,ID:a.ID,URL:a.URL,content:a.content,NSDate:g,NEDate:u,Nremind:a.remind,Nupdate:a.update};i.push(d)}}else{let m={mark:"from Notion",pageID:a.pageID,URL:a.URL,ID:a.ID,pastDate:!1!==s&&s.startDate,remind:a.remind,content:a.content,junkDate:a.junkDate,make:a.make};r.push(m)}}),!0===e&&i.length>0&&hateAlert(i),r=r.filter(e=>!n.some(t=>t.ID===e.ID))}function hateAlert(e){e.forEach(e=>{let{title:t,start:n,end:r,remind:i,update:l,ID:a,URL:s,content:o,NSDate:c,NEDate:$,Nremind:g,Nupdate:u}=e,p=new Date($);c===$&&p.setDate(p.getDate()+1);let d=n.includes("T")?n:${n}T00:00+0900,m=r.includes("T")?r:${r}T00:00+0900,f=Utilities.formatDate(new Date(d),Session.getScriptTimeZone(),"M月dd日(EEE) HH:mm"),D=Utilities.formatDate(new Date(m),Session.getScriptTimeZone(),"M月dd日(EEE) HH:mm"),h=Utilities.formatDate(new Date(l),Session.getScriptTimeZone(),"M月dd日(EEE) HH:mm"),=Utilities.formatDate(new Date(c),Session.getScriptTimeZone(),"M月dd日(EEE) HH:mm"),T=Utilities.formatDate(new Date(p),Session.getScriptTimeZone(),"M月dd日(EEE) HH:mm"),I=Utilities.formatDate(new Date(u),Session.getScriptTimeZone(),"M月dd日(EEE) HH:mm"),y=i,M=createRemind(n,g);if(M.length<=0&&(M="なし"),!1===i&&(y="なし"),f===&&D===T&&t===o&&y===M){console.log(${a}の同期重複がありましたが、主要プロパティ「タイトル」「通知」「日付」に変更が見られなかったのでthroughリストに追加されませんでした);return}console.log(${a}の同期重複がありました。主要なデータが上書きされることが懸念されるので、これ以降「${a}」同期を停止します),throughList(a,new Date(p));let N=<b>==========<br>${a}<br>同期の重複が見つかったので同期を停止します。<br>解除するにはTHROUGH_LISTから「${a}」を削除するか、同一の予定を複製してください<br><br>🟢Googleカレンダーの内容<br>タイトル: ${t}<br>通知: ${y}<br>日付: ${f} ~ ${D}<br>最終更新: ${h}<br><br>⚪️<a href=${s}>Notionの内容</a><br>タイトル: ${o}<br>通知: ${M}<br>日付: ${_} ~ ${T}<br>最終更新: ${I}<br>==========;MailApp.sendEmail(NOTIFICATION_EMAIL,"GAS: カレンダー同期の重複エラー","",{htmlBody:N})})}function createSync(){let e=new Date,t=Utilities.formatDate(e,Session.getScriptTimeZone(),"M-dd HH:mm (ss")+"s)",n=e.getTime(),r=(nMath.floor(1e3Math.random())%1e6).toString().padStart(6,"0"),i=同期時刻: ${t} Code: ${r.substring(0,3)}-${r.substring(3)} ====================;return i}function toNotion(e,t,n,r){let i="post",l;t&&(l={method:i,headers:{Authorization:Bearer ${NOTION_TOKEN},"Content-Type":"application/json","Notion-Version":"2022-06-28"},payload:JSON.stringify({filter:t})}),""!==r&&(r="/"+r,i="patch"),n&&(e=https://api.notion.com/v1/pages${r},l={method:i,headers:{Authorization:Bearer ${NOTION_TOKEN},"Content-Type":"application/json","Notion-Version":"2022-06-28"},payload:JSON.stringify(n)});let a=UrlFetchApp.fetch(e,l);return a}function sendError(e){GmailApp.sendEmail(NOTIFICATION_EMAIL,【Notion🔁Gカレンダー】(${e}),"何らかのエラーが発生しました。お手数だけど自分で確認する")}
4. 環境変数を以下のように設定する
- プロパティ
- 値
-
NOTIFICATION_EMAIL
- 自分のメールアドレス
-
NOTION_TOKEN
- ※必須
Notion APIのインテグレーショントークン -
NOTION_DATABASE_ID
-
※必須
データベースのID
5.トリガーを以下のように設定する
実行する関数: main
実行するデプロイ: Head
イベントのソースを選択: 時間主導型
トリガータイプ: 分のベースタイマー
時間の間隔: 1分おき
同期テスト
1. Notionで「Calendar & Tasks」のテンプレートを開く
2. テスト用の予定を作成する

3.「Googleカレンダーと同期する」ボタンを押す
4. Google Apps Script から先ほどのプロジェクト開く
5. エディターを開き「▶実行」を押す
6. Googleカレンダーを開いて指定日に入力した予定内容が記されているか確認する
7. Notionの同期したデータの同期コードと、Googleカレンダーに同期した予定に書かれている同期コードが一致しているか確かめる
同期コードが一緒なら全ての設定は完了です!!


同期コードが一緒であるさま
上: Notionの各ページ
下: Googleカレンダーのイベント
作成方法
予定の作成方法は3つ! 用途によって使い分けてね!
① ショートカットボタンから作成
② 分類ボタンから作成
③ データベースから作成
ショートカットボタンから作成
ショートカットボタンとは現在日時からの相対的な日付を使って予定を立てられるボタンです
最初から汎用的なショートカットボタンがボタンが用意されているのですぐに使えます。
例:「今週の金曜」や「週末・月末」など
1. 好きなボタンを押す
2. タイトル部分に内容を書き込む
3. お好みで「リマインド」や「分類」をつける
分類ボタンとショートカットボタンの画像
分類ボタンから作成
予定の作成方法は3つ! 用途によって使い分けてね!
① ショートカットボタンから作成
② 分類ボタンから作成
③ データベースから作成
ショートカットボタンから作成
ショートカットボタンとは現在日時からの相対的な日付を使って予定を立てられるボタンです
最初から汎用的なショートカットボタンがボタンが用意されているのですぐに使えます。
例:「今週の金曜」や「週末・月末」など
1. 好きなボタンを押す
2. タイトル部分に内容を書き込む
3. お好みで「リマインド」や「分類」をつける

分類ボタンは作成と同時に予定に分類を付けられるボタンです。
Googleカレンダーに分類の内容は同期されないため、Notionでのフィルターに使われるだけです。
不要に感じる人は使う必要はありません。
削除しても問題ないです。
1. 好きなボタンを押す
2. タイトル部分に内容を書き込む
3.「input」を書き込む
4. お好みで「リマインド」をつける
年(4桁) + 月(1桁or2桁) + 日(2桁)
※今年の場合、年は省略できます
1/4: 104 or 1/4
12/4: 1204 or 12/4
2025 12/4: 20251204 or 2025 12/4
データベースから作成
まとめて予定を作成するときにはデータベースから予定を作成するのがおすすめ。
1. 作成ボタンとなっているところを押す
2.「🔨作成」ビューを開く
3.「+ 新規ページ」を押す
4.「内容」と「input」を書き込む
5. お好みで「リマインド」や「分類」をつける
input プロパティ
予定に時間を追加する
1.「日付」を書く
2.「日付」の後ろに「t」を書く
3.「t」の後ろに「3桁 or 4桁 の数字」を書く
~例~
12/4の9時: 1204t900
予定に期間を追加する
1.「1つ目の日付」を書く
2.「1つ目の日付」の後ろに「~」を書く
3.「~」の後ろに「2つ目の日付」を書く
~例~
1/4の14時~17時: 104t1400~t1700
1/4の14時~1/5の10時: 104t1400~105t1000
特殊記号 n の活用
n を使うことで来年を省略できます
1.「2025」などの代わりに「n」を書くだけ
特殊記号 # の活用
「今日から〇日後」の予定
# を使うことで現在日を基準にして、#の後の数分だけ進んだ日付で予定を作成できます
1.「#」を書く
2.「#」の後ろに「数字」を書く
① #3#t12
② #3t1200
※「i」も同様の操作ができます
特殊記号 i の活用
「現在時刻から〇日後」の予定
i を使うことで現在時刻からの予定を作成できます
1.「i」を書く
2.「i」の後ろに「数字」を書く
特殊記号 ## の活用
「週末(土曜日)」の予定
## を使うことで現在日から〇週末の予定を作成できます
1.「##」を書く
2.「##」の後ろに「数字」を書く
「(数字後の)週末」の予定が作成されます特殊記号 ### の活用
「月末」の予定
### を使うことで現在日から〇月末の予定を作成できます
1.「###」を書く
2.「###」の後ろに「数字」を書く
「(数字後の)月末」の予定が作成されます付加記号 +- の活用
+- を「##」や「###」と組み合わせて使うことで完全に自由な相対日時を設定できます
例えば
「##0-1」とすれば「今週の金曜」の予定
「###1+5」とすれば「来月5日」の予定
「###000+1##0+1t1200」
こんな風に書けば「再々来月最初の日曜日」というこみいった注文も対応できます。
※実際に使うかは謎
「###(-1)+12」
さらにこのように()で囲めば負の数も指定できます
※「###0」が月末なので「###(-1)」は先月末を示しています
※つまりこれは「今月12日」ってこと。+の値を増やせば以外に普通に使い道ある
便利記号 m の活用
m は今月であることを示します
「###(-1)+」と同じ意味
今月の予定を速攻で立てたいときに便利
便利記号 月~日 の活用
月, 火, 水, 木, 金, 土, 日 は直近のその曜日を示します
「##数-曜日インデックス」とほとんど同じ意味
指定曜日の予定を速攻で立てたいときに便利
その他のプロパティ
※()内はプロパティの構造名
- プロパティ名
- 内容
- ✓ (チェックボックス)
- 完了したら✓をつけることで予定をアーカイブできます
- 内容 (タイトル)
- 予定の内容を書き込めます
- input (テキスト)
- 文字列を予定日-Cに渡して日付に変換してもらいます
- 分類 (セレクト)
- 予定を好きなカテゴリで分類できます
- remind (マルチセレクト)
- Googleカレンダーのリマインドを追加できます
- See (formula.text)
- 予定日、予定期間、予定までの期間 が表示されます
- 同期ボタン (ボタン)
- 同期プロパティにCを追加し、Recentプロパティにtrueを付与します
- ID (ID)
- 一意のIDは同期時の重複作成を防ぎ、上書き保存を可能とします
- SyncState (formula-text)
- 同期情報を見やすいデザインで提供します
- 同期 (テキスト)
- Googleカレンダーから同期情報が送られます
- 予定日-C (formula.date)
- 予定日をUTC形式にフォーマットします。
これにより、Notion上でもカレンダービューで予定を確認できるようになる - 作成日 (作成日時)
- 更新 (最終更新日時)
- Recent (formula.boolean)
- 同期待機状態を監視
- 🍐 (テキスト)
- 1列看板を作成するためのダミープロパティ