forceCalendar/Docs
Core Package

Event Model

The Event class -- creation, validation, timezone support, attendees, reminders, and categories.

Overview

The Event class represents a calendar event with full timezone support, attendee management, reminders, and category tagging. Events are created through Event.normalize() which applies defaults and coerces field values.

import { Event } from '@forcecalendar/core';

const event = new Event({
  id: 'team-meeting',
  title: 'Team Meeting',
  start: new Date('2026-03-01T09:00:00'),
  end: new Date('2026-03-01T10:00:00'),
  timeZone: 'America/New_York',
});

Constructor Properties

PropertyTypeDefaultDescription
idstringRequiredUnique identifier (not auto-generated by Event; assign your own)
titlestringRequiredNon-empty event title
startDate | stringRequiredValid start date/time
endDateSame as startEnd date/time
allDaybooleanfalseAll-day event flag
descriptionstring''Event description
locationstring''Event location
colorstringnullPrimary color
backgroundColorstringnullBackground override
borderColorstringnullBorder override
textColorstringnullText color override
recurringbooleanfalseWhether event recurs
recurrenceRulestring|objectnullRRULE string or parsed rule object
recurrencestring|objectnullAlias for recurrenceRule
timeZonestringnullIANA timezone for start time
endTimeZonestringnullIANA timezone for end time (if different)
statusstring'confirmed''confirmed', 'tentative', 'cancelled'
visibilitystring'public''public', 'private', 'confidential'
organizerobjectnull{ email, name }
attendeesArray[]Attendee objects
remindersArray[]Reminder objects
categoriesArray[]Category strings
categorystringnullShorthand for single category (added to categories)
attachmentsArray[]Attachment objects
conferenceDataobjectnullVideo conference data
metadataobject{}Custom key-value metadata

Static Methods

Event.normalize(data)

Normalize raw event data into a complete event object with defaults applied. Ensures start and end are Date objects (defaulting end to start when omitted), trims and length-limits string fields, and applies default values. It does not generate an id -- if none is provided, id defaults to an empty string.

const normalized = Event.normalize({
  title: 'Quick Event',
  start: '2026-03-01T09:00:00',
});
// { id: '', title: 'Quick Event', start: Date, end: Date, ... }

Event.validate(data)

Validate normalized event data. Returns void and throws an Error on failure.

const normalized = Event.normalize({
  id: 'meeting', title: 'Meeting',
  start: new Date(2026, 9, 5, 9), end: new Date(2026, 9, 5, 10),
});
Event.validate(normalized);

IDs, titles and start dates are required. Dates must be valid, and an end cannot precede the start (equal instants are allowed). Recurring events require a rule. Attendees and metadata also have validation/size constraints. Normalization defaults invalid status/visibility to supported values; it does not generate an ID. The constructor normalizes and validates its input.

Event.fromObject(obj)

Create an Event instance from a plain object.

const event = Event.fromObject(savedEventData);

Computed Properties (Getters)

GetterTypeDescription
durationnumberDuration in milliseconds
durationMinutesnumberDuration in minutes
durationHoursnumberDuration in hours (decimal)
isMultiDaybooleanSpans midnight
isCancelledbooleanStatus is 'cancelled'
isTentativebooleanStatus is 'tentative'
isConfirmedbooleanStatus is 'confirmed'
isPrivatebooleanVisibility is 'private'
isPublicbooleanVisibility is 'public'
hasAttendeesbooleanHas at least one attendee
hasRemindersbooleanHas at least one reminder
isMeetingbooleanHas attendees
isVirtualbooleanHas conference data
categorystring|nullPrimary (first) category

Instance Methods

isRecurring()

Returns true if the event has a recurrence rule.

occursOn(date)

Check if the event occurs on a specific date (considering timezone).

event.occursOn(new Date('2026-03-01')); // true

overlaps(otherEvent)

Check if this event's time range overlaps with another event.

const hasConflict = eventA.overlaps(eventB);

contains(datetime)

Check if a specific datetime falls within this event's time range.

clone(updates?)

Create a copy of this event, optionally with modifications.

const copy = event.clone({ title: 'Copy of Meeting' });

toObject()

Serialize to a plain object (for storage or transmission).

const obj = event.toObject();
JSON.stringify(obj);

equals(other)

Deep equality check against another event.

Timezone Methods

getStartInTimezone(timezone)

Get the start time converted to a specific timezone.

const tokyoStart = event.getStartInTimezone('Asia/Tokyo');

getEndInTimezone(timezone)

Get the end time converted to a specific timezone.

updateTimes(start, end, timezone?)

Update start/end times, optionally specifying the source timezone.

Attendee Management

addAttendee(attendee)

event.addAttendee({
  email: 'alice@example.com',
  name: 'Alice',
  status: 'needs-action',  // 'accepted', 'declined', 'tentative', 'needs-action'
  role: 'required',         // 'required', 'optional', 'chair'
});

removeAttendee(email)

event.removeAttendee('alice@example.com');

updateAttendeeResponse(email, status)

event.updateAttendeeResponse('alice@example.com', 'accepted');

getAttendee(email)

Returns the attendee object for the given email, or undefined.

hasAttendee(email)

Returns boolean.

getAttendeesByStatus(status)

const accepted = event.getAttendeesByStatus('accepted');

getAttendeeCounts()

const counts = event.getAttendeeCounts();
// { total: 5, accepted: 3, declined: 1, tentative: 0, 'needs-action': 1 }

Reminder Management

addReminder(reminder)

event.addReminder({
  type: 'email',      // 'email', 'popup', 'sms'
  minutes: 15,        // Minutes before event
});

removeReminder(reminderId)

Remove the reminder whose id matches. Returns true if a reminder was removed.

getActiveReminders()

Returns reminders that haven't triggered yet.

getReminderTriggerTimes()

Returns an array of Date objects representing when each reminder should fire.

const times = event.getReminderTriggerTimes();
// [Date(2026-03-01T08:45:00), Date(2026-03-01T08:00:00)]

Category Management

addCategory(category)

event.addCategory('important');

removeCategory(category)

event.removeCategory('important');

hasCategory(category)

Returns boolean.

hasAnyCategory(categories)

Returns true if the event has at least one of the given categories.

event.hasAnyCategory(['meeting', 'review']); // true if has either

hasAllCategories(categories)

Returns true if the event has all of the given categories.

Full-field snapshots and serialization

Core 2.5.6 retains endTimeZone in clone() and toObject(), alongside supported event fields. Event.isEquivalent(a, b) compares the fields used by reconciliation, including nested host data; it is distinct from relying on object identity.

All-day normalization uses local start-of-day through the inclusive last day's 23:59:59.999. Use civil-date parsing at an external adapter boundary; see date migration.

Occurrence Events produced by EventStore carry isOccurrence, recurringEventId and occurrenceStart, with stable IDs derived from the master and occurrence instant. Event.occurrenceId(masterId, start) and Event.parseOccurrenceId(id) support that identity contract. Reconciliation resolves occurrence references back to their master rather than replacing a recurring series with one visible instance.