イベント、繰り返し、時刻の扱い
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 を返します。
- 構文エラー
- 不正な値や組み合わせ、行数の超過
対応しているルール要素:
| ルール要素 | 指定できる値 |
|---|---|
FREQ | DAILY、WEEKLY、MONTHLY、YEARLY |
INTERVAL | 1〜10000 |
COUNT | 1〜10000。COUNT と UNTIL は同時に指定できません。 |
UNTIL | 時刻指定の UNTIL は UTC(...Z)で指定します。終日の UNTIL は日付形式です。 |
BYDAY | MO〜SU。MONTHLY・YEARLY では ±1〜5 も指定できます。 |
BYMONTHDAY | ±1〜31 |
BYMONTH | 1〜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 を返します。対象は次のとおりです。
instancessingleEvents=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の設定) reminderscolorIdtransparencyextendedProperties
招待された側の編集による影響:
- 出欠回答の変更は、主催者のイベントと他の参加者のコピーにも反映されます。
- 繰り返しの系列の 1 つのインスタンスに出欠回答すると、主催者のカレンダーに例外が作成されます。
- 招待を辞退(
declined)すると、招待された側のコピーはtransparentになります。それ以外のattendeesの変更ではopaqueに戻ります。 - コピー側で変更した
colorIdとtransparencyは、次回主催者がイベントを更新したときに上書きされます。
ゲスト権限フラグ
| フラグ | デフォルト |
|---|---|
guestsCanModify | false |
guestsCanInviteOthers | true |
guestsCanSeeOtherGuests | true |
- 真偽値として保存と取得ができます。
- 変更できるのは主催者だけです。
- 値が保存されるだけで、招待されたユーザーが編集できる範囲には影響しません。
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: [] を返します。