@bimetal/core
v0.9.0
Published
Platform-agnostic calendar core: time model, segmentation, layout, collision detection, domain models
Downloads
642
Maintainers
Readme
@bimetal/core
Platform-agnostic calendar core. Stateless, deterministic, pure functions. Zero external dependencies.
Installation
npm install @bimetal/coreWhat's Inside
Time Model
CalendarDateTime — a point in time with timezone context. All arithmetic happens in UTC, timezone is used for wall-clock interpretation.
import { createDateTime, addDays, daysBetween, getHours, isSameDay } from '@bimetal/core';
const dt = createDateTime(2026, 2, 15, 9, 0, 'Europe/Berlin');
const tomorrow = addDays(dt, 1); // DST-safe: preserves wall-clock time
const days = daysBetween(dt, tomorrow); // calendar days, not epochMs/86400000TimeRange, Duration
import { createTimeRange, overlaps, duration, toMinutes } from '@bimetal/core';
const range = createTimeRange(start, end); // throws if start >= end
const conflict = overlaps(rangeA, rangeB);
const mins = toMinutes(duration(range));Grid Calculation
import { monthGrid, weekGrid, daySlots, calendarWeekNumber } from '@bimetal/core';
const weeks = monthGrid(2026, 3, config); // CalendarDateTime[][]
const days = weekGrid(currentDate, config); // CalendarDateTime[7]
const slots = daySlots(currentDate, config); // TimeSlot[] (respects granularity + working hours)
const kw = calendarWeekNumber(dt); // ISO 8601Segmentation + Layout
import { segmentAllDay, segmentTimed, layoutAllDay, layoutTimed } from '@bimetal/core';
const segments = segmentAllDay(events, weekDates); // clips + splits at week boundaries
const items = layoutAllDay(segments, weekDates); // lane assignment (LayoutItem[])
const timedItems = layoutTimed(timedSegments); // column assignment + relative offsetsGranularity
import { snap, generateSlots } from '@bimetal/core';
const snapped = snap(dt, { minutes: 15 }, 'floor'); // 09:07 → 09:00
const slots = generateSlots(dayRange, { minutes: 15 }); // 96 slots for a 24h dayAxis Mapping
import { timeToPosition, positionToTime } from '@bimetal/core';
const pos = timeToPosition(noon, dayRange); // 0.5
const time = positionToTime(0.5, dayRange); // noonDomain Models
Typed extension point for business data on events.
import { getDomain } from '@bimetal/core';
import type { CalendarBaseDomain } from '@bimetal/core';
const status = getDomain<CalendarBaseDomain>(event, 'calendar')?.status;Base Rules
Validation as typed, composable rule objects.
import { evaluateRules, BASE_EVENT_RULES } from '@bimetal/core';
const failures = evaluateRules(BASE_EVENT_RULES, event); // RuleResult[]Configuration
import { defaultConfig, mergeConfig } from '@bimetal/core';
const config = mergeConfig({
timezone: 'Europe/Berlin',
weekStart: 0, // Monday
granularity: { day: { minutes: 15 }, week: { minutes: 15 }, month: { minutes: 60 } },
workingHours: { start: 8, end: 18, days: [0, 1, 2, 3, 4] },
});Key Design Decisions
- Temporal-aligned:
{ epochMs, timezone }— future-proof, DST-safe - Immutable: all types are
readonly, all functions return new objects - Zero dependencies: only
Intl.DateTimeFormatfor formatting - Deterministic: injectable
Clockfor tests and SSR
