time-slot-generator について
スケジュール戦略から予約可能な時間枠を生成する TypeScript スキルです。時間枠ロジックを独自サービスに組み込むチーム向けで、決定的な ID、タイムゾーン処理、一覧表示と予約で共通の空き状況確認を重視します。
主な機能
- 01ルールに基づく時間枠生成
- 02決定的な時間枠 ID
- 03タイムゾーンと DST の処理
- 04予約やブロックを反映した空き状況
- 05予約時の定員再確認
活用シーン
- 01定期または単発の予約枠を作成
- 02定員を反映した空き状況を表示
- 03同時予約による定員超過を防止
- 04時間枠ロジックのタイムゾーン問題を修正
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ timerise-ai/ time-slot-generator --skill time-slot-generatorインストール後、Claude Code で/time-slot-generatorを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.md.gitattributesCLAUDE.mdLICENSEREADME.md.github/workflows/agent-eval.ymlevals/prompts.mdreferences/firestore-backend.mdadaptation.mdavailability.mdgenerators.mdprovenance.mdtimezones.mdnextjs-routes.mdstrategy-model.mdtesting.mdsupabase-backend.md詳細
時間枠ジェネレーター
時間枠は保存されません。戦略を保存し、読み出すたびにそこから時間枠を再生成します。予約時には、決定論的な ID を使って該当する時間枠を再特定します。このスキルの仕組みはすべて、この考え方に基づいています。ジェネレーターは戦略、現在時刻、表示範囲を入力とする純粋関数であり、その出力は表示範囲、サーバーのタイムゾーン、年の何日目かに左右されません。そのため、ゲストが「予約」を押したとき、画面に表示されていた時間枠は引き続き存在します。
使用する場面
- ルールから予約可能な時間枠を生成する場合:「月曜日から金曜日まで、09:00~17:00、30 分ごと」、「毎月第 1 金曜日と最終金曜日の18:00~20:00」、「終日、週末のみ」、「特定の土曜日、10:00~14:00」。
- 空き状況を表示する場合:時間枠ごとの定員から、予約、予約不可時間、ホストの他の予定、共有ルームや機器、営業時間外を差し引きます。
- 同時リクエスト時にも定員を再確認したうえで、ID によって時間枠を予約する場合。
- タイムゾーンに関するバグのある時間枠コードを修正する場合や、壁時計の時刻を UTC として保存する方式から移行する場合。
使用しない場面
- 予約画面やキオスクのフロー:このスキルが生成する時間枠を利用する
booking-kiosk。 - カレンダー同期とメール機能を備えた、ホストごとの公開スケジュールリンク:
calendar-scheduling-links。 - カレンダーの繰り返し予定(RRULE、iCalendar、Google または Outlook のイベントシリーズ):
rruleまたはプロバイダーの API。戦略が表すのは予約可能な定員であり、イベントではありません。 - 支払い、デポジット、請求書:
bookable-events、invoicing。
アーキテクチャ
strategies (DB) --normalizeStrategy--> SlotStrategy[] --generateSlots(now, viewport)--> GeneratedSlot[]
|
bookings, busy blocks, host bookings, resources, working hours --SlotStore--> buildAvailability
|
GET /api/slots listServiceSlots: toSlotOutput (UTC + local offset) <-----------------+
POST /api/bookings prepareBookingSlots(ids): one generation, then lookups <--------------+
|
v
book_slots / assertCapacityInTransaction: seats re-counted in the write
エンジン(lib/slots/)は Next.js をインポートしない、プレーンな TypeScript です。フレームワークに触れるのはルートのファイルだけで、データベースに触れるのは SlotStore だけです。
適応に関する契約
adaptation.md に契約全体、名称の対応表、作業順序、必須事項が記載されています。
| 接続箇所 | このスキルが提供するもの | ホストが用意するもの |
|---|---|---|
| ドメイン用語 | Service、Strategy、Booking、Host、Location、Resource、BusyBlock、project | ホスト側の名称 |
| テナントと認証 | 保存済みサービスから読み取る projectId、マーク付きのガード用スロット | 書き込み前のガード |
| データアクセス | Firestore と Supabase 向けの SlotStore | プロジェクトとスキーマ |
| ルート | Supabase 向けの一覧取得および予約ルートハンドラー | URL 方式 |
| 時間枠 ID | node:crypto による md5 | Edge ランタイム向けの純粋な md5 実装 |
| 文字列と UI | 画面はなく、エラーコードのみ | i18n と時間枠ピッカー |
重要事項
- 時点は UTC で保存し、壁時計の時刻を記述し、タイムゾーンを固定します。
start.time: "09:00"は、作成時に固定された戦略のtimeZoneにおける09:00を意味します。そのため、後からプロジェクトのタイムゾーンを変更しても、既存の時間枠は移動しません。 - ローカルの暦日を順に処理し、決して
+24hを使わないでください。 DST の日は23時間または25時間です。春の夏時間への切り替え日の前夜、23:30に24時間を足すと、ローカル時刻では2日後になります。 - 早期終了する前に、時系列順に出力してください。 ジェネレーターはカットオフを過ぎた最初の時間枠で終了するため、曜日ごとに候補を生成すると、週表示が最初の平日で終わってしまいます。
- 上限は表示範囲ではなく、戦略の開始時点から数えます。 そうしないと、「最初の10枠」はクライアントがどの週を要求するかによって変わり、ある表示では見える時間枠が予約できなくなります。
- サーバーのタイムゾーンは関係ありません。
new Date('2026-03-29T09:00')はプロセスのタイムゾーンで解析されます。また、時刻をずらした Date に対するgetUTCDay()が正しいのは、プロセスが UTC の場合だけです。そのため、エンジンでは Intl ヘルパーを使います。 - 予約ごとに一度だけ生成し、その後は検索します。 要求された時間枠ごとに再生成すると、予約内の時間枠数に応じて処理量が増えます。
- 書き込み時には生成結果が古くなっています。 2人のゲストが最後の1席を同時に見ている場合があるため、予約トランザクション内で席数を再集計します。
厳守事項
- スロット ID の入力を絶対に変更しないでください。
md5("<utcStartISO>;<duration>;<strategyId>")は、すべてのクライアントと予約が保持する識別情報です。入力が異なると、すべてのクライアントと予約が紐付かなくなります。 - 終了時刻が開始時刻より前だからといって、「夜間」と推測しないでください。 時刻の逆転は夜勤よりも入力ミスであることがはるかに多いため、夜間の時間帯には
overnight: trueが必要です。 - 期間を指定せずに生成しないでください。
maxHorizonDaysとmaxSlotsですべてのストラテジーに上限を設けてください。そうしないと、終了条件のないストラテジーは 2038 年まで生成し続けます。 - 一覧表示のときだけ空き状況でフィルタリングしないでください。 一覧表示と予約処理は同じ
buildAvailabilityマップを参照するため、一覧に表示されない ID は予約できません。 - ホストがこのサービスを予約した分を、ホストの予約済み時間として数えないでください。 その予約はすでに
BOOKINGとして座席数から差し引かれています。さらに予約済みとしてブロックすると、まだ空きがあるスロットが非表示になります。
クイックスタート
- ホストを確認し、接合部の契約と名前変更テーブルを記入します。adaptation.md を参照してください。
- 型とバリデーションをコピーします。strategy-model.md を参照してください。
- 時刻ヘルパー、期間、正規化処理をコピーします。timezones.md を参照してください。
- スロットの識別方法とジェネレーターをコピーします。generators.md を参照してください。
- 空き状況の処理とサービスのエントリーポイントをコピーします。availability.md を参照してください。
- テストスイートをコピーし、4 つの
TZ値でnpm testに接続して実行します。 testing.md を参照してください。 - バックエンドを選択します。supabase-backend.md または firestore-backend.md を参照してください。
- ルートハンドラーから提供します。nextjs-routes.md を参照してください。
- 既存のエンジンを再構築しますか?provenance.md に台帳と修正の順序が、 testing.md に差分チェックが記載されています。
引き継ぎ時には、適用するスキーマまたはインデックス、2 つの環境変数、ホストのガードを配置する場所を伝えてください。
リファレンスディレクトリ
| シナリオ | トリガーキーワード | リファレンス |
|---|---|---|
| 接合部、名前変更、作業順序 | adapt, host, rename, tenant, auth guard, non-negotiables | adaptation.md |
| ストラテジーの形式、フィールド、バリデーション | RANGE, PERIODICITY, ONCE, DAY_RANGE, tendency, timeOffset, slotsLimit, overnight, strategy-bounds-invalid | strategy-model.md |
| タイムゾーンと DST | IANA, wall clock, UTC, DST gap, overlap, fromZonedTime, offset, storageVersion, legacy | timezones.md |
| スロットの生成 | generateSlots, horizon, maxSlots, truncated, slot ID, md5, chronological, viewport | generators.md |
| まだ空いている枠 | availability, seats, BOOKING, BLOCK, working hours, resource, capacity, overbooking, prepareBookingSlots | availability.md |
| Supabase と Postgres | timestamptz, jsonb, RLS, with check, gin index, book_slots, advisory lock | supabase-backend.md |
| Firestore | firebase-admin, Timestamp, composite index, array-contains-any, onSnapshot, runTransaction | firestore-backend.md |
| ルートハンドラー | app/api/slots, app/api/bookings, route.ts, Node.js runtime, 409, SUPABASE_SERVICE_ROLE_KEY | nextjs-routes.md |
| 動作の検証 | node:test, vitest, npm test, TZ=, DST regression, Lord Howe, Auckland, differential check | testing.md |
| この設計を採用した理由 | provenance, audit, kept deliberately, fix order | provenance.md |
兄弟スキルを一覧できる Timerise Skills インデックスの一部です。
コメント
Markdown 対応