0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

カレンダーの繰り返し予約のRRULEを学ぶ

Posted at

はじめに

今回はカレンダーの繰り返し予約のRRULEを学んでいきます。カレンダーで、「このMTGは8月まで毎日10時〜11時に設定する」などで利用するあれです!繰り返し予約は全部DBに保存しておくのではなく、ルールだけ保存しておいてフロント側で展開しています。

スクリーンショット 2025-05-28 9.15.34.png

そもそもRRULEとは何か?

カレンダーアプリで「毎週の会議」や「毎月の締め切り」のような繰り返し予定を扱いたいときに使われるのが RRULE(Recurrence Rule)です。

これはRFC 5545(iCalendar仕様)で定義されている、繰り返しルールを表すフォーマットです。

image.png

基本構文

RRULE(繰り返しルール)は、カレンダーイベントの繰り返しを定義する文字列で、以下のような形式で記述します。

RRULE:FREQ=FREQUENCY;INTERVAL=n;COUNT=x;UNTIL=yyyyMMddTHHmmssZ;BYxxx=…

パラメータ 説明
FREQ 繰り返しの頻度(必須) FREQ=WEEKLY → 毎週
INTERVAL 繰り返しの間隔。省略時は1 INTERVAL=2 → 2週ごと
COUNT 繰り返す回数 COUNT=10 → 10回繰り返して終了
UNTIL 繰り返しの終了日時(UTC形式) UNTIL=20251231T235959Z
BYDAY 曜日を指定(MO=月曜、TU=火曜…) BYDAY=MO,WE → 月・水曜に繰り返す
BYMONTHDAY 月内の日付を指定 BYMONTHDAY=15 → 毎月15日に実行
BYMONTH 月を指定 BYMONTH=1,7 → 1月と7月のみ実行
EXDATE 繰り返しから除外する日付(UTC形式でカンマ区切り) EXDATE=20240510T000000Z → 5/10を除外

実践

react-big-calendarで実践してみましょう!

環境作成
ディレクトリ構成
~/develop/rrule-big-calendar-demo  (main)$ tree src/
src/
├── App.css
├── App.tsx
├── assets
│   └── react.svg
├── index.css
├── lib
│   ├── localizer.ts
│   └── rruleHelpers.ts
├── main.tsx
└── vite-env.d.ts

3 directories, 8 files
src/App.tsx
import { useMemo, useState } from "react";
import { Calendar } from "react-big-calendar";
import "react-big-calendar/lib/css/react-big-calendar.css";
import { rruleToEvents } from "./lib/rruleHelpers";
import { localizer } from "./lib/localizer";

// rruleパターン生成関数
function getRRulePattern(): string {
	// JST 2024年5月1日 9:00 → UTC 2024年5月1日 0:00
	const dtstart = "20240501T000000Z";
	return `DTSTART:${dtstart}\nRRULE:FREQ=DAILY;COUNT=365`;
}

function App() {
	const [date, setDate] = useState(new Date("2024-05-01T00:00:00Z"));

	const rrule = getRRulePattern();
	console.log("rrule", rrule);

	// 2時間(120分)のイベント
	const events = useMemo(
		() => rruleToEvents(rrule, 120, "毎日のイベント"),
		[rrule],
	);

	return (
		<div style={{ height: "100vh", width: "100vw", margin: 0, padding: 0 }}>
			<Calendar
				localizer={localizer}
				events={events}
				defaultView="week"
				date={date}
				onNavigate={setDate}
				style={{ height: "100%", width: "100%" }}
			/>
		</div>
	);
}

export default App;

src/lib/localizer.ts
import { format, parse, startOfWeek, getDay } from "date-fns";
import { ja } from "date-fns/locale";
import { dateFnsLocalizer } from "react-big-calendar";

export const localizer = dateFnsLocalizer({
	format,
	parse,
	startOfWeek,
	getDay,
	locales: { ja },
});
src/lib/rruleHelpers.ts
import { RRule } from "rrule";
import type { Event } from "react-big-calendar";

/**
 * RRULE 文字列を react-big-calendar の events[] に展開
 */
export function rruleToEvents(
	rruleString: string,
	durationMinutes: number,
	title: string,
): Event[] {
	// RRULE文字列をパース
	const rrule = RRule.fromString(rruleString);

	// 繰り返しの日付を生成
	const dates = rrule.all();

	// イベントの配列を生成
	return dates.map((date, index) => ({
		id: index,
		title,
		start: date,
		end: new Date(date.getTime() + durationMinutes * 60000),
	}));
}

では実践していきます!!

パターン1:平日のみ毎日イベント

DTSTART:20240429T000000Z RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;COUNT=260

// rruleパターン生成関数
function getRRulePattern(): string {
	// JST 2024年4月29日 9:00 → UTC 2024年4月29日 0:00
	const dtstart = "20240429T000000Z";
	return `DTSTART:${dtstart}\nRRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;COUNT=260`;
}

image.png

→土曜と日曜が表示されなくなります。

パターン2:毎月第1月曜に定例会議を表示

// rruleパターン生成関数
function getRRulePattern(): string {
	// 2024年5月6日(月)9:00 JST → 0:00 UTC
	const dtstart = "20240506T000000Z";
	return `DTSTART:${dtstart}\nRRULE:FREQ=MONTHLY;BYDAY=1MO;COUNT=12`;
}

month kurikaeshi.gif

→余談ですが、COUNT=12をつけないと白飛びします。原因は無限に繰り返そうとして、メモリを食い切ることが原因のようです。繰り返しの範囲を指定する方法もあるんですが、一旦置いておきましょう。

パターン3:期間限定の毎週水曜キャンペーン(6月〜8月)

// rruleパターン生成関数(夏のキャンペーン:毎週水曜)
function getRRulePattern(): string {
	// JST 2024年6月5日(水)9:00 → UTC 0:00
	const dtstart = "20240605T000000Z";
	const until = "20240828T000000Z";
	return `DTSTART:${dtstart}\nRRULE:FREQ=WEEKLY;BYDAY=WE;UNTIL=${until}`;
}

month_maishu.gif

パターン4:特定の日だけ繰り返しを除外する

DTSTART:20240605T000000ZRRULE:FREQ=WEEKLY;BYDAY=WE;UNTIL=20240828T000000ZEXDATE:20240612T000000Z

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?