スマホのカレンダーに、繰り返しの予定が入らなかった——.ics 取り込みの切り分けと、UID から学んだこと

Kairos

発端——公開した日の夜

目覚ましの例(平日は 6 時半、土曜と祝日は 8 時)は、Playground の「カレンダーに入れる」ボタンで .ics ファイルになり、そのままカレンダーに取り込める。この機能を 1.0.7 として公開したのが 10 月 8 日の 0 時過ぎ。その夜のうちに、実機で確かめた。

PC のブラウザで開いた Google カレンダーに取り込むと、12 回分が 1 つの繰り返し予定として入った。削除も「この予定/すべての予定」の一度で済む。ここまでは設計どおりである。

同じファイルを Android の Google カレンダーアプリで開くと、初回の 1 件(10 月 5 日)だけが入った。残りの 11 回は出てこない。

そして、その後が本題だった。同じファイルを PC に取り込み直すと、今度は PC でも入らない。画面にはこう出る。

0 件の予定のうち 0 件をインポートしました。iCal/CSV ファイルを処理できません

1 時間前には入ったファイルである。

仮説を 4 回外す

ここからは切り分けの記録である。原則は 1 つ、1 回に 1 変数しか変えない。

仮説 1: 行の折り返しがエスケープを割っている。 .ics は 1 行 75 オクテットで折り返す決まりがあり、\n のような 2 文字のエスケープが行末と行頭に分かれる形はあり得る。読み手によっては嫌うかもしれない。ところが、12 件が「1 点ずつ別の予定」として入ったファイルと、入らなかったファイルで、折り返しの位置も定義文の行も同一だった。折り返しは原因ではない。

仮説 2: 同じ UID の予定が既にあるから捨てられた。 予定には UID という識別子があり、同じ UID の予定を二度入れると取り込み側は重複として扱う。そこで UID だけを書き換えた同一ファイルを作って PC に取り込むと、「1 件の予定のうち 1 件をインポートしました」で全回が入った。UID が関わっていることは確定した。しかし「同じカレンダーに同じ UID を取り込み直す」こと自体は、別の試験で通った。重複判定、では説明が足りない。

仮説 3: スマホと PC で別のカレンダーに入れていた。 確認したら同じカレンダーだった。

仮説 4: 削除した予定の墓標が残っている。 ゴミ箱を空にしても変わらなかった。

ここで試験の組み方を変えた。順序を変数にする。

  • UID だけ新しくしたファイルを、先にスマホで取り込み、削除し、それから PC で取り込む。結果は「0 件」。再現した。
  • PC で入れた予定を削除し、PC で取り込み直す。結果は「1 件」。入る。
  • 同じ UID のまま、期間を 2 週間から 4 週間に延ばしたファイルを PC で取り込み直す。結果は「1 件」。 後半の回が追加され、前半は重複しない。

確定した事実

10 月 8 日の観測で確定したことは 4 つある。

  • PC の Google カレンダー(ブラウザ)では、1 つの予定に RDATE で日付を列挙した形は、1 つの繰り返し予定として 全回入る。同じ UID の取り込み直しは「更新」で、削除後の取り込み直しも通る。
  • Android の Google カレンダーアプリは RDATE を読まない。初回の 1 件が、繰り返しでない単発の予定として登録される。
  • その単発の予定が一度でも作られると、削除してもゴミ箱を空にしても、同じ UID の繰り返し予定を PC からも 取り込めなくなる。
  • 「1 点ずつ別の予定にする」形(12 回なら 12 件の予定・UID はすべて別)は、PC でもスマホでも全件入る。
図 1: 同じ .ics を取り込んだ結果(2026 年 10 月 8 日の観測)。1 つの繰り返し予定は PC のブラウザでは 12 回入るが、Android のアプリでは初回の 1 件だけ。1 点ずつの形はどちらでも 12 件入る。
図 1: 同じ .ics を取り込んだ結果(2026 年 10 月 8 日の観測)。1 つの繰り返し予定は PC のブラウザでは 12 回入るが、Android のアプリでは初回の 1 件だけ。1 点ずつの形はどちらでも 12 件入る。

Kairos の .ics の UID は定義から決まる。同じ定義なら、いつ誰が生成しても同じ UID になるように作ってある(再取り込みで重複しないための設計である)。だから 3 つ目の事実は、こう言い換えられる。その定義は、スマホで一度「繰り返し予定」として取り込んだ時点で、PC からは入らなくなる。

読み手ごとに RDATE の扱いが違う、というところまでは想定していた。Outlook 系がそうであることは公開前に知っていて、「1 点ずつ別の予定にする」のチェックを用意していた。想定していなかったのは、片方の読み手が作った単発の予定が、もう片方の読み手の判断を変えることである。

1.0.8 で変えたこと

同じ日のうちに 1.0.8 を出した。言語は変わっていない。変えたのは出口の既定と文書である。

  • Playground の「1 点ずつ別の予定にする」は、スマホ(タッチ主体のポインタ、または幅 700px 未満)では最初からオン。 PC では従来どおりオフ(1 つの繰り返し予定)。チェックの文言にもそう書いた。
  • 行の折り返しがエスケープ列を割らないようにした。今回の原因ではなかったが、読み手の堅牢性のために同乗させた。
  • 「再生成の取り込みで丸ごと置き換わる」と書いていた文書を、「同じ UID の取り込み直しは更新」に改めた。 公開済みの 1.0.7 の Release 本文も同じ文に直してある。

詳細は Release v1.0.8 にある。

UID は「定義をまとめる鍵」だった

この一件で得たものを、1 つだけ挙げるなら UID である。

iCalendar には RRULE という「規則」を書く欄があるが、Kairos はそれを使わない。書けないスケジュールの博物誌で見たとおり、RRULE に書けない形が存在理由だからで、近似の規則を出すくらいなら日付を展開し切って渡す。その代わり、展開した日付の束に「これは 1 つの定義から出た」という印を付ける必要がある。それが UID だった。

図 2: 同じ 12 回を書く 2 つの形。左は 1 つの繰り返し予定(UID は 1 つ・残りの 11 回は RDATE)、右は 1 点ずつ 12 件の予定(UID は 12 個)。右の UID は左の「-series@」を点の時刻に置き換えた形。
図 2: 同じ 12 回を書く 2 つの形。左は 1 つの繰り返し予定(UID は 1 つ・残りの 11 回は RDATE)、右は 1 点ずつ 12 件の予定(UID は 12 個)。右の UID は左の「-series@」を点の時刻に置き換えた形。

UID が定義から決定的に決まるおかげで、

  • 同じ定義を取り込み直しても重複しない(PC の Web で実測した「更新」)
  • 期間を延ばして再生成すれば、新しい回だけが足される
  • 削除は「すべての予定」の 1 回で済む

という性質が、規則を渡さずに手に入る。規則の解釈を読み手に委ねないから、どの読み手でも同じ日付が出る。そして今回分かったのは、この鍵が読み手の都合で封じられることがある、ということだった。

裏返せば、Kairos が手に入れたのは、展開した点の一つひとつが「自分はどの定義から出たか」に答えられる手段である。点の集合を渡す言語にとって、これは規則を渡す言語の RRULE に相当する、唯一の「族」の印になる。

次——鍵を JSON にも出す

ならば、鍵を .ics の中だけに閉じ込めておく理由はない。

Kairos の CLI には --json があり、評価結果を機械可読で返す。次の版(1.0.9)では、この JSON の各式に uid を付ける。値は .ics の繰り返し予定の UID と一字違わず同じにする。

"results": [{
  "source": "(bizDay |> at(T06:30)) | (lateDay |> at(T08:00))",
  "uid": "kairos-18e98d5bc4d5-series@kairos-lang.org",
  "points": [1791149400000, 1791235800000, ...]
}]

.ics 側の繰り返し予定の UID はこれと同じ kairos-18e98d5bc4d5-series@kairos-lang.org で、1 点ずつの形ならkairos-18e98d5bc4d5-1791149400000@kairos-lang.org のように -series@ が点の値に置き換わる(上の目覚ましの定義を10 月 5 日から 2 週間で評価した実測。12 点)。

カレンダーアプリやスケジューラが Kairos を組み込むなら、.ics を経由せずに、この uid と点の列で自分の予定を作り、更新できる。読み手が RDATE を読むかどうかに依存しない。

ただし、この値は指紋であって、同一性ではない。式の字面、ファイル内の順番、タイムゾーンのどれかを変えれば別の値になる。アプリは自分の記録 id を正とし、uid は「同じ定義かどうか」を見分けるために使う。そこまでを schema と文書に書いて出す。

いま使う人への手引き

  • スマホで取り込むなら「1 点ずつ別の予定にする」のまま(1.0.8 からは最初からオン)。全回入る。
  • PC の Google カレンダーなら、1 つの繰り返し予定の形で入る。期間を延ばした .ics を取り込み直せば、新しい回だけが足される。
  • 同じ定義をスマホで繰り返し予定として入れたことがあるなら、PC からも同じ定義は入らない。 定義の文を 1 文字変えれば別の予定になる (コメント行の追記でもよい)。
  • 取り込み直すときは、先に古い予定を消す必要はない。同じ UID は更新される。

Playground の「暮らしの例」から カレンダーに入れる を押せば、式を読まずにここまで試せる。

切り分けは、設計者が実機でファイルを 1 本ずつ試し、当方が仮説と試験ファイルを出す往復で進めた。4 回外した仮説も、全部ここに書いた。

コメント

タイトルとURLをコピーしました