====== std.datetime ====== Datum und Uhrzeit rund um einen **Unix-Zeitstempel in Sekunden**: formatieren (ISO 8601, RFC 2822, eigene Muster), parsen, rechnen, prüfen. Die Unit arbeitet durchgehend in **UTC** und braucht keine externen Bibliotheken. → [[lyx_-_programmiersprache:units:time|std.time]] · [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units|Standard Library]] Alle Beispiele dieser Seite sind mit ''lyxc 1.1.2E'' übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben und gegen Pythons ''datetime'' geprüft. > **Die Erweiterungen aus [[https://github.com/SEOLizer/LyX-Compiler/issues/1601|#1601]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1602|#1602]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1603|#1603]] und [[https://github.com/SEOLizer/LyX-Compiler/issues/1605|#1605]] stecken in der Repo-Fassung der Standardbibliothek.** Die unter ''/usr/include/lyx/units'' ausgelieferte Fassung hinkt hinterher ([[https://github.com/SEOLizer/LyX-Compiler/issues/1578|#1578]]). Wer ''undefined function 'DatetimeFromYmdHms''' sieht, übersetzt gegen die Repo-Fassung: > > lyxc --std-path=/pfad/zum/compiler-repo programm.lyx -o programm **Einsatz:** Protokolle und Audit-Trails, HTTP-Kopfzeilen (''Date:'', ''Last-Modified:''), Kalender- und Buchungslogik, Datenbank-Zeitstempel, SMTP/IMAP. > **Datetime-Werte und Ergebniszeiger nicht verwechseln.** Beide Parametersorten stehen in den Signaturen als ''int64'', meinen aber Verschiedenes: > > * ''FormatIso(datetime, output)'' und die übrigen ''Format…''-Funktionen erwarten den **Wert** — einen Unix-Zeitstempel in Sekunden. > * ''ParseIso(input, result)'' und die übrigen ''Parse…''-Funktionen erwarten einen **Zeiger** auf einen Speicherbereich, in den das Ergebnis geschrieben wird. > > Beim Verketten also den Wert auslesen: > > var dt: int64 := alloc(8); ParseIso("2026-08-10T14:30:45"c, dt); // schreibt nach dt var ts: int64 := peek64(dt); // Wert holen FormatIso(ts, ausgabe); // -> 2026-08-10T14:30:45Z > > Wird versehentlich der Zeiger an ''FormatIso'' übergeben, meldet der Compiler nichts (beide sind ''int64'') — die Ausgabe ist dann ein Datum irgendwo im Jahr 587. **Ausgabepuffer stellt der Aufrufer.** Jede ''Format…''-Funktion schreibt in einen übergebenen Puffer und hängt ein Nullbyte an; zurück kommt die Zahl der Zeichen ohne dieses Nullbyte. 64 Byte reichen für alle festen Formate, für ''Format'' mit eigenem Muster entsprechend mehr. ---- ===== Formatieren ===== ^ Signatur ^ Ergebnis ^ | ''FormatIso(datetime, output)'' | ''2026-08-17T07:45:12Z'' | | ''FormatRfc2822(datetime, output)'' | ''Mon, 17 Aug 2026 07:45:12 +0000'' | | ''Format(datetime, fmt, output)'' | frei über Platzhalter — siehe Tabelle | | ''FormatDate(year, month, day, output)'' | ''2026-08-17'' — nimmt **Einzelwerte**, keinen Zeitstempel | | ''FormatTime(hour, minute, second, output)'' | ''07:45:12'' — ebenfalls Einzelwerte | | ''FormatLocale(datetime, locale, output)'' | ''17.08.2026 14:30:00'' (de) — siehe Kasten | ==== Platzhalter für ''Format'' ==== ^ Platzhalter ^ Bedeutung ^ Beispiel ^ | ''%Y'' | Jahr, vierstellig | ''2026'' | | ''%y'' | Jahr, zweistellig | ''26'' | | ''%m'' | Monat, zweistellig | ''08'' | | ''%d'' | Tag, zweistellig | ''17'' | | ''%H'' | Stunde, 00–23 | ''07'' | | ''%M'' | Minute | ''45'' | | ''%S'' | Sekunde | ''12'' | | ''%w'' | Wochentag als Ziffer, 0 = Sonntag | ''1'' | | ''%W'' | Wochentag ausgeschrieben | ''Monday'' | | ''%b'' | Monatskürzel | ''Aug'' | | ''%B'' | Monat ausgeschrieben | ''August'' | | ''%p'' | ''AM'' / ''PM'' | ''AM'' | | ''%z'' | Zeitzonenversatz aus ''TZ'' | ''+0000'' | | ''%Z'' | Zonenname — **immer ''UTC''** | ''UTC'' | | ''%I'' | Stunde im 12-Stunden-Format, 01–12 | ''02'' | | ''%j'' | Tag im Jahr, dreistellig | ''229'' | | ''%%'' | ein wörtliches Prozentzeichen | ''%'' | | ''%f'' | Mikrosekunden — **immer ''000000''** | ''000000'' | Ein **unbekannter Platzhalter wird weiterhin wörtlich ausgegeben** — ''%e'' erscheint als ''%e'' im Ergebnis. Stumm ist das seit [[https://github.com/SEOLizer/LyX-Compiler/issues/1602|#1602]] nicht mehr: der **Rückgabewert ist dann negativ**, sein Betrag bleibt die Zahl der geschriebenen Zeichen. Wer ihn wie bisher ignoriert, bekommt dieselbe Zeichenkette wie zuvor. %p %I -> PM 02 rc=5 %j -> 229 rc=3 100%% fertig -> 100% fertig rc=11 %e %s -> %e %s rc=-5 import std.datetime; import std.alloc; import std.string; fn main(): int64 { var out: int64 := alloc(256); var o: pchar := out as pchar; // 2026-08-17T07:45:12Z var ts: int64 := 1786952712; FormatIso(ts, o); PrintLn(StrConcat("ISO 8601: ", o)); FormatRfc2822(ts, o); PrintLn(StrConcat("RFC 2822: ", o)); Format(ts, "%d.%m.%Y %H:%M:%S"c, o); PrintLn(StrConcat("deutsch: ", o)); Format(ts, "%W, %d. %B %Y"c, o); PrintLn(StrConcat("lang: ", o)); Format(ts, "%y-%m-%d %I%p"c, o); PrintLn(StrConcat("gemischt: ", o)); Format(ts, "%Z %z %w %b %f"c, o); PrintLn(StrConcat("Rest: ", o)); FormatLocale(ts, "de_DE"c, o); PrintLn(StrConcat("Locale de: ", o)); // Roundtrip: ISO parsen und zurueck var buf: int64 := alloc(8); var ok: int64 := ParseIso("2026-08-13T09:45:12Z"c, buf); PrintLn(StrConcat(StrConcat("ParseIso ok=", IntToStr(ok)), StrConcat(" ts=", IntToStr(peek64(buf))))); FormatIso(peek64(buf), o); PrintLn(StrConcat("zurueck: ", o)); return 0; } ISO 8601: 2026-08-17T07:45:12Z RFC 2822: Mon, 17 Aug 2026 07:45:12 +0000 deutsch: 17.08.2026 07:45:12 lang: Monday, 17. August 2026 gemischt: 26-08-17 %IAM Rest: UTC +0000 1 Aug 000000 Locale de: 2026-08-17T07:45:12Z ParseIso ok=1 ts=1786614312 zurueck: 2026-08-13T09:45:12Z > **''FormatLocale'' lokalisiert seit [[https://github.com/SEOLizer/LyX-Compiler/issues/1415|#1415]] wirklich.** Unterstützt werden die Sprachkennungen ''de'', ''at'', ''ch'', ''en'', ''fr'', ''es'', ''it'' und ''pt'' — geprüft werden nur die ersten beiden Zeichen. ''en_US'' bekommt als einziges Locale die US-Schreibweise mit 12-Stunden-Zählung; alles Übrige folgt der Tag-vor-Monat-Ordnung. > > Ein **nicht unterstütztes** Locale ist kein Fehler: die Funktion fällt still auf ''FormatIso'' zurück. Ob ein Locale erkannt wird, beantwortet ''LocaleSupported(locale): bool'' vorab. > > de_DE : 17.08.2026 14:30:00 en_US : 8/17/2026 2:30:00 PM fr_FR : 17/08/2026 14:30:00 ja_JP : 2026-08-17T14:30:00Z <- Rückfall auf FormatIso LocaleSupported(de_DE) : true LocaleSupported(ja_JP) : false > > Die Wochentags- und Monatsnamen (''%W'', ''%B'', ''WeekdayLong'', ''MonthLong'') sind weiterhin ausschließlich **englisch**. ---- ===== Parsen ===== ^ Signatur ^ Erwartet ^ Rückgabe ^ | ''ParseIso(input, result)'' | ''YYYY-MM-DDTHH:MM:SS'' (mindestens 19 Zeichen) | ''1'' / ''0'' | | ''ParseRfc2822(input, result)'' | ''Www, DD Mon YYYY HH:MM:SS ±HHMM'' (mindestens 25 Zeichen) | ''1'' / ''0'' | | ''ParseFlexible(input, result)'' | vier Formate, siehe Kasten | ''1'' / ''0'' | | ''ParseDate(input, year, month, day)'' | ''YYYY-MM-DD'' | ''1'' / ''0'' | | ''ParseTime(input, hour, minute, second)'' | ''HH:MM'' oder ''HH:MM:SS'' | ''1'' / ''0'' | ''result'', ''year'', ''month'', ''day'', ''hour'', ''minute'' und ''second'' sind **Zeiger** auf je acht Byte, in die das Ergebnis geschrieben wird. Alle fünf Parser setzen das Ziel seit [[https://github.com/SEOLizer/LyX-Compiler/issues/1603|#1603]] **vorab auf 0**. Ein übersehener Rückgabewert-Test endet damit bei ''1970-01-01'' statt bei einem plausibel aussehenden Altwert. import std.datetime; import std.alloc; import std.string; fn main(): int64 { var out: int64 := alloc(256); var o: pchar := out as pchar; var buf: int64 := alloc(8); // RFC 2822 parsen, auch mit Zeitzonenversatz PrintLn(StrConcat("ParseRfc2822 ok=", IntToStr(ParseRfc2822("Mon, 17 Aug 2026 07:45:12 +0000"c, buf)))); FormatIso(peek64(buf), o); PrintLn(StrConcat(" -> ", o)); ParseRfc2822("Mon, 17 Aug 2026 09:45:12 +0200"c, buf); FormatIso(peek64(buf), o); PrintLn(StrConcat("+0200 -> UTC: ", o)); // ParseFlexible nimmt beides PrintLn(StrConcat("ParseFlexible(ISO): ", IntToStr(ParseFlexible("2026-08-17T07:45:12Z"c, buf)))); PrintLn(StrConcat("ParseFlexible(RFC): ", IntToStr(ParseFlexible("Mon, 17 Aug 2026 07:45:12 +0000"c, buf)))); PrintLn(StrConcat("ParseFlexible(dt.): ", IntToStr(ParseFlexible("17.08.2026"c, buf)))); PrintLn(StrConcat("ParseFlexible(Datum):", IntToStr(ParseFlexible("2026-08-17"c, buf)))); poke64(buf, 0 - 1); PrintLn(StrConcat("ParseFlexible(Unsinn):", IntToStr(ParseFlexible("kein Datum"c, buf)))); PrintLn(StrConcat(" Zielwert danach: ", IntToStr(peek64(buf)))); // Einzelteile var y: int64 := alloc(8); var m: int64 := alloc(8); var d: int64 := alloc(8); PrintLn(StrConcat("ParseDate('2026-08-17') ok=", IntToStr(ParseDate("2026-08-17"c, y, m, d)))); PrintLn(StrConcat(" Jahr/Monat/Tag: ", StrConcat(IntToStr(peek64(y)), StrConcat("/", StrConcat(IntToStr(peek64(m)), StrConcat("/", IntToStr(peek64(d)))))))); PrintLn(StrConcat("ParseDate('2026-13-01') ok=", IntToStr(ParseDate("2026-13-01"c, y, m, d)))); // Zeitzonenversatz der Umgebung PrintLn(StrConcat("GetTimezoneOffset(): ", IntToStr(GetTimezoneOffset()))); FormatTimezoneOffset(GetTimezoneOffset(), o); PrintLn(StrConcat(" formatiert: ", o)); FormatTimezoneOffset(0 - 18000, o); PrintLn(StrConcat(" -18000 s: ", o)); // Namen PrintLn(StrConcat(StrConcat("WeekdayLong(1)=", WeekdayLong(1)), StrConcat(" MonthLong(8)=", MonthLong(8)))); return 0; } ParseRfc2822 ok=1 -> 2026-08-17T07:45:12Z +0200 -> UTC: 2026-08-17T07:45:12Z ParseFlexible(ISO): 1 ParseFlexible(RFC): 1 ParseFlexible(dt.): 1 ParseFlexible(Datum):1 ParseFlexible(Unsinn):0 Zielwert danach: 0 ParseDate('2026-08-17') ok=1 Jahr/Monat/Tag: 2026/8/17 ParseDate('2026-13-01') ok=0 GetTimezoneOffset(): 0 formatiert: +00:00 -18000 s: -05:00 WeekdayLong(1)=Monday MonthLong(8)=August Der Zeitzonenversatz aus RFC 2822 wird korrekt herausgerechnet: ''09:45:12 +0200'' ergibt denselben Zeitstempel wie ''07:45:12 +0000''. > **''ParseFlexible'' probiert vier Formate** in dieser Reihenfolge ([[https://github.com/SEOLizer/LyX-Compiler/issues/1603|#1603]]): ISO 8601 mit Zeitanteil, RFC 2822, ISO-Datum ohne Zeit (Zeitanteil ''00:00:00'') und die deutsche Schreibweise ''17.08.2026'' mit optionalem '' 14:30'' oder '' 14:30:00''. Ein US-amerikanisches ''8/17/2026'' scheitert weiterhin. > > **Die Parser prüfen die Trennzeichen an festen Stellen**, nicht die Semantik: ''ParseIso'' verlangt ''-'' an Position 4 und 7, ''T'' an 10, '':'' an 13 und 16. Ein Leerzeichen statt ''T'' (wie in SQL-Zeitstempeln) wird abgewiesen. Angehängte Bruchteile oder Zonenversätze (''…:12.500+02:00'') werden dagegen **ignoriert**, nicht ausgewertet — die Zeit gilt als UTC. ---- ===== Zeitstempel bauen und zerlegen ===== Bis [[https://github.com/SEOLizer/LyX-Compiler/issues/1601|#1601]] führte der Weg von Kalenderwerten zu einem Zeitstempel nur über eine Zeichenkette — ISO-String zusammensetzen, ''ParseIso'' aufrufen. Beide Richtungen gibt es jetzt direkt: ^ Signatur ^ Beschreibung ^ | ''DatetimeFromYmdHms(year, month, day, hour, minute, second)'' | Zeitstempel aus Einzelwerten | | ''DatetimeFromYmd(year, month, day)'' | wie oben, Zeitanteil ''00:00:00'' | | ''YearOf(dt)'' / ''MonthOf(dt)'' / ''DayOf(dt)'' | Kalenderwerte aus dem Zeitstempel | | ''HourOf(dt)'' / ''MinuteOf(dt)'' / ''SecondOf(dt)'' | Uhrzeitwerte aus dem Zeitstempel | | ''DayOfWeek(dt)'' | Wochentag zum Zeitstempel, 0 = Sonntag | | ''DayOfWeekYmd(year, month, day)'' | Wochentag aus Einzelwerten | | ''WeekYear(year, month, day)'' | Jahr, zu dem die ISO-Kalenderwoche gehört | | ''StartOfDay(dt)'' / ''EndOfDay(dt)'' | ''00:00:00'' bzw. ''23:59:59'' desselben Tages | | ''StartOfMonth(dt)'' / ''EndOfMonth(dt)'' | Monatserster bzw. Monatsletzter | import std.io; import std.string; import std.alloc; import std.datetime; fn main(): int64 { var ts: int64 := DatetimeFromYmdHms(2026, 8, 17, 14, 30, 0); Print("DatetimeFromYmdHms : "); PrintLn(IntToStr(ts)); Print("YearOf/MonthOf/DayOf : "); Print(IntToStr(YearOf(ts))); Print(" "); Print(IntToStr(MonthOf(ts))); Print(" "); PrintLn(IntToStr(DayOf(ts))); Print("HourOf/MinuteOf/SecondOf : "); Print(IntToStr(HourOf(ts))); Print(" "); Print(IntToStr(MinuteOf(ts))); Print(" "); PrintLn(IntToStr(SecondOf(ts))); Print("DayOfWeek : "); Print(IntToStr(DayOfWeek(ts))); Print(" -> "); PrintLn(WeekdayLong(DayOfWeek(ts))); Print("WeekNumber/WeekYear 29.12.2025 : "); Print(IntToStr(WeekNumber(2025, 12, 29))); Print(" / "); PrintLn(IntToStr(WeekYear(2025, 12, 29))); Print("StartOfDay/EndOfMonth : "); Print(IntToStr(StartOfDay(ts))); Print(" "); PrintLn(IntToStr(EndOfMonth(ts))); Print("DiffMonths 2026-01-31 -> 2026-08-17 : "); PrintLn(IntToStr(DiffMonths(DatetimeFromYmd(2026, 1, 31), ts))); return 0; } DatetimeFromYmdHms : 1786977000 YearOf/MonthOf/DayOf : 2026 8 17 HourOf/MinuteOf/SecondOf : 14 30 0 DayOfWeek : 1 -> Monday WeekNumber/WeekYear 29.12.2025 : 1 / 2026 StartOfDay/EndOfMonth : 1786924800 1788220799 DiffMonths 2026-01-31 -> 2026-08-17 : 6 > **''WeekNumber'' allein reicht für eine Datumsangabe nicht.** Der 29.12.2025 liegt in **KW 1 des Jahres 2026** — genau dafür gibt es ''WeekYear''. Wer „KW ''x'' / Jahr aus ''%Y''" schreibt, produziert an jedem Jahreswechsel eine falsche Angabe. > > **''DiffMonths'' und ''DiffYears'' zählen volle Einheiten**, angefangene nicht. Ein Alter aus dem Geburtsdatum stimmt damit auch am Tag vor dem Geburtstag. ---- ===== Rechnen und Prüfen ===== ^ Signatur ^ Beschreibung ^ | ''AddSeconds(datetime, seconds)'' | schlichte Addition | | ''AddMinutes(datetime, minutes)'' | ''minutes × 60'' | | ''AddHours(datetime, hours)'' | ''hours × 3600'' | | ''AddDays(datetime, days)'' | ''days × 86400'' | | ''AddWeeks(datetime, weeks)'' | ''weeks × 604800'' | | ''AddMonths(datetime, months)'' | kalendarisch, mit **Klemmung** auf den Monatsletzten | | ''AddYears(datetime, years)'' | kalendarisch, klemmt den 29. Februar auf den 28. | | ''DiffSeconds(datetime1, datetime2)'' | ''datetime2 − datetime1'' — Reihenfolge beachten | | ''DiffDays(y1, m1, d1, y2, m2, d2)'' | Tagesdifferenz aus Einzelwerten | | ''DiffMonths(von, bis)'' / ''DiffYears(von, bis)'' | volle Monate bzw. Jahre zwischen zwei Zeitstempeln | | ''DaysInMonth(year, month)'' | 28–31, mit Schaltjahresregel | | ''DayOfYear(year, month, day)'' | 1–366 | | ''WeekNumber(year, month, day)'' | ISO-8601-Kalenderwoche | | ''WeekYear(year, month, day)'' | Jahr zur Kalenderwoche — siehe oben | | ''IsValidDate'' / ''IsValidTime'' / ''IsValidDateTime'' | kalendarische Prüfung, liefert ''bool'' | | ''ToUnixTimestamp'' / ''FromUnixTimestamp'' | Gleichheitsabbildung — der Datetime-Wert **ist** der Zeitstempel | | ''ToMilliseconds'' / ''FromMilliseconds'' | ''× 1000'' bzw. ''/ 1000'' | import std.datetime; import std.alloc; import std.string; fn J(b: bool): pchar { if (b) { return "gueltig"; } return "ungueltig"; } fn main(): int64 { var out: int64 := alloc(256); var o: pchar := out as pchar; // Kalenderrechnen PrintLn(StrConcat("DaysInMonth(2024, 2): ", IntToStr(DaysInMonth(2024, 2)))); PrintLn(StrConcat("DaysInMonth(2026, 2): ", IntToStr(DaysInMonth(2026, 2)))); PrintLn(StrConcat("DaysInMonth(1900, 2): ", IntToStr(DaysInMonth(1900, 2)))); PrintLn(StrConcat("DayOfYear(2026, 8, 13):", IntToStr(DayOfYear(2026, 8, 13)))); PrintLn(StrConcat("WeekNumber(2026, 8, 13):", IntToStr(WeekNumber(2026, 8, 13)))); PrintLn(StrConcat("DiffDays(2026-08-13 -> 2026-12-24): ", IntToStr(DiffDays(2026, 8, 13, 2026, 12, 24)))); // Gueltigkeit PrintLn(StrConcat("IsValidDate(2026, 2, 29): ", J(IsValidDate(2026, 2, 29)))); PrintLn(StrConcat("IsValidDate(2024, 2, 29): ", J(IsValidDate(2024, 2, 29)))); PrintLn(StrConcat("IsValidTime(24, 0, 0): ", J(IsValidTime(24, 0, 0)))); // Arithmetik am Monatsende var buf: int64 := alloc(8); ParseIso("2026-01-31T12:00:00Z"c, buf); var jan31: int64 := peek64(buf); FormatIso(AddMonths(jan31, 1), o); PrintLn(StrConcat("31.01. + 1 Monat: ", o)); FormatIso(AddMonths(jan31, 13), o); PrintLn(StrConcat("31.01. + 13 Monate:", o)); FormatIso(AddDays(jan31, 1), o); PrintLn(StrConcat("31.01. + 1 Tag: ", o)); ParseIso("2024-02-29T12:00:00Z"c, buf); FormatIso(AddYears(peek64(buf), 1), o); PrintLn(StrConcat("29.02.2024 + 1 Jahr:", o)); // Dauer FormatDuration(93784, o); PrintLn(StrConcat("FormatDuration(93784): ", o)); FormatDurationShort(93784, o); PrintLn(StrConcat("FormatDurationShort(93784): ", o)); FormatDuration(45, o); PrintLn(StrConcat("FormatDuration(45): ", o)); return 0; } DaysInMonth(2024, 2): 29 DaysInMonth(2026, 2): 28 DaysInMonth(1900, 2): 28 DayOfYear(2026, 8, 13):225 WeekNumber(2026, 8, 13):33 DiffDays(2026-08-13 -> 2026-12-24): 133 IsValidDate(2026, 2, 29): ungueltig IsValidDate(2024, 2, 29): gueltig IsValidTime(24, 0, 0): ungueltig 31.01. + 1 Monat: 2026-02-28T12:00:00Z 31.01. + 13 Monate:2027-02-28T12:00:00Z 31.01. + 1 Tag: 2026-02-01T12:00:00Z 29.02.2024 + 1 Jahr:2025-02-28T12:00:00Z FormatDuration(93784): 26h 03m 04s FormatDurationShort(93784): 26:03:04 FormatDuration(45): 45s Die Schaltjahresregel stimmt bis zur Jahrhundertausnahme (1900 hatte 28 Tage, 2024 hat 29), Wochennummer und Tagesnummer decken sich mit Pythons ''isocalendar()'' und ''timetuple()''. > **''AddMonths'' klemmt, statt überzulaufen.** 31.01. + 1 Monat ergibt den 28.02., nicht den 03.03. Das ist die übliche Kaufmannsregel — aber **nicht umkehrbar**: ''%%AddMonths(AddMonths(t, 1), -1)%%'' liefert den 28.01., nicht den 31.01. Wer Fristen rechnet, sollte das Ausgangsdatum getrennt aufheben. ---- ===== Relative Zeit und Dauer ===== ^ Signatur ^ Beispielausgabe ^ | ''FormatRelative(datetime, output)'' | ''2 hours ago'', ''in 3 days'' — gegen die **aktuelle** Zeit | | ''FormatDuration(seconds, output)'' | ''26h 03m 04s'', ''-26h 03m 04s'', ''12345h'' | | ''FormatDurationShort(seconds, output)'' | ''26:03:04'', ''-00:45'' | **Negative Dauern und Dauern ab 1000 Stunden** ergaben bis [[https://github.com/SEOLizer/LyX-Compiler/issues/1605|#1605]] stumm Müllzeichen oder einen Leerstring. Beide Funktionen rechnen jetzt mit dem Betrag und stellen ein ''-'' voran; die Stundenzahl ist nicht mehr auf drei Stellen begrenzt: FormatDuration(-93784) -> -26h 03m 04s FormatDuration(1000*3600) -> 1000h FormatDurationShort(-45) -> -00:45 ''FormatRelative'' holt die Gegenwart über ''clock_gettime''; bis lyxc 1.0.17C rechnete es wegen einer nicht gebundenen Zeitquelle gegen die Epoche und lieferte deshalb Abstände von über 50 Jahren (#1179). Die Ausgabe ist **englisch**. ---- ===== Zeitzonen ===== ^ Signatur ^ Beschreibung ^ | ''GetTimezoneOffset(): int64'' | Versatz zu UTC in Sekunden, aus der Umgebungsvariablen ''TZ'' | | ''FormatTimezoneOffset(offset_seconds, output)'' | ''+02:00'' / ''-05:00'' | Ohne gesetztes ''TZ'' ist der Versatz ''0''. ''FormatIso'' und ''FormatRfc2822'' schreiben grundsätzlich UTC (''Z'' bzw. ''+0000'') — der Versatz wirkt nur über den Platzhalter ''%z'' und über ''FormatTimezoneOffset''. ^ ''TZ'' ^ Ergebnis ^ | ''CET-1'' | 3600 | | ''EST5'' | −18000 | | ''UTC'' oder nicht gesetzt | 0 | | ''IST-5:30'' | **18000** statt 19800 | | ''CET-1CEST,M3.5.0,M10.5.0/3'' | 3600 — Sommerzeitregel bleibt unbeachtet | > **Der Minutenanteil wird verschluckt.** Zonen mit halber oder viertelstündiger Abweichung — Indien (+05:30), Iran (+03:30), Nepal (+05:45), Neufundland (−03:30), Teile Australiens — bekommen einen falschen Versatz, ohne Fehlermeldung ([[https://github.com/SEOLizer/LyX-Compiler/issues/1416|#1416]]). > > **Sommerzeit gibt es nicht.** Eine ''TZ''-Angabe mit Umstellungsregel liefert immer den Standardversatz, unabhängig vom Datum. Wer lokale Zeit anzeigen muss, rechnet den Versatz selbst und behandelt die Umstellung außerhalb dieser Unit. ---- ===== Namen ===== ^ Signatur ^ Wertebereich ^ Ergebnis ^ | ''WeekdayShort(weekday)'' | 0–6, 0 = Sonntag | ''Mon'' | | ''WeekdayLong(weekday)'' | 0–6 | ''Monday'' | | ''MonthShort(month)'' | 1–12 | ''Aug'' | | ''MonthLong(month)'' | 1–12 | ''August'' | Alle vier liefern **englische** Namen und geben einen internen Zeiger zurück — nicht freigeben. Den Wochentag zu einem Zeitstempel liefert ''DayOfWeek(dt)'' als Zahl — damit sind ''WeekdayShort'' und ''WeekdayLong'' ohne Umweg über ''Format'' benutzbar. Für Einzelwerte gibt es ''DayOfWeekYmd(year, month, day)''. ---- ===== Fallstricke ===== * **Alles ist UTC.** Es gibt keine lokale Zeitrechnung; ''TZ'' wirkt nur über ''%z''. * **''DiffSeconds(a, b)'' rechnet ''b − a''** — die umgekehrte Reihenfolge der Namen. * **''FormatDate'' und ''FormatTime'' nehmen Einzelwerte**, keinen Zeitstempel. Wer versehentlich einen Zeitstempel übergibt, bekommt eine Ziffernfolge ohne Fehlermeldung. * **''ToUnixTimestamp'' und ''FromUnixTimestamp'' tun nichts** — der Datetime-Wert ist bereits der Zeitstempel. Sie stehen nur der Lesbarkeit halber da. * **Jahr vierstellig — aber kein Müll mehr.** ''FormatDate'' und die ''%Y''-Ausgabe rechnen mit vier Stellen; Jahre außerhalb 1000–9999 fallen seit [[https://github.com/SEOLizer/LyX-Compiler/issues/1605|#1605]] auf die vollständige Zahl in abweichender Breite zurück, statt Steuerzeichen zu erzeugen. * **Keine Mikrosekunden.** ''%f'' liefert immer ''000000'', ''ToMilliseconds'' multipliziert schlicht mit 1000. * **Ausgabepuffer großzügig bemessen** — keine ''Format…''-Funktion kennt eine Obergrenze und keine prüft, wie viel Platz vorhanden ist. ---- **Weiterführend:** [[lyx_-_programmiersprache:units:time|std.time — Uhr, Schlafen, Messung]] · [[lyx_-_programmiersprache:units:env|std.env]] · [[lyx_-_programmiersprache:units|Standard Library]] **Quelle:** ''std/datetime.lyx'' · **Autor:** Andreas Röne · **Copyright:** 2024–2025 Andreas Röne Letzte Aktualisierung: 2026-08-18 — nachgezogen auf ''lyxc 1.1.2E'': neuer Abschnitt „Zeitstempel bauen und zerlegen" ([[https://github.com/SEOLizer/LyX-Compiler/issues/1601|#1601]]), Platzhalter ''%I'', ''%j'' und ''%%'' sowie negativer Rückgabewert bei unbekanntem Spezifizierer ([[https://github.com/SEOLizer/LyX-Compiler/issues/1602|#1602]]), ''ParseFlexible'' mit vier Formaten und vorab genulltem Ziel ([[https://github.com/SEOLizer/LyX-Compiler/issues/1603|#1603]]), Vorzeichen- und Bereichsprüfung der Dauerausgabe ([[https://github.com/SEOLizer/LyX-Compiler/issues/1605|#1605]]), ''FormatLocale'' lokalisiert wirklich ([[https://github.com/SEOLizer/LyX-Compiler/issues/1415|#1415]]). Vorherige Aktualisierung: 2026-08-13 — Seite gegen ''std/datetime.lyx'' überarbeitet: drei lauffähige Beispiele mit echter Ausgabe (gegen Pythons ''datetime'' geprüft), vollständige Platzhalter-Tabelle für ''Format'', Parser-Erwartungen, Klemmverhalten von ''AddMonths''/''AddYears'', Zeitzonen-Tabelle und Fallstricke. Belegt und als Issue erfasst: ''FormatLocale'' ignoriert den Locale ([[https://github.com/SEOLizer/LyX-Compiler/issues/1415|#1415]]), ''GetTimezoneOffset'' verschluckt den Minutenanteil ([[https://github.com/SEOLizer/LyX-Compiler/issues/1416|#1416]]). Geprüft mit ''lyxc 1.0.21A''.