Warum ein EPG die falsche Zeit anzeigt: XMLTV-Zeitstempel und Offsets
Ein Guide, der um volle Stunden danebenliegt, ist fast nie ein Rätsel. Es ist ein Suffix, das in der Datei stand und nicht beachtet wurde.
<programme start="20260719150000 +0200" stop="20260719160000 +0200" channel="one.example.com">
Vierzehn Ziffern, ein Leerzeichen, dann fünf Zeichen, die darüber entscheiden, ob diese Zeile um 15:00 oder um 13:00 beginnt. Die vierzehn Ziffern sind kein Zeitpunkt. Sie sind eine Uhrzeit ohne Ort, und ein korrekter Guide hängt daran, dass diese letzten fünf Zeichen gelesen werden, oder dass ihr Fehlen so gedeutet wird, wie der Grabber es gemeint hat, der die Datei geschrieben hat.
Bevor Sie irgendetwas ändern, messen Sie die Verschiebung. Wenn jede Sendung auf jedem Sender um dieselbe volle Stundenzahl danebenliegt, ist genau eine Sache kaputt, und fast immer ist es ein Offset, der in der Datei stand und nicht beachtet wurde. Wenn manche Sender stimmen und andere nicht, oder wenn eine Zeile danebenliegt und die Zeilen darüber und darunter passen, ist das ein anderer Fehler, und nichts von der Offset-Arbeit weiter unten fasst ihn an.
Unser Player parst den Guide auf Ihrem Gerät, an der Adresse, auf die Sie ihn richten. Wir kuratieren Guide-Adressen, und für die Katalogarbeit läuft derselbe Parser bei uns über die kuratierten Adressen. Eine Guide-Adresse, die Sie selbst angeben, wird auf Ihrem Rechner abgerufen und gelesen, und Ihre Guide-Daten liegen nie bei uns.
Ein verschobener Guide und eine einzelne falsche Sendung sind zwei verschiedene Fehler
Eine gleichmäßige Verschiebung hat drei plausible Ursachen, und die lassen sich in einer Minute auseinanderhalten.
Die erste: Ein Offset stand in der Datei, und der Parser hat ihn weggeworfen. Die zweite: Es stand nie einer drin, der Grabber hat Ortszeiten ohne Suffix geschrieben, und der Parser konnte das nicht wissen. Die dritte: Eine Korrektur wird zweimal angewendet, einmal von der Quelle und einmal von Ihnen. Zu ihr greifen die meisten zuerst, und sie erzeugt genau die doppelte Verschiebung dessen, was eingestellt wurde. Wenn Ihr Guide nach einer Zwei-Stunden-Korrektur von einer Stunde daneben auf drei Stunden daneben gesprungen ist, ist es der dritte Fall, und die Antwort lautet: Korrektur zurück auf null, bevor Sie sonst irgendetwas diagnostizieren.
Zum Messen brauchen Sie zwei Vergleiche und eine Uhr. Nehmen Sie eine Sendung, die gerade läuft, und notieren Sie, wie weit ihr angegebener Beginn von der Wahrheit entfernt ist. Dann dasselbe auf einem zweiten Sender derselben Quelle, dann auf einem Sender einer anderen Quelle. Derselbe Fehler auf beiden Sendern der einen Quelle und kein Fehler bei der anderen: Dann liegt es an der Datei dieser Quelle. Derselbe Fehler überall zeigt auf die Uhr des Geräts, und das ist eine ganz andere Reparatur.
Eine ungleichmäßige Verschiebung ist überhaupt kein Zeitzonenproblem. Meistens hängt die Guide-Zeile am falschen Sender, und dann sieht ein korrektes Programm für irgendeine andere Station aus wie ein falsches Programm für Ihren Sender. Das ist ein Identitätsfehler, und wie er aussieht, steht in einem eigenen Artikel. Eine ID in der Playlist zu haben ist nicht dasselbe wie eine ID, die zu Ihrem Guide passt: In der größeren der beiden von uns vermessenen Playlists endeten 527 von 1.895 Zeilen ganz ohne brauchbare ID. Die übrigen ungleichmäßigen Ursachen betreffen Dauern statt Startzeiten, und die kommen weiter unten.
Ein Suffix, das mit einem Vorzeichen beginnt, hat Ihnen gesagt, dass es eine Zahl ist
Das ist die Regel, die unser Zeitstempel-Parser anwendet, samt dem Kommentar darüber, wörtlich:
var suffix = value[14..].Trim();
if (suffix.Length > 0 && (suffix[0] == '+' || suffix[0] == '-'))
{
// A suffix that looks like a numeric offset must read as one. Falling
// through to the assumed timezone here is what used to shift a whole
// feed by hours with nothing in the logs: the offset was present, said
// +02:00, and was read as UTC.
if (!TryParseOffset(suffix, out var offset))
{
return false;
}
Dieser Kommentar beschreibt unseren eigenen Fehler. In einer früheren Version dieses Parsers haben wir das falsch gemacht: Ein unlesbarer Offset wurde genauso behandelt wie gar kein Offset. Durchfallen, UTC annehmen, weitermachen. Das Symptom war ein Guide, der genau um den Betrag verschoben war, den die Datei selbst angegeben hatte, auf jedem Sender, für jeden Tag im Feed, mit einer sauberen Import-Zusammenfassung und nichts im Log, das man sich hätte ansehen können. Die Ursache war ein fehlendes frühes return. Der Preis: Aus der App heraus war der Fehler nicht zu entdecken. Kein Zähler bewegte sich, keine Log-Zeile wurde geschrieben, und der Guide blieb falsch, bis ihn jemand mit einer Uhr verglichen hat.
Die Zeile abzuweisen ist schlechter für diese eine Sendung und viel besser für den Guide. Eine verworfene Sendung hinterlässt eine Lücke, die man sieht. Ein still umgedeuteter Feed ist ein Programm, das überall gleich und mit voller Überzeugung falsch ist, und man kann es sich stundenlang ansehen, ohne es zu merken.
Diese Regel hat ein Loch, und das sagen wir hier deutlich. Die Prüfung sieht sich nur das erste Zeichen des Suffix an. Ein Suffix wie GMT+0200, mit dem Kürzel vor statt hinter dem Vorzeichen, beginnt nicht mit einem Vorzeichen und nimmt deshalb den leisen Weg. Durch den ausgelieferten Parser geschickt, liefert es 15:00 UTC für 15:00 Ortszeit: Der Offset steht da, in der Zeichenkette, und wird ignoriert. Kein Test nagelt diese Form fest, und wir haben kein eigenes XMLTV-Korpus. Dass uns kein Fall begegnet ist, ist also kein Beleg dafür, dass sie selten wäre.
Drei Offset-Formen werden gelesen, sieben abgewiesen
Hinter dem Vorzeichen nimmt der Parser alles bis zum ersten Leerzeichen und misst die Ziffernfolge. Nur drei Längen bedeuten etwas: zwei Zeichen für einen Offset aus reinen Stunden, vier für Stunden und Minuten am Stück, fünf, wenn das dritte Zeichen ein Doppelpunkt ist. +02, +0200 und +02:00 lösen alle zu denselben zwei Stunden auf. Alles unter drei Zeichen insgesamt wird abgewiesen, bevor das switch überhaupt erreicht wird, und jede andere Länge fällt aus dem Default-Zweig heraus.
Alle drei Formen sind ISO 8601, und genau deshalb werden alle drei gelesen. +0200 ist die Basisform, +02:00 die erweiterte, +02 die mit reinen Stunden, und die XMLTV-DTD schreibt ihr eigenes Beispiel in der Basisform, als +0300. Eine Form zu segnen und die beiden anderen still als UTC zu lesen, würde jeden Feed, der sich anders entschieden hat, um eine Stunde oder mehr verschieben.
Zwei Toleranzen liegen darum herum. Das Leerzeichen vor dem Vorzeichen ist optional, weil das Suffix als alles ab Zeichen fünfzehn definiert ist, 20260719150000+0200 funktioniert also genauso. Und ein Zonenkürzel hinter einem echten Offset wird weggeworfen statt als Schaden behandelt: +0100 BST wird am Leerzeichen abgeschnitten, gelesen wird +0100, und aus 15:00 Ortszeit werden 14:00 UTC. Halbstundenzonen fallen gratis mit ab, weil die Minuten als eigene zwei Ziffern geparst werden, +05:30 löst also korrekt auf.
Diese Grenze bei Zeichen fünfzehn hat eine Folge, die niemand entworfen hat. Der Datumskern sind genau die ersten vierzehn Zeichen. Ein Zeitstempel mit einer fünfzehnten Ziffer, 202607191500000, parst seine ersten vierzehn also sauber und reicht die verirrte 0 an den Suffix-Leser weiter, der ein Zeichen sieht, das kein Vorzeichen ist, und den Weg ohne Offset nimmt. Durch den Parser geschickt, liefert dieser Wert 15:00 UTC, und die überzählige Ziffer verschwindet kommentarlos. Für ein angehängtes Z ist das das harmlose Ergebnis: Dasselbe Durchfallen fängt es ab, und Z bedeutet ohnehin UTC. Für alles andere ist es eine stille Kürzung. Harmlos ist der Z-Fall nur, weil in der ausgelieferten App nichts eine angenommene Zeitzone setzt, und dazu gibt es weiter unten einen eigenen Abschnitt.
Diese sieben sind per Test als Fehlschläge festgenagelt, und jede benennt eine echte Schadensklasse:
+2, eine einstellige Stunde, zu kurz, um das switch zu erreichen+02:0, eine Doppelpunktform, der eine Ziffer fehlt+0X30, ein Buchstabe mitten in der Ziffernfolge+2400, vierundzwanzig Stunden+99:00, neunundneunzig Stunden-0270, siebzig Minuten+, ein Vorzeichen und sonst nichts
Zwei Prüfungen erzeugen die meisten dieser Abweisungen. Minuten über 59 fliegen sofort raus, und der Gesamtbetrag ist bei 14 Stunden gedeckelt, weil der zugrunde liegende Datumstyp alles darüber ablehnt und, wie der Kommentar im Code sagt, ein Guide, der mehr behauptet, „corrupt rather than exotic“ ist (kaputt, nicht exotisch). Die Grenze gilt einschließlich, +1400 löst also zu einer Stunde nach Mitternacht UTC auf und +1401 scheitert. Der Ziffern-Test verlangt außerdem ausdrücklich ASCII-Ziffern und parst ohne jeden aktivierten Zahlenstil, damit kein führendes Vorzeichen, kein Tausendertrennzeichen und keine Ziffer in Vollbreite durch ein großzügiges, kulturabhängiges Parsen rutscht.
Zwei der drei Beispiel-Zeitstempel der DTD weist unser Parser ab
Die DTD erlaubt nicht nur die vierzehnstellige Form. Sie sagt, ein Datum kann YYYYMMDDhhmmss sein oder ein beliebiger Anfangsteil davon, und sie druckt drei Beispiele ab: 200007281733 BST, 200209 und 19880523083000 +0300. Nur das dritte überlebt unseren Parser und löst zu 05:30 UTC auf. Das zweite scheitert an der Mindestlänge, die alles unter vierzehn Zeichen abweist, bevor überhaupt geparst wird. Das erste kommt über diese Hürde und scheitert trotzdem, weil der Parser die ersten vierzehn Zeichen als Datumskern herausschneidet und 200007281733 B kein Datum ist.
Diese Verengung ist unsere Entscheidung, und wenn sie zuschlägt, ist die Datei nicht schuld. Ein Zeitstempel auf die Minute genau hat keine Sekunden zum Herausschneiden, ein Zeitstempel aus Jahr und Monat ist in keinem brauchbaren Sinn ein Sendungsbeginn, und ein Parser, der eines von beidem akzeptierte, würde die fehlenden Felder erfinden. Sie abzuweisen ist vertretbar. Sie als Schaden zu verbuchen nicht, und die letzte Zeile im Durchgang weiter unten ist konformes XMLTV, das wir wegwerfen.
Das erste Beispiel der DTD trägt die andere Hälfte der Asymmetrie dieses Artikels in einer einzigen Klammer. 200007281733 BST ist dort mit (BST == +0100.) annotiert. Das Dokument, das das Format definiert, löst also genau das Kürzel auf, dessen Auflösung unser Parser verweigert.
Warum +0100 BST geglaubt wird und ein nacktes BST nicht
Hier gibt es eine Asymmetrie, die wie ein Versehen aussieht und eine Entscheidung ist. Ein Suffix +0100 BST löst zu plus einer Stunde auf, weil die Zahl da ist und das Kürzel weggeworfen wird. Ein Suffix aus reinem BST löst zu UTC auf, und das ist an jedem britischen Sommertag, an dem es auftaucht, eine Stunde falsch. Beides ist per Test festgenagelt, beides also Absicht.
Benannte Zonen werden nie aufgelöst, und der Kommentar nennt den Grund: „the abbreviations are ambiguous across regions“ (die Abkürzungen sind von Region zu Region mehrdeutig). BST ist British Summer Time, und BST wurde auch für Bangladesh Standard Time geschrieben, fünf Stunden auseinander. CST ist die Kollision, die sich in der tz-Datenbank selbst hält, gleichzeitig getragen von Chicago, Havanna und Shanghai. Einen Namen aufzulösen heißt, sich für eine Variante zu entscheiden, also zu raten, und eine Vermutung, die in einem Markt stimmt und im nächsten danebenliegt, ist schlimmer als eine Regel, die überall auf dieselbe Weise falsch ist.
Wir haben die nach fester Regel berechenbar falsche Antwort dem Raten vorgezogen, und das kostet an Sommerdaten eine Stunde in jedem Feed, der seine Zeiten so beschriftet, statt sie zu beziffern. Dass die DTD ihr eigenes Beispiel annotiert, macht diese Haltung schwerer zu verteidigen, als sie vor dem Schreiben dieses Artikels aussah: Die Dokumentation des Formats hat ein Kürzel aufgelöst, und der Absatz oben ist unser gesamtes Argument dafür, dass wir es weiterhin nicht tun.
Das ist eine echte Einschränkung, und wir wissen nicht, wie häufig diese Form vorkommt: Ein nackter Zonenname ist genau die Eingabe, die keinen Fehler, keinen Zähler und keine Beschwerde erzeugt, bis jemand den Guide mit einer Uhr vergleicht.
Die Diagnosezeile zählt Verworfenes, ohne den Grund zu nennen
Ein Zeitstempel, der sich nicht parsen lässt, wirft keine Exception. Das Attribut wird nie zugewiesen, der Beginn bleibt auf null, die Zeile wird beim Bauen abgewiesen, und ein Zähler zählt hoch. Dieser Zähler taucht im EPG-Einstellungsdialog der Quelle unter „Letzter Guide-Abruf“ auf, formatiert als:
{0} channels · {1} programmes · {2} dropped · {3} ms
Mehr Anzeige bekommen Sie nicht, und sie ist ehrlich bei der Summe und stumm beim Grund. Drei Bedingungen, die nichts miteinander zu tun haben, erhöhen dieselbe Zahl: eine Sendung ohne channel-Attribut, eine Sendung, deren start-Zeitstempel gescheitert ist, und eine Sendung, die aus dem Aufbewahrungsfenster gefallen ist.
Große Zahlen stammen meistens aus diesem Fenster, und es gibt nicht nur eines. Es beginnt immer 24 Stunden vor dem Parse-Zeitpunkt. Es endet acht Tage danach bei einem Guide pro Quelle, vier Tage danach, wenn der entpackte Download 200 MB überschreitet, und zwei Tage danach beim haushaltsweiten Speicher, der weniger hält, weil er alle Quellen auf einmal fasst. Ein Guide mit drei Wochen Programm meldet deshalb bei einem völlig gesunden Import sehr viele Verworfene, und dieselbe Datei meldet noch viel mehr, sobald der haushaltsweite Speicher eingeschaltet ist. Eine hohe Zahl ist ein Grund hinzusehen, kein Beleg für ein Zeitproblem.
Unangenehm daran ist: Unser lautester Fehlerfall ist eine einzelne Zahl in einem Dialog, den niemand öffnet, solange nicht ohnehin schon etwas komisch aussieht.
Ein kaputtes stop kostet Sie drei Stunden, nicht eine Sendung
Ein unlesbarer Offset am start-Zeitstempel wirft die Zeile weg. Ein unlesbarer Offset am stop-Zeitstempel nicht.
Wenn start parst und stop nicht, bleibt stop auf null, fällt damit durch den Test „stop liegt hinter start“, und die Zeile bekommt eine Standarddauer von drei Stunden. Eine separate Deckelung begrenzt jede Sendung auf sechs Stunden; hier bleibt sie wirkungslos, weil drei unter sechs liegt, und sie greift nur bei Zeilen, deren stop zwar parst, aber weit daneben landet. Weder die Standarddauer noch die Deckelung erhöhen den Zähler. Eine Datei, deren stop-Attribute kaputt sind und deren start-Attribute nicht, ergibt also einen Guide mit korrekten Startzeiten, Drei-Stunden-Blöcken überall, überlappenden Zeilen und einer sauberen Import-Zusammenfassung. Das ist eine häufige Form hinter Meldungen über einen Guide, bei dem die Zeiten stimmen, aber nichts zusammenpasst.
Sechs Sendungen gehen rein, vier kommen raus
Hier ist ein Fragment, gebaut, um jeden Weg auf einmal durchzuspielen. Es wurde gegen den ausgelieferten Parser ausgeführt, nicht von Hand nachvollzogen. Jede Sender-ID nutzt eine reservierte Beispiel-Domain, jeder Titel ist erfunden, und nehmen Sie an, dass das Aufbewahrungsfenster diese Datumsangaben abdeckt, sonst räumt die Aufbewahrung das Argument ab, bevor die Offsets an die Reihe kommen.
<?xml version="1.0" encoding="UTF-8"?>
<tv>
<channel id="one.example.com"><display-name>Example One</display-name></channel>
<programme start="20260719150000 +0200" stop="20260719160000 +0200" channel="one.example.com">
<title>Clean offset</title>
</programme>
<programme start="20260719160000 +02:0" stop="20260719170000 +02:0" channel="one.example.com">
<title>Broken on both ends</title>
</programme>
<programme start="20260719170000 +02:00" stop="20260719180000 +02:0" channel="one.example.com">
<title>Broken on the stop only</title>
</programme>
<programme start="20260719180000 BST" stop="20260719190000 BST" channel="one.example.com">
<title>Zone name, no number</title>
</programme>
<programme start="20260719190000 +0100 BST" stop="20260719200000 +0100 BST" channel="one.example.com">
<title>Zone name after a number</title>
</programme>
<programme start="20260719" stop="20260720" channel="one.example.com">
<title>Date only</title>
</programme>
</tv>
Zeile eins ist der Idealfall: 15:00 bei plus zwei Stunden sind 13:00 UTC, und sie läuft bis 14:00 UTC.
Zeile zwei hat +02:0 auf beiden Attributen. Das Suffix beginnt mit einem Vorzeichen, muss sich also als Offset lesen lassen; die Ziffernfolge ist vier Zeichen lang, und die letzten beiden sind nicht beide Ziffern, also scheitert sie. Der Beginn wird nie zugewiesen, die Zeile wird abgewiesen, und der Zähler steht auf eins.
Zeile drei hat einen sauberen Beginn und dasselbe kaputte stop. Der Beginn löst zu 15:00 UTC auf. Das stop-Attribut löst nicht auf, also greift die Standarddauer von drei Stunden, und die Zeile belegt 15:00 bis 18:00 UTC statt einer Stunde. Gezählt wird nichts. Diese Zeile ist der Grund, warum der Zähler keine Gesundheitsmetrik ist.
Zeile vier ist der Ein-Stunden-Fall in Reinform. Das Suffix ist BST, es beginnt nicht mit einem Vorzeichen, der Parser nimmt also den Weg ohne Offset und wendet die angenommene Zeitzone an, und die ist UTC. Der Grabber meinte 17:00 UTC. Der Guide zeigt 18:00 UTC. Kein Zähler bewegt sich.
Zeile fünf trägt dasselbe Kürzel hinter einer echten Zahl und löst korrekt zu 18:00 UTC auf. Die Zeilen vier und fünf beginnen damit beide um 18:00 UTC auf einem Sender, und nur eine von beiden wollte das. So sieht von innen eine Datei aus, hinter der zwei Grabber stecken.
Zeile sechs hat acht Zeichen, liegt unter der Mindestlänge von vierzehn, also scheitern beide Attribute, und der Zähler steht auf zwei. Sie ist zugleich die Zeile, die die DTD als zulässig ansehen würde, ein Anfangsteil eines vollen Zeitstempels. Die Hälfte des gezählten Schadens in diesem Fragment geht damit auf unsere Verengung zurück und nicht auf die Datei.
Vier Zeilen haben es in den Speicher geschafft, und zwei davon sind auf eine Weise falsch, die nichts meldet: Eine beginnt eine Stunde zu spät, eine läuft dreimal so lang wie in Wirklichkeit. Der Zähler meldet die anderen zwei und nennt für keine von beiden einen Grund.
Der Offset wird pro Zeitpunkt aufgelöst, nicht einmal pro Datei
Der Datumskern wird ohne jedes Annahme-Flag geparst, was einen Wert liefert, dessen Kind auf Unspecified steht, und das ist Absicht. Der Parser paart ihn danach ausdrücklich mit einem Offset. Die Alternative wäre, einen unspezifizierten Wert an einen Konstruktor zu geben, der die Lücke selbst füllt. Dann wendet .NET den eigenen Offset des Rechners an, dieselbe Datei ergäbe auf einem Gerät in Paris ein anderes Programm als auf einem Gerät in Tunis, und der Speicher wäre zwischen zwei Geräten in einem Haushalt nicht mehr portabel.
Auf dem Weg ohne Offset wird der Offset der angenommenen Zone für genau diesen Zeitpunkt nachgeschlagen, nicht einmal für die ganze Datei. Über eine Frühjahrsumstellung in einer westeuropäischen Zone hinweg liegen zwei Zeitstempel, die auf der lokalen Uhr zweieinhalb Stunden auseinander liegen, in UTC neunzig Minuten auseinander. Das ist richtig, und genau das würde ein einziger Offset auf Dateiebene für jede Zeile nach der Umstellung falsch machen. Die beiden Ortszeiten, die eine Umstellung unbrauchbar macht, die Stunde, die zweimal kommt, und die Stunde, die nie kommt, lösen beide zum Standard-Offset auf, und ein erneutes Parsen liefert denselben Wert. Eine Datei kann also nicht bei zwei aufeinanderfolgenden Importen zwei verschiedene Guides ergeben.
In dieser Mechanik klafft eine Lücke, und wir sagen sie hier offen. Den Parameter für die angenommene Zeitzone gibt es, beide Wege, die auf dem Gerät einen Guide importieren, übergeben ihn, und beide übergeben null. Nichts in der ausgelieferten App setzt ihn je, und ein Test nagelt das fest. Ein Guide, der nackte Ortszeiten ohne Suffix schreibt, wird als UTC gelesen, so wie es die DTD dokumentiert, und es gibt keine Einstellung pro Quelle, die daran etwas ändert. Alles im Absatz über die Umstellung ist Mechanik, die derzeit kein ausgelieferter Codepfad erreicht.
Die Stundenkorrektur geht in ganzen Stunden, und an zwei Stellen ist sie aus
Es gibt eine Korrektur, und sie arbeitet auf einer anderen Ebene. Jede Quelle trägt eine Zeitverschiebung in ganzen Stunden, von minus 48 bis plus 48, änderbar in demselben EPG-Einstellungsdialog unter „Zeitverschiebung (ganze Stunden)“ mit dem Feld „Hinzuzufügende Stunden“. Sie greift, wenn Sendungen aus dem Speicher gelesen werden, nicht wenn sie hineingehen. Sie zu ändern verlangt also keinen neuen Download und schreibt nichts um, was schon gespeichert ist.
Zwei Dinge tut sie mit Absicht nicht. Sie kennt nur ganze Stunden, kann also keine Halbstundenzone korrigieren: Eine Quelle aus einer +05:30-Region, die als UTC gelesen wurde, holen Sie damit nicht zurück. Und in zwei Fällen wird sie auf null gezwungen. Der erste ist der haushaltsweite Guide-Speicher, der beim Import auf UTC normalisiert wird und dem nichts mehr bleibt, was eine Verschiebung korrigieren könnte. Der zweite ist jeder Sender, der zu einer aggregierten Quelle gehört: Dahinter stehen mehrere Mitglieder gleichzeitig, und der richtige Offset hängt davon ab, welches gerade ausliefert. Eine einzige Zahl wäre für alle bis auf eines falsch, also bieten wir den Regler gar nicht erst an, statt ihn anzubieten und zu lügen.
Diese letzte Entscheidung würde ich als Erstes neu aufrollen, wenn sich die Faktenlage ändert. Sie ist vertretbar, und sie bedeutet zugleich, dass der Notausgang genau dort fehlt, wo ein Haushalt die meisten Quellen hat und damit die meisten Gelegenheiten, dass eine davon danebenliegt.
Unsere Apps für Windows und Xbox erledigen dieses ganze Parsen lokal, an einer Guide-Adresse, die Sie schon haben. Die Diagnosezeile und der Regler für die Verschiebung sitzen aus einem Grund im selben Dialog: Die erste Zahl sagt Ihnen, ob die Datei überhaupt gelesen wird, und zur zweiten greifen Sie, sobald Sie wissen, dass sie es wird.
Wenn Sie Ihren eigenen Parser schreiben: Die Regel, die Sie rettet, ist die, für die wir am längsten gebraucht haben. Ein Suffix, das mit einem Vorzeichen beginnt, hat Ihnen gesagt, dass es eine Zahl ist. Lesen Sie es, oder lassen Sie die Zeile scheitern, aber fallen Sie nicht auf einen Standard durch. Ein Standard wird gleichmäßig und mit voller Überzeugung auf jeden Zeitstempel in der Datei angewendet, und ein Fehler, der überall gleich und mit voller Überzeugung auftritt, ist genau der, den niemand meldet.
Was dieser Artikel gemessen hat28 Behauptungen, jeweils mit den Beweisen dahinter
| Anspruch | Beweise | Gezählt |
|---|---|---|
| Die XMLTV-Dokumenttypdefinition sagt: Ein Zeitstempel ohne ausdrückliche Zeitzone ist als UTC zu lesen.xmltv.dtd, date field documentation: you can also append a timezone to the end; if no explicit timezone is given, UTC is assumed. | Spezifikation | Nicht zutreffend |
| Die XMLTV-DTD schreibt ihren eigenen Beispiel-Offset in der vierstelligen Basisform, als +0300.xmltv.dtd, date field documentation, example 19880523083000 +0300 | Spezifikation | Nicht zutreffend |
| Die XMLTV-DTD erlaubt als Datum YYYYMMDDhhmmss oder einen beliebigen Anfangsteil davon und druckt 200007281733 BST, 200209 und 19880523083000 +0300 als ihre drei Beispiele ab.xmltv.dtd, date field documentation, including the annotation (BST == +0100.) on the first example | Spezifikation | Nicht zutreffend |
| ISO 8601 schreibt einen UTC-Offset in drei Formen: Basis (+0200), erweitert (+02:00) und nur Stunden (+02).ISO 8601-1, time offset from UTC | Spezifikation | Nicht zutreffend |
| Ein .NET-DateTimeOffset lehnt einen Offset ab, dessen Betrag 14 Stunden überschreitet, und akzeptiert genau 14 Stunden..NET DateTimeOffset constructor, offset argument range -14:00 to +14:00 inclusive | Spezifikation | Nicht zutreffend |
| Ein DateTimeOffset aus einem DateTime zu bauen, dessen Kind Unspecified ist, übernimmt den lokalen Offset des Rechners..NET DateTimeOffset(DateTime) constructor remarks, which treat an Unspecified Kind as local time | Spezifikation | Nicht zutreffend |
| Zonenkürzel sind nicht eindeutig: CST tragen America/Chicago, America/Havana und Asia/Shanghai gleichzeitig, und BST steht für British Summer Time, wurde aber auch für Bangladesh Standard Time geschrieben, fünf Stunden auseinander.IANA time zone database, theory.html on the ambiguity of zone abbreviations, plus the CST abbreviation on America/Chicago, America/Havana and Asia/Shanghai | Spezifikation | Nicht zutreffend |
| Ein Zeitstempel-Suffix, das mit + oder - beginnt, muss sich als numerischer Offset lesen lassen, sonst scheitert der ganze Zeitstempel. Das Durchfallen zur angenommenen Zeitzone war der ausgelieferte Fehler, den diese Regel ersetzt hat. | n = 2 | 22.08.2026 |
| Akzeptiert werden nur Ziffernfolgen der Länge 2, 4 oder 5 mit Doppelpunkt an Index 2, also lösen +02, +0200 und +02:00 alle auf; jede andere Länge fällt aus dem Default-Zweig heraus. | n = 3 | 22.08.2026 |
| Sieben Offset-Formen sind per Test als Parse-Fehler festgenagelt: +2, +02:0, +0X30, +2400, +99:00, -0270 und ein nacktes +. | n = 7 | 22.08.2026 |
| Minuten über 59 werden abgelehnt, und der Gesamtbetrag ist bei einschließlich 14 Stunden gedeckelt, also löst +1400 auf und +1401 nicht; der Leser für die zwei Ziffern verlangt ASCII-Ziffern und parst mit NumberStyles.None. | n = 2 | 22.08.2026 |
| Ein Zonenkürzel hinter einem echten Offset wird am ersten Leerzeichen abgeschnitten, also löst +0100 BST zu +01:00 auf, und aus 15:00 Ortszeit werden 14:00 UTC. | n = 1 | 22.08.2026 |
| Ein reiner Zonenname aus Buchstaben erreicht den Offset-Leser nie und nimmt den Weg über die angenommene Zeitzone, also lösen 20260719150000 BST und 20260719150000 UTC beide zu 15:00 UTC auf. | n = 2 | 22.08.2026 |
| Zwei der drei Beispiel-Zeitstempel, die die XMLTV-DTD abdruckt, weist unser Parser ab: 200007281733 BST und 200209 scheitern beide, nur 19880523083000 +0300 wird geparst, zu 05:30 UTC. | n = 3 | 22.08.2026 |
| Ein Zeitstempel mit weniger als 14 Zeichen wird abgewiesen, bevor überhaupt geparst wird, ein reiner Datumswert scheitert also. | n = 1 | 22.08.2026 |
| Der Datumskern sind genau die ersten 14 Zeichen, eine verirrte 15. Ziffer landet also im Suffix, fällt beim Vorzeichentest durch und verschwindet stillschweigend. | n = 1 | 22.08.2026 |
| Die Regel für den harten Fehler hängt am ersten Zeichen des Suffix, eine Form mit Kürzel zuerst wie GMT+0200 nimmt deshalb den Weg über die angenommene Zeitzone und wird als UTC gelesen, der Offset bleibt liegen. | n = 1 | 22.08.2026 |
| Derselbe Zähler für Verworfenes zählt hoch bei einem fehlenden channel-Attribut, bei einem nicht parsbaren start und bei einer Sendung außerhalb des Aufbewahrungsfensters. | n = 3 | 22.08.2026 |
| Das Aufbewahrungsfenster beginnt immer 24 Stunden vor dem Parse-Zeitpunkt und endet acht Tage danach auf dem Weg pro Quelle, vier Tage danach, wenn der entpackte Download 200 MB überschreitet, und zwei Tage danach beim haushaltsweiten Speicher. | n = 3 | 22.08.2026 |
| Eine Sendung mit gültigem start und nicht parsbarem stop bleibt erhalten, bekommt drei Stunden Standarddauer und zählt nicht als verworfen; das getrennte Maximum von sechs Stunden greift dabei nicht. | n = 1 | 22.08.2026 |
| Die Anzeige zum letzten Abruf ist als Sender, Sendungen, verworfen und Millisekunden formatiert, unter der Überschrift Letzter Guide-Abruf. | n = 1 | 22.08.2026 |
| Die Korrektur pro Quelle geht in ganzen Stunden von -48 bis +48, heißt Zeitverschiebung (ganze Stunden) mit dem Feld Hinzuzufügende Stunden und greift beim Lesen, nicht beim Import. | n = 1 | 22.08.2026 |
| Die Korrektur wird auf null gezwungen, wenn der haushaltsweite Speicher aktiv ist und wenn der Sender zu einer aggregierten Quelle gehört. | n = 2 | 22.08.2026 |
| Beide Import-Wege auf dem Gerät bauen die Parse-Pipeline mit assumeTimezone null, ein nackter Zeitstempel wird also als UTC gelesen, und die Zeitzone des Rechners kommt nie ins Spiel. | n = 2 | 22.08.2026 |
| Auf dem Weg über die angenommene Zeitzone wird der Offset pro Zeitpunkt aufgelöst, zwei Ortszeiten mit zweieinhalb Stunden Abstand über eine Frühjahrsumstellung hinweg liegen in UTC daher neunzig Minuten auseinander. | n = 2 | 22.08.2026 |
| Die Stunde, die es lokal zweimal gibt, und die Stunde, die es lokal nicht gibt, lösen beide zum Standard-Offset auf, und ein erneutes Parsen liefert denselben Wert. | n = 3 | 22.08.2026 |
| Das von Hand gebaute Fragment in diesem Artikel ergibt 1 Sender, 4 gespeicherte Zeilen und 2 Verworfene; eine gespeicherte Zeile beginnt eine Stunde zu spät, und eine läuft drei Stunden statt einer. | n = 6 | 22.08.2026 |
| In der größeren der beiden Playlists unseres M3U-Korpus enden 527 von 1.895 Zeilen ohne brauchbare Sender-ID, eine Guide-Zeile hat sich also an nichts Verlässliches zu hängen. | n = 1895 | 22.08.2026 |