# Sprachen, Kalenderdaten und Zeitzonen

> Köpfe und Texte mit jeder Intl-Locale lokalisieren, SuperScheduler.Date formatieren und Zeitzonen und Wiederholungen dort lassen, wo sie hingehören: in Ihrer App.

Source: https://superscheduler.org/de/docs/locales-dates-timezones/
Reviewed: 2026-10-07

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.

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](#built-in-strings)).

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

```tsx
// src/FrenchPlanning.tsx
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:

| Locale | `ddd d MMM` | Uhr | Woche beginnt am |
|---|---|---|---|
| `en-us` | Mo 5 Oct | 12 Stunden | Sonntag |
| `en-gb` | Mo 5 Oct | 24 Stunden | Montag |
| `es-es` | L 5 oct | 24 Stunden | Montag |
| `de-de` | Mo 5 Okt | 24 Stunden | Montag |
| `fr-fr` | lu 5 oct. | 24 Stunden | Montag |
| `pt-br` | se 5 out. | 24 Stunden | Sonntag |

## 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.

> **Behavior:**
> `date.toString(pattern, locale)` liest die `locale` des Planers nicht. Ohne das zweite Argument formatiert die Methode in `en-us`. Halten Sie die Locale-ID in einer einzigen Konstante und übergeben Sie sie an die Komponente und an jeden Aufruf von `toString`, wie im Beispiel oben.

`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:

```ts
// src/format.ts
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.

```ts
// src/locale.ts
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."
```
> **Behavior:**
> Felder, die Sie in `new SuperScheduler.Locale(id, fields)` weglassen, erhalten die Standardwerte für US-Englisch (englische Namen, `M/d/yyyy`, 12-Stunden-Uhr, Sonntag), nicht die `Intl`-Daten für diese ID. Gehen Sie wie gezeigt von `SuperScheduler.Locale.find(id)` aus und überschreiben Sie dann.

## Eingebaute Texte und ihre Sprachen
Die Bibliothek schreibt einige eigene Texte. Die Sprache ist der erste Teil der Locale-ID (`ca-es` ergibt `ca`).

| Texte | Sprachen | Überschreiben |
|---|---|---|
| Tastaturansagen und Fokusbeschriftungen | Englisch, Spanisch, Katalanisch, Baskisch, Galicisch, Deutsch, Französisch, Italienisch, Portugiesisch | Nicht konfigurierbar |
| Dauern und Ablehnungen auf der Ziehkarte („2 nights“, „Overlaps“, „Not allowed“) | Dieselben neun Sprachen | `dragCard={{ labels: { ... } }}` |
| Name des Rasters, Lade-, Leer- und Fehlertexte | Englisch, oder Spanisch für `es`-Locales | `emptyState`, `errorState` und `loadingLabelText`; der barrierefreie Name des Rasters ist fest |
| Beschriftungen der Verlaufseinträge („Move“, „Resize“) | Englisch, oder Spanisch für `es`-Locales | `createHistory({ labels })` |
| Beschriftungen der Minimap | Englisch, oder Spanisch für `es`-Locales | `createMinimap(control, element, { labels })` oder die Prop `labels` von `SchedulerMinimap` |
| Badge für die Detailstufe | Englisch, oder Spanisch für `es`-Locales | drittes Argument von `createLodBadge` |
| Lite: Beschriftung des Rasters und Leertext | Englisch | `ariaLabel`, `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:

```ts
// src/time-zones.ts
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.

> **Limitation:**
> Ein Planer hat eine Zeitachse. Zeilen in verschiedenen Zeitzonen können sie trotzdem teilen, aber dann entscheiden Sie, was die Achse bedeutet: Rechnen Sie jede Zeile in eine gemeinsame Anzeigezone um, oder zeigen Sie jede Zeile in ihrer eigenen Ortszeit und nehmen Sie in Kauf, dass dieselbe Spalte verschiedene Zeitpunkte bedeutet.

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:

```ts
// src/recurrence.ts
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.

→ https://superscheduler.org/de/examples/video-production/
Verwandte Anleitungen: [Ressourcen, Ereignisse und Intervalle](https://superscheduler.org/de/docs/resources-events-intervals/), [Zeitskalen und Zoom](https://superscheduler.org/de/docs/time-scales-zoom/), [Tastatur, Barrierefreiheit und Touch](https://superscheduler.org/de/docs/keyboard-accessibility-touch/) und [Daten nach Zeitraum laden](https://superscheduler.org/de/docs/range-loading/).
