Zum Inhalt springen
SuperScheduler

ProduktionGilt fürLite und Pro

Sprachen, Kalenderdaten und Zeitzonen

Setzen Sie `locale` auf eine beliebige Intl-Locale-ID wie `fr-fr`: Monats- und Tagesnamen, die 12- oder 24-Stunden-Uhr und der erste Wochentag richten sich danach, und `timeFormat` und `weekStarts` überschreiben die letzten beiden. Datumswerte sind bürgerliche Wanduhrzeiten: Der Planer rechnet nie Zeitzonen um und expandiert nie wiederkehrende Ereignisse. Rechnen Sie Zeitpunkte in die Zeitzone des Geschäfts um, bevor Sie Ereignisse übergeben, rechnen Sie beim Speichern zurück und expandieren Sie Serien in Ihrer Anwendung zu einzelnen Vorkommen.

Geprüft mit v0.1.0 · überarbeitet am 7. Oktober 2026.md

Ein Planer zeigt Menschen Datumsangaben; deshalb treffen hier zwei getrennte Fragen aufeinander. Die Lokalisierung entscheidet, wie ein Datum geschrieben wird: Namen, Reihenfolge, Uhr und erster Wochentag. Die Zeitsemantik entscheidet, welches Datum es ist: Die Bibliothek arbeitet mit Wanduhrzeiten in bürgerlicher Zeit und überlässt Zeitzonen und Wiederholungen Ihrer Anwendung. Diese Anleitung behandelt beides, für Pro und, wo vermerkt, für Lite.

Die Locale festlegen

locale akzeptiert jede Locale-ID, die Intl versteht, im Stil von SuperScheduler kleingeschrieben: en-us (der Standard), en-gb, fr-fr, de-de, es-es, pt-br, nl-nl, ja-jp und so weiter. Es gibt keine Liste, in die Sie sich eintragen müssten. en_US wird zu en-us normalisiert, und eine ID, die Intl nicht auflösen kann, fällt auf en-us zurück.

In Pro steuert die Locale:

  • die Monats- und Tagesnamen in den Standard-Zeitköpfen und in jedem format-Muster eines Kopfes;
  • die Standarduhr, wenn timeFormat auf 'Auto' steht (12 Stunden für en-us, 24 Stunden für die meisten europäischen Locales);
  • den ersten Wochentag, wenn weekStarts auf 'Auto' steht (Sonntag für en-us und pt-br, Montag für die meisten europäischen Locales);
  • die Standard-Datumsmuster der Tagesköpfe und die Datumsangaben auf der Ziehkarte;
  • die Sprache der Tastaturansagen und einiger eingebauter Beschriftungen (siehe Eingebaute Texte).

Lite akzeptiert dieselben locale-IDs für seine Tagesköpfe.

src/FrenchPlanning.tsxtsx
import { useCallback, useState } from 'react'
import { SuperScheduler, SuperSchedulerComponent } from 'super-scheduler'
import type { SchedulerEventClickArgs } from 'super-scheduler'

// One constant for the scheduler and for every date you format yourself.
const LOCALE = 'fr-fr'
const WEEK_START = SuperScheduler.Date.today().firstDayOfWeek(1)

const TIME_HEADERS: SuperScheduler.TimeHeaderData[] = [
  { groupBy: 'Day', format: 'dddd d MMMM' }, // "lundi 5 octobre"
  { groupBy: 'Hour' }, // default labels follow timeFormat: "0" to "23" here, "2 PM" in 12-hour mode
]

interface Props {
  readonly rooms: SuperScheduler.ResourceData[]
  readonly events: SuperScheduler.EventData[]
}

export function FrenchPlanning({ rooms, events }: Props) {
  const [summary, setSummary] = useState('')

  // SuperScheduler.Date#toString does not read the scheduler's locale: pass it explicitly.
  const onEventClick = useCallback((args: SchedulerEventClickArgs) => {
    const start = args.e.start().toString('dddd d MMMM, HH:mm', LOCALE)
    const end = args.e.end().toString('HH:mm', LOCALE)
    setSummary(`${args.e.text()} : ${start} – ${end}`)
  }, [])

  return (
    <>
      <p aria-live="polite">{summary}</p>
      <SuperSchedulerComponent
        locale={LOCALE}
        // 'Auto' already gives Monday and a 24-hour clock for fr-fr; explicit values ignore the locale.
        weekStarts={1}
        timeFormat="Clock24Hours"
        startDate={WEEK_START}
        days={7}
        scale="Hour"
        cellWidth={48}
        timeHeaders={TIME_HEADERS}
        resources={rooms}
        events={events}
        onEventClick={onEventClick}
      />
    </>
  )
}

Sie sollten Köpfe wie „lundi 5 octobre“ über Stundenspalten mit den Beschriftungen 0 bis 23 sehen, eine Woche, die am Montag beginnt, und eine französische Zusammenfassung, wenn Sie auf ein Ereignis klicken.

Uhrformat und erster Wochentag

timeFormat akzeptiert 'Auto', 'Clock12Hours' oder 'Clock24Hours'. Es ändert nur die Standardbeschriftungen der Stunden; ein explizites format in timeHeaders hat immer Vorrang ('HH:mm' für 24 Stunden, 'h:mm tt' für 12 Stunden). Es ist eine Anzeigeeinstellung, keine Zeitumrechnung: Ein Umschalten verschiebt nie ein Ereignis.

weekStarts akzeptiert 'Auto' oder eine Tagesnummer von 0 (Sonntag) bis 6 (Samstag). Es wirkt sich auf Week-Zellen und Kopfgruppen aus, auf die Wochenlinien, die beim Herauszoomen gezeichnet werden, und auf die Standard-Wochennummern: ISO-Nummern, wenn Wochen am Montag beginnen, sonst US-Nummern. In Ihrem eigenen Code verwendet date.firstDayOfWeek() standardmäßig Sonntag; übergeben Sie daher denselben Wert (firstDayOfWeek(1)) oder die Locale-ID (firstDayOfWeek('fr-fr')).

Worauf 'Auto' bei einigen IDs hinausläuft:

Localeddd d MMMUhrWoche beginnt am
en-usMo 5 Oct12 StundenSonntag
en-gbMo 5 Oct24 StundenMontag
es-esL 5 oct24 StundenMontag
de-deMo 5 Okt24 StundenMontag
fr-frlu 5 oct.24 StundenMontag
pt-brse 5 out.24 StundenSonntag

Datumswerte in Ihrer eigenen Oberfläche formatieren

SuperScheduler.Date formatiert mit den Mustern von SuperScheduler: yyyy, yy, MMMM, MMM, MM, M, dddd, ddd, dd, d, HH, H, hh, h, mm, m, ss, s und tt (AM/PM). Alles andere ist wörtlicher Text.

ddd ist der kurze Wochentagsname aus Intl, der in mehreren Locales aus einem oder zwei Buchstaben besteht: „Mo“ auf Englisch, „L“ auf Spanisch, „lu“ auf Französisch, „dl“ auf Katalanisch. Verwenden Sie dddd für den vollen Namen, registrieren Sie eigene Kurznamen (siehe unten) oder formatieren Sie direkt mit Intl. date.toDate() gibt ein natives Date mit denselben Ticks zurück, das in UTC gelesen werden soll; formatieren Sie es daher mit timeZone: 'UTC', um auf jedem Gerät genau den Wert in bürgerlicher Zeit anzuzeigen:

src/format.tsts
import type { SuperScheduler } from 'super-scheduler'

// toDate() returns a native Date with the same ticks, meant to be read in UTC.
// Formatting it with timeZone 'UTC' shows exactly the civil value, on any device.
const dayFormat = new Intl.DateTimeFormat('fr-FR', {
  weekday: 'short',
  day: 'numeric',
  month: 'short',
  timeZone: 'UTC',
})
const stayFormat = new Intl.DateTimeFormat('en-GB', {
  day: 'numeric',
  month: 'short',
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'UTC',
})

export function formatDay(date: SuperScheduler.Date): string {
  return dayFormat.format(date.toDate()) // "lun. 5 oct."
}

export function formatStay(start: SuperScheduler.Date, end: SuperScheduler.Date): string {
  return stayFormat.formatRange(start.toDate(), end.toDate())
}

Eigene Namen und Muster

SuperScheduler.Locale.register() ersetzt die Namen und Muster einer ID für jeden Planer und jeden Aufruf von toString, der sie verwendet. Eine SuperScheduler.Locale, die direkt als Option locale übergeben wird, wird automatisch registriert.

src/locale.tsts
import { SuperScheduler } from 'super-scheduler'

// Start from the Intl data for the id: fields you leave out fall back to US English, not to French.
const base = SuperScheduler.Locale.find('fr-fr')

// Run once at startup, before the first scheduler mounts.
SuperScheduler.Locale.register(
  new SuperScheduler.Locale('fr-fr', {
    ...base,
    // `ddd` gives two-letter Intl abbreviations ("lu"); these read "lun.".
    dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
  }),
)

// Every scheduler with locale="fr-fr" and every toString(pattern, 'fr-fr') now uses these names.
export const sample = new SuperScheduler.Date('2026-10-05').toString('ddd d MMM', 'fr-fr') // "lun. 5 oct."

Eingebaute Texte und ihre Sprachen

Die Bibliothek schreibt einige eigene Texte. Die Sprache ist der erste Teil der Locale-ID (ca-es ergibt ca).

TexteSprachenÜberschreiben
Tastaturansagen und FokusbeschriftungenEnglisch, Spanisch, Katalanisch, Baskisch, Galicisch, Deutsch, Französisch, Italienisch, PortugiesischNicht konfigurierbar
Dauern und Ablehnungen auf der Ziehkarte („2 nights“, „Overlaps“, „Not allowed“)Dieselben neun SprachendragCard={{ labels: { ... } }}
Name des Rasters, Lade-, Leer- und FehlertexteEnglisch, oder Spanisch für es-LocalesemptyState, errorState und loadingLabelText; der barrierefreie Name des Rasters ist fest
Beschriftungen der Verlaufseinträge („Move“, „Resize“)Englisch, oder Spanisch für es-LocalescreateHistory({ labels })
Beschriftungen der MinimapEnglisch, oder Spanisch für es-LocalescreateMinimap(control, element, { labels }) oder die Prop labels von SchedulerMinimap
Badge für die DetailstufeEnglisch, oder Spanisch für es-Localesdrittes Argument von createLodBadge
Lite: Beschriftung des Rasters und LeertextEnglischariaLabel, emptyState

Jede andere Sprache erhält Englisch; eine französische oder deutsche Anwendung sollte daher eigene Texte für Verlauf, Minimap, Badge und Statustexte übergeben. Ereignistexte, Ressourcennamen und jegliches HTML, das Sie rendern, übersetzen Sie selbst.

Bürgerliche Zeit

Jedes Datum in SuperScheduler ist ein Wert in bürgerlicher Zeit, also eine Wanduhrzeit ohne Zeitzone. '2026-10-01T10:00:00' bedeutet zehn Uhr auf der Planungstafel, egal wo die Seite geöffnet wird. Die Folgen:

  • Strings brauchen Sekunden. '2026-10-01' und '2026-10-01T10:00:00' sind gültig; '2026-10-01T10:00' wirft „is not an ISO 8601 date“. Native Date-Objekte bestehen die Typprüfung für start oder end nicht.
  • Zonen werden in UTC umgerechnet. Ein String mit Z oder einem Offset wird in die UTC-Wanduhrzeit umgerechnet: '2026-10-01T10:00:00+02:00' wird zu 08:00:00. Entfernen Sie Zonenangaben erst, nachdem Sie selbst in die Zeitzone des Geschäfts umgerechnet haben.
  • Keine Überraschungen durch die Sommerzeit. 2026-03-29T02:30:00 existiert, und eine Stunde darauf ergibt 03:30, unabhängig von der Zeitzone des Browsers. Dauern sind einfache Differenzen von Wanduhrzeiten.
  • Intervalle sind halboffen. Ein Ereignis von 14:00 bis 16:00 endet vor einer Buchung, die um 16:00 beginnt.
  • Native Datumswerte. new SuperScheduler.Date(date) liest die UTC-Felder eines nativen Date; new SuperScheduler.Date(date, true) liest seine lokalen Felder. toDate() gibt ein Date zurück, das in UTC zu lesen ist; toDateLocal() gibt eines zurück, dessen lokale Felder die Wanduhrzeit zeigen.
  • „Heute“ ist das Gerät des Betrachters. SuperScheduler.Date.today(), der Standardwert von startDate, die Hervorhebung von heute und die Jetzt-Linie verwenden die Uhr des Geräts. Wer in New York ein Hotel in Madrid plant, sieht das Heute von New York. Wenn das wichtig ist, berechnen Sie das „Heute“ des Geschäfts selbst und übergeben es als startDate oder an scrollTo.

Zeitzonen sind Sache Ihrer Anwendung

Wenn Ihr Backend Zeitpunkte speichert (UTC-Zeitstempel), wählen Sie die Zeitzone, die ein Planer jeweils darstellt, meist die des Standorts oder des Ressourceninhabers, und rechnen an den Rändern um. Intl.DateTimeFormat mit einer timeZone liefert Ihnen die Wanduhrzeit jedes Zeitpunkts, ohne zusätzliche Abhängigkeit:

src/time-zones.tsts
import { SuperScheduler } from 'super-scheduler'

const formatters = new Map<string, Intl.DateTimeFormat>()

function partsFormatter(timeZone: string): Intl.DateTimeFormat {
  let formatter = formatters.get(timeZone)
  if (formatter === undefined) {
    formatter = new Intl.DateTimeFormat('en-US', {
      timeZone,
      hourCycle: 'h23',
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
    })
    formatters.set(timeZone, formatter)
  }
  return formatter
}

/** What a wall clock in `timeZone` shows at `instant`, as a civil ISO string: "2026-10-01T10:00:00". */
export function toWallClock(instant: Date, timeZone: string): string {
  const part: Record<string, string> = {}
  for (const { type, value } of partsFormatter(timeZone).formatToParts(instant)) part[type] = value
  return `${part.year}-${part.month}-${part.day}T${part.hour}:${part.minute}:${part.second}`
}

function offsetAt(ms: number, timeZone: string): number {
  return Date.parse(`${toWallClock(new Date(ms), timeZone)}Z`) - ms
}

/**
 * The instant at which a wall clock in `timeZone` shows `wall`. Times skipped or repeated by a
 * daylight-saving change have no single answer: this picks a neighbouring instant, so validate
 * them in your application if they matter.
 */
export function fromWallClock(wall: SuperScheduler.DateInput, timeZone: string): Date {
  const asUtc = Date.parse(`${new SuperScheduler.Date(wall).value}Z`)
  const guess = asUtc - offsetAt(asUtc, timeZone)
  return new Date(asUtc - offsetAt(guess, timeZone))
}

/** API instants to scheduler events on the property's wall clock. */
export async function loadBookings(fromUtc: string, toUtc: string, timeZone: string) {
  const rows = await fetchBookingInstants(fromUtc, toUtc)
  return rows.map((row): SuperScheduler.EventData => ({
    id: row.id,
    resource: row.roomId,
    text: row.guest,
    start: toWallClock(new Date(row.startUtc), timeZone),
    end: toWallClock(new Date(row.endUtc), timeZone),
  }))
}

// Saving goes the other way: fromWallClock(event.start, 'Europe/Madrid').toISOString()

Rechnen Sie auf dem Hinweg um (toWallClock, wenn Sie API-Zeilen auf Ereignisse abbilden) und auf dem Rückweg (fromWallClock, wenn Sie start und end speichern, die nach einem Ziehen SuperScheduler.Date-Objekte sind). Beim bereichsweisen Laden rechnen Sie start und end des Abschnitts, die in bürgerlicher Zeit vorliegen, auf dieselbe Weise um, bevor Sie eine UTC-basierte API abfragen.

Zeiten, die bei der Zeitumstellung übersprungen oder wiederholt werden, sind eine Geschäftsregel, kein Formatierungsdetail. Eine Buchung um 02:30 in der Nacht, in der die Uhren vorgestellt werden, existiert in Madrid nicht; entscheiden Sie, ob Sie sie ablehnen, verschieben oder anders speichern.

Wiederkehrende Ereignisse

Der Planer hat keine Engine für Wiederholungen. Die Felder recurrent und recurrentMasterId sind typisiert, aber control.events.findRecurrent() ist reserviert und gibt null zurück. Speichern Sie Serien in Ihrer Anwendung und expandieren Sie sie für die Tage auf dem Bildschirm zu gewöhnlichen Ereignissen. Geben Sie jedem Vorkommen eine ID, die über Anfragen hinweg stabil ist, etwa die Serien-ID plus das Datum, damit das bereichsweise Laden es zusammenführen kann:

src/recurrence.tsts
import { SuperScheduler } from 'super-scheduler'

/** A weekly series as your application stores it. */
export interface WeeklySeries {
  readonly id: string
  readonly resource: string
  readonly text: string
  /** First occurrence, civil date-time with seconds. */
  readonly start: string
  readonly durationMinutes: number
  /** 0 = Sunday ... 6 = Saturday. */
  readonly weekdays: readonly number[]
  /** Last day of the series, inclusive, as yyyy-MM-dd. */
  readonly until: string
  /** Days removed from the series, as yyyy-MM-dd. */
  readonly exceptions: readonly string[]
}

/** The occurrences that overlap [from, to), each with an id that is stable across requests. */
export function expandWeekly(
  series: WeeklySeries,
  from: SuperScheduler.Date,
  to: SuperScheduler.Date,
) {
  const first = new SuperScheduler.Date(series.start)
  const firstDay = first.getDatePart().ticks
  const afterLastDay = new SuperScheduler.Date(series.until).addDays(1).ticks
  // Look back far enough to catch an occurrence that started earlier and is still running.
  const lookBack = Math.ceil(series.durationMinutes / 1440)
  const events: SuperScheduler.EventData[] = []
  for (
    let day = from.getDatePart().addDays(-lookBack);
    day.ticks < to.ticks;
    day = day.addDays(1)
  ) {
    if (day.ticks < firstDay || day.ticks >= afterLastDay) continue
    if (!series.weekdays.includes(day.getDayOfWeek())) continue
    const key = day.toString('yyyy-MM-dd')
    if (series.exceptions.includes(key)) continue
    const start = day.addTime(first.getTimePart())
    const end = start.addMinutes(series.durationMinutes)
    if (end.ticks <= from.ticks) continue
    events.push({
      id: `${series.id}:${key}`,
      resource: series.resource,
      text: series.text,
      start,
      end,
    })
  }
  return events
}

Auch das Bearbeiten liegt bei Ihnen. „Nur dieses Vorkommen“ bedeutet meist, das Datum zu den Ausnahmen der Serie hinzuzufügen und ein eigenständiges Ereignis anzulegen; „dieses und alle folgenden“ teilt die Serie; „alle Vorkommen“ ändert die Serie und expandiert sie neu. Bilden Sie Änderungen aus onEventsChange anhand der ID des Vorkommens auf diese Operationen ab. Das Expandieren in der load-Funktion eines Range-Loaders hält lange Serien günstig: Nur die sichtbaren Abschnitte werden expandiert.

Checkliste

  • Eine einzige Konstante LOCALE, übergeben an die Komponente und an jeden Aufruf von toString.
  • weekStarts und firstDayOfWeek() stimmen überein.
  • Eigene Texte für Verlauf, Minimap, Badge und Statustexte für alle Sprachen außer Englisch und Spanisch.
  • Start- und Endzeiten der Ereignisse als Strings in bürgerlicher Zeit mit Sekunden, in die Zeitzone des Geschäfts umgerechnet, bevor sie den Planer erreichen.
  • Beim Speichern wird in Zeitpunkte zurückgerechnet, falls Ihr Backend solche speichert.
  • Serien werden pro sichtbarem Zeitraum expandiert, mit stabilen IDs für die Vorkommen.

Planung einer VideoproduktionEin Dreh dauert länger. Verschieben Sie den abhängigen Schnitt, verstehen Sie warum, und nehmen Sie es zurück. Verwandte Anleitungen: Ressourcen, Ereignisse und Intervalle, Zeitskalen und Zoom, Tastatur, Barrierefreiheit und Touch und Daten nach Zeitraum laden.