Calendar API

イベント、繰り返し、時刻の扱い

Calendarpit における繰り返しイベントの展開、招待や会議のローカルシミュレーション、タイムゾーン、夏時間(DST)、busy 区間の扱いについて説明します。

Calendarpit は、次の処理をすべてローカルで行います。

  • 繰り返しの系列の展開
  • ローカルのテストユーザー間での招待の同期
  • 時間範囲と busy 区間の計算

対応しているエンドポイントとフィールドの一覧は、Calendar API の互換性を参照してください。

繰り返しイベント

繰り返しイベントは、系列のまま取得することも、各回に展開して取得することもできます。

events.list では、singleEvents で返し方が変わります。

  • singleEvents=false(デフォルト): 繰り返しの親イベント(マスター)と、保存済みの例外(個別に変更された回)を返します。
  • singleEvents=true: 各回のイベントを展開して返します。

instances エンドポイント

GET /calendar/v3/calendars/{calendarId}/events/{eventId}/instances は、単一の繰り返しの系列を展開した各回のインスタンスを常に返します。

  • singleEvents=false の場合、showDeleted を指定しなくてもキャンセル済みの例外が含まれます。
  • events.list のパラメータに加えて originalStart を受け付けます。timeMin、timeMax、showDeleted、maxResults、pageToken、timeZone などが使えます。
  • syncToken を指定するとエラーになります。
  • レスポンスに nextSyncToken は含まれません。

インスタンス ID と例外

各インスタンスには recurringEventId、originalStartTime、再現可能な ID が付与されます。

  • 時刻指定イベント: UTC の <seriesId>_<YYYYMMDDTHHMMSSZ>
  • 終日イベント: <seriesId>_<YYYYMMDD>

/events/{instanceId} では、各メソッドが単一の回に対して動作します。

メソッド動作
GET例外が保存されていなくても、該当する回のインスタンスを返します。
PUT, PATCHその回を例外として保存します。
DELETEキャンセル済みの例外として保存します。

親イベントへの変更は、各回にも影響します。

  • 親イベントを更新すると、各インスタンスも更新されます。新しいルールで生成されなくなった保存済みの例外はキャンセルされます。
  • 親イベントを削除すると、その例外もすべてキャンセルされます。

展開ロジックは他の機能にも適用されます。

  • singleEvents=true の場合、差分同期では存在しなくなった回が cancelled として返されます。
  • freebusy.query でも同じ展開ロジックが使われます。

ルールと検証

recurrence には RRULE、EXRULE、RDATE、EXDATE の行を 1〜20 行指定できます。空配列を指定すると、繰り返しなしの通常イベントとして保存されます。

次の場合は 400 unsupportedFeature を返します。

  • これ以外の行(DTSTART など)
  • FREQ の欠落
  • 日単位未満の FREQ(HOURLY、MINUTELY、SECONDLY)
  • 下表にない大文字のルール要素(BYHOUR など)
  • ルール要素の重複

次の場合は 400 invalidParameter を返します。

  • 構文エラー
  • 不正な値や組み合わせ、行数の超過

対応しているルール要素:

ルール要素指定できる値
FREQDAILY、WEEKLY、MONTHLY、YEARLY
INTERVAL1〜10000
COUNT1〜10000。COUNT と UNTIL は同時に指定できません。
UNTIL時刻指定の UNTIL は UTC(...Z)で指定します。終日の UNTIL は日付形式です。
BYDAYMO〜SU。MONTHLY・YEARLY では ±1〜5 も指定できます。
BYMONTHDAY±1〜31
BYMONTH1〜12
BYSETPOS±1〜366
WKST対応

タイムゾーンに関する要件:

  • 時刻指定の系列: start.timeZone が必須です。
  • RDATE と EXDATE: 時刻指定の系列では ;TZID=<zone> を指定できます。終日の系列では ;VALUE=DATE が必須です。

展開の上限

展開には 2 つの上限があります。いずれかを超えると 400 unsupportedFeature を返します。

  • インスタンス数: 1 回の展開につき最大 10,000 インスタンスです。リクエストの期間内、または timeMax がない場合は系列全体が対象です。
  • 期間: DTSTART から最大 100 年(366×100 日)までです。timeMax なしで展開する終了条件のある系列と、timeMin・timeMax の値が対象です。

終了条件のない系列(COUNT・UNTIL なし)では、次の場合に timeMax が必須です。

  • 展開する場合(singleEvents=true)
  • events.list に timeMin を渡す場合

1 つでも上限を超える系列があると、一覧取得や freeBusy のリクエスト全体が失敗します。削除済みの系列も対象に含まれます。timeMax を指定するか、状態をリセットしてください。

夏時間(DST)

ルールは start.timeZone の現地時刻を維持します。

存在しない現地時刻(夏時間開始時のスキップ)の場合、展開処理がその回に到達した時点で 400 unsupportedFeature を返します。対象は次のとおりです。

  • instances
  • singleEvents=true または timeMin/timeMax を指定した events.list
  • スキップ前後約 2 日以内の回のインスタンス ID 指定
  • freeBusy

イベントの作成自体は成功します。エラーはリクエスト全体を失敗させます。

曖昧な現地時刻(夏時間終了時の重複)には、移行前の早い方のオフセットを使います。たとえばニューヨークでは -04:00 です。

招待、リマインダー、ローカルでの会議データ

Calendarpit が実際にメールを送信したり、Google Meet と通信したりすることはありません。

招待

attendees には、重複のないメールアドレスを最大 200 件まで指定できます。各参加者には displayName、optional、resource、additionalGuests、comment、responseStatus を指定できます。responseStatus は needsAction、accepted、tentative、declined のいずれかです。

プライマリカレンダーを持つローカルのテストユーザーを招待すると、そのカレンダーにイベントのコピーが書き込まれます。コピーは同じ id と iCalUID を持ちます。

  • 主催者による更新やキャンセルは各コピーに反映され、watch 通知が発行されます。
  • 参加者を削除すると、そのユーザーのコピーはキャンセルされます。
  • reminders と extendedProperties.private は、コピーごとに独立して保持されます。
  • extendedProperties.shared は主催者のイベントから同期されます。

出欠回答(RSVP)と招待された側の編集

招待されたユーザーは PATCH を使う必要があります。PUT は 403 forbiddenForNonOrganizer を返します。

招待されたユーザーが編集できるのは次の項目だけです。

  • 自身の responseStatus と comment(または、自身のエントリのみを含めた attendeesOmitted: true の設定)
  • reminders
  • colorId
  • transparency
  • extendedProperties

招待された側の編集による影響:

  • 出欠回答の変更は、主催者のイベントと他の参加者のコピーにも反映されます。
  • 繰り返しの系列の 1 つのインスタンスに出欠回答すると、主催者のカレンダーに例外が作成されます。
  • 招待を辞退(declined)すると、招待された側のコピーは transparent になります。それ以外の attendees の変更では opaque に戻ります。
  • コピー側で変更した colorId と transparency は、次回主催者がイベントを更新したときに上書きされます。

ゲスト権限フラグ

フラグデフォルト
guestsCanModifyfalse
guestsCanInviteOtherstrue
guestsCanSeeOtherGueststrue
  • 真偽値として保存と取得ができます。
  • 変更できるのは主催者だけです。
  • 値が保存されるだけで、招待されたユーザーが編集できる範囲には影響しません。

sendUpdates

値記録される内容
all書き込みごとに 1 件記録されます。recipients は主催者を除く全参加者で、削除された参加者も含みます。招待されたユーザーによる REPLY の宛先は主催者です。
externalOnlyローカルのテストユーザーではない宛先のみが記録されます。
none(デフォルト)何も記録されません。
  • 記録されたレコード(REQUEST、CANCEL、REPLY)は、GET /__calendarpit/state の notifications で確認できます。保持されるのは最新 200 件です。テスト制御 APIを参照してください。
  • sendUpdates の値にかかわらず、ローカルのコピー間の同期は行われます。

リマインダー

  • 形式: { useDefault: boolean, overrides?: [{ method: "email" | "popup", minutes: 0..40320 }] }
  • 上限: useDefault: false の場合、overrides は最大 5 件までです。
  • 動作: データは保存されるだけで、リマインダーが通知されることはありません。

会議データ(conferenceData)

  • null 以外の conferenceData を扱うには、?conferenceDataVersion=1 の指定が必要です。
  • hangoutsMeet の createRequest.requestId を指定すると、再現可能な ID、hangoutLink、https://meet.calendarpit.invalid/<id> を指す video エントリポイントが生成されます。
  • conferenceData: null を指定すると、会議データが消去されます。

時刻の扱いと FreeBusy

イベントの期間は半開区間 [start, end) として扱われます。end は start より後の時刻である必要があります。

一覧取得のフィルタは start < timeMax かつ end > timeMin で判定します。そのため、前後に隣接するイベント同士は重複しません。

終日イベント

  • start.date と、終了日(その当日は含まない)を表す end.date は YYYY-MM-DD 形式です。
  • カレンダーのタイムゾーンの午前 0 時を基準にします。
  • 夏時間の切り替えで 1 日が 23 時間や 25 時間になる日も同様です。

時刻指定イベント

  • dateTime には UTC オフセット(Z を含む)または IANA の timeZone が必須です。
  • オフセットのない現地時刻が夏時間のスキップ期間または重複期間に該当する場合は 400 を返します。
  • レスポンスの dateTime は、リクエストで指定した timeZone(未指定時はカレンダーのタイムゾーン)の数値オフセット付きでフォーマットされます。
  • レスポンスの timeZone フィールドも、リクエストで指定したタイムゾーン(またはカレンダーのタイムゾーン)になります。イベントに保存されたタイムゾーンではありません。

FreeBusy

POST /calendar/v3/freeBusy は最大 50 個のカレンダーを照会します。timeZone(デフォルトは UTC)でフォーマットされた busy 区間を返します。

busy 区間の計算では、次の処理を行います。

  • 繰り返しの系列を展開します。
  • transparent(予定なしとして扱うイベント。辞退した招待を含む)のイベントを除外します。
  • cancelled(削除済み)のイベントを除外します。
  • 重複または隣接する区間を結合します。
  • [timeMin, timeMax) の範囲に切り詰めます。

存在しないカレンダーや所有していないカレンダーには、カレンダーごとの notFound エラーと busy: [] を返します。

目次