Pourquoi un EPG affiche la mauvaise heure : horodatages et décalages XMLTV

Un guide décalé d’un nombre entier d’heures n’est presque jamais un mystère. C’est un suffixe qui était présent dans le fichier et qui n’a pas été honoré.

<programme start="20260719150000 +0200" stop="20260719160000 +0200" channel="one.example.com">

Quatorze chiffres, un espace, puis cinq caractères qui décident si cette ligne commence à 15:00 ou à 13:00. Les quatorze chiffres ne sont pas un instant. C’est une heure lue sur une horloge, sans aucun lieu attaché, et un guide des programmes correct dépend soit de la lecture de ces cinq derniers caractères, soit de leur absence interprétée comme l’entendait le grabber qui a écrit le fichier.

Avant de toucher à quoi que ce soit, mesurez le décalage. Si tous les programmes de toutes les chaînes sont décalés du même nombre entier d’heures, il n’y a qu’un seul problème, et c’est presque toujours un décalage qui était présent dans le fichier et qui n’a pas été honoré. Si certaines chaînes sont justes et d’autres fausses, ou si une ligne est décalée alors que celles qui l’encadrent vont bien, c’est une autre panne, et rien de ce qui suit sur les décalages n’y changera quoi que ce soit.

Notre lecteur analyse le guide sur votre appareil, à partir de l’adresse que vous lui donnez. Nous sélectionnons des adresses de guide, et le même analyseur tourne de notre côté sur celles que nous avons sélectionnées, pour le travail de catalogue. Une adresse de guide que vous fournissez est récupérée et lue sur votre machine, et nous ne conservons jamais vos données de guide.

Un guide entièrement décalé et un seul programme faux sont deux bugs différents

Un décalage uniforme a trois causes plausibles, et on les distingue en une minute.

La première : un décalage que le fichier portait et que l’analyseur a jeté. La deuxième : un décalage que le fichier n’a jamais porté, parce que le grabber a écrit des heures locales sans aucun suffixe et que l’analyseur n’avait aucun moyen de le deviner. La troisième : une correction appliquée deux fois, une fois par la source et une fois par vous, celle vers laquelle tout le monde se précipite en premier et qui produit un décalage valant exactement le double de ce qui a été réglé. Si votre guide est passé d’une heure de décalage à trois après que vous avez réglé une correction de deux heures, c’est le troisième cas, et la réponse est de remettre la correction à zéro avant de diagnostiquer quoi que ce soit d’autre.

Le mesurer demande deux comparaisons et une horloge. Prenez un programme diffusé en ce moment et notez de combien son heure de début annoncée s’écarte de la réalité, puis recommencez sur une deuxième chaîne de la même source, puis sur une chaîne d’une autre source. La même erreur sur les deux chaînes d’une source et aucune erreur sur l’autre source, cela réduit le problème au fichier de cette source. La même erreur partout désigne plutôt l’horloge de l’appareil, et là c’est une tout autre réparation.

Un décalage non uniforme n’est pas du tout un problème de fuseau horaire. La cause habituelle, c’est que la ligne de guide est rattachée à la mauvaise chaîne, ce qui fait passer la grille correcte d’une autre station pour une grille fausse de la vôtre. C’est un problème d’identité, et sa forme fait l’objet d’un article séparé. Qu’un identifiant soit présent dans la playlist ne veut pas dire qu’il corresponde à votre guide : dans la plus grande des deux playlists que nous avons mesurées, 527 lignes sur 1 895 se terminaient sans le moindre identifiant exploitable. Les autres causes non uniformes concernent les durées plutôt que les heures de début, et elles sont traitées plus bas.

Un suffixe qui commence par un signe vous a dit que c’est un nombre

Voici la règle qu’applique notre analyseur d’horodatages, et le commentaire qui la surplombe, mot pour mot :

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;
    }

Ce commentaire décrit notre propre bug. Nous nous sommes plantés là-dessus dans une version antérieure de cet analyseur, qui traitait un décalage illisible exactement comme une absence de décalage : on passe, on suppose UTC, on continue. Le symptôme, c’était un guide déplacé de la taille exacte du décalage que le fichier annonçait, sur toutes les chaînes, pour tous les jours du flux, avec un résumé d’import impeccable et rien à regarder dans les logs. La cause, c’était une sortie anticipée manquante. Le coût, c’est que la panne était indétectable depuis l’intérieur de l’application : aucun compteur ne bougeait, aucune ligne de log n’était écrite, et le guide restait faux jusqu’à ce que quelqu’un le compare à une horloge.

Refuser la ligne est pire pour ce programme-là et bien meilleur pour le guide. Un programme ignoré laisse un trou qui se voit. Un flux réinterprété en silence, c’est une grille uniformément fausse et sûre d’elle, et vous aurez beau la fixer, elle ne vous le dira jamais.

Cette règle a un trou, et autant le dire franchement. Le test ne regarde que le premier caractère du suffixe. Un suffixe écrit GMT+0200, avec le libellé avant le signe plutôt qu’après, ne commence pas par un signe : il prend donc le chemin silencieux. Passé dans l’analyseur livré, il renvoie 15:00 UTC pour une heure locale de 15:00, autrement dit le décalage est là, dans la chaîne de caractères, et il est ignoré. Aucun test ne fige cette forme, et nous n’avons pas de corpus XMLTV à nous : l’absence de témoin ne prouve donc pas que le cas soit rare.

Trois formes de décalage sont lues, sept sont refusées

Après le signe, l’analyseur prend tout jusqu’au premier espace et mesure la suite de chiffres. Trois longueurs seulement veulent dire quelque chose : deux caractères pour un décalage en heures seules, quatre pour heures et minutes collées, et cinq quand le troisième caractère est un deux-points. +02, +0200 et +02:00 se résolvent tous aux mêmes deux heures. Tout ce qui fait moins de trois caractères au total est refusé avant même d’atteindre le switch, et toute autre longueur ressort par une branche par défaut.

Les trois formes sont de l’ISO 8601, et c’est la raison pour laquelle les trois sont lues. +0200 est la forme de base, +02:00 la forme étendue, +02 la forme en heures seules, et la DTD XMLTV écrit son propre exemple dans la forme de base, +0300. Bénir une seule forme et prendre discrètement les deux autres pour de l’UTC décalerait d’une heure ou plus tous les flux qui ont choisi autrement.

Deux tolérances entourent tout ça. L’espace avant le signe est facultatif, parce que le suffixe est défini comme tout ce qui suit le quatorzième caractère, donc 20260719150000+0200 marche pareil. Et un libellé de fuseau placé après un vrai décalage est jeté plutôt que traité comme une avarie : +0100 BST est tronqué à l’espace, lu +0100, et donne 14:00 UTC pour une heure locale de 15:00. Les fuseaux à la demi-heure, on les a gratuitement, puisque les minutes sont analysées comme leurs propres deux chiffres : +05:30 se résout correctement.

Cette frontière au quinzième caractère a une conséquence que personne n’a conçue. Le cœur de la date est pris comme exactement les quatorze premiers caractères : un horodatage qui porte un quinzième chiffre, 202607191500000, analyse proprement ses quatorze premiers et passe le 0 orphelin à la lecture du suffixe, qui voit un caractère qui n’est pas un signe et prend le chemin sans décalage. Passée dans l’analyseur, cette valeur renvoie 15:00 UTC et le chiffre en trop est jeté sans un mot. C’est le résultat inoffensif pour un Z final, que le même chemin sans décalage gère et qui veut de toute façon dire UTC, et une troncature silencieuse pour tout le reste. Le cas du Z n’est inoffensif que parce que rien dans l’application livrée ne définit de fuseau supposé, ce qui fera l’objet d’une section à part plus bas.

Ces sept-là sont figées comme des échecs par des tests, et chacune nomme une vraie catégorie d’avarie :

  • +2, une heure à un seul chiffre, trop court pour atteindre le switch
  • +02:0, une forme à deux-points à laquelle il manque un chiffre
  • +0X30, une lettre au milieu de la suite de chiffres
  • +2400, vingt-quatre heures
  • +99:00, quatre-vingt-dix-neuf heures
  • -0270, soixante-dix minutes
  • +, un signe et rien d’autre

Deux garde-fous produisent la plupart de ces refus. Les minutes au-dessus de 59 sont rejetées d’emblée, et l’amplitude totale est plafonnée à 14 heures, parce que le type de date sous-jacent refuse tout ce qui dépasse et que, comme le dit le commentaire du code, “a guide claiming one is corrupt rather than exotic” (un guide qui en annonce un est corrompu, pas exotique). La borne est inclusive : +1400 se résout à une heure après minuit UTC et +1401 échoue. Le test de chiffres exige en plus des chiffres ASCII précisément, et analyse sans aucun style numérique activé, si bien qu’aucun signe de tête, aucun séparateur de milliers et aucun chiffre pleine largeur ne se glisse au travers d’une analyse permissive sensible à la culture.

Deux des trois horodatages d’exemple de la DTD sont refusés par notre analyseur

La DTD ne permet pas seulement la forme à quatorze chiffres. Elle dit qu’une date peut être YYYYMMDDhhmmss ou une sous-chaîne initiale quelconque, et elle imprime trois exemples : 200007281733 BST, 200209 et 19880523083000 +0300. Seul le troisième survit à notre analyseur, en se résolvant à 05:30 UTC. Le deuxième échoue au plancher de longueur, qui refuse tout ce qui fait moins de quatorze caractères avant même de tenter une analyse. Le premier passe ce plancher et échoue quand même, parce que l’analyseur découpe les quatorze premiers caractères comme cœur de date et que 200007281733 B n’est pas une date.

Ce rétrécissement est un choix que nous avons fait, et quand il se déclenche, ce n’est pas la faute du fichier. Un horodatage à la minute n’a pas de secondes à découper, un horodatage année-et-mois n’est un début de programme dans aucun sens utile, et un analyseur qui accepterait l’un ou l’autre inventerait les champs manquants. Les refuser se défend. Les présenter comme une avarie, non, et la dernière ligne du déroulé ci-dessous est du XMLTV conforme que nous ignorons.

Le premier exemple de la DTD porte l’autre moitié de l’asymétrie de cet article dans une seule parenthèse. 200007281733 BST y est annoté (BST == +0100.) : le document qui définit le format résout donc précisément l’abréviation que notre analyseur refuse de résoudre.

Pourquoi +0100 BST est cru et BST seul ne l’est pas

Il y a ici une asymétrie qui a l’air d’un oubli et qui est une décision. Un suffixe +0100 BST se résout à plus une heure, parce que le nombre est là et que le libellé est jeté. Un suffixe BST tout seul se résout à UTC, ce qui est faux d’une heure pour chaque date d’été britannique où il apparaît. Les deux sont figés par des tests, autrement dit les deux sont volontaires.

Les fuseaux nommés ne sont jamais résolus, et le commentaire en donne la raison : “the abbreviations are ambiguous across regions” (les abréviations sont ambiguës d’une région à l’autre). BST, c’est British Summer Time, et cela a aussi été écrit pour Bangladesh Standard Time, cinq heures d’écart. CST est la collision qui survit dans la base tz elle-même, portée à la fois par Chicago, La Havane et Shanghai. Résoudre un nom, c’est en choisir un parmi eux, donc deviner, et une devinette juste sur un marché et fausse sur un autre est pire qu’une règle fausse de la même façon partout.

Nous avons pris la réponse fausse d’une façon connue et fixe plutôt que la devinette, et cela coûte une heure sur les dates d’été dans tout flux qui étiquette ses heures ainsi au lieu de les chiffrer. Le fait que la DTD annote son propre exemple rend ce choix plus difficile à défendre qu’il n’en avait l’air avant l’écriture de cet article : la documentation du format, elle, a bien résolu une abréviation, et le paragraphe ci-dessus est tout ce que nous avons comme argument pour ne toujours pas le faire.

C’est une vraie limite, et nous ne savons pas à quel point cette forme est répandue, parce qu’un nom de fuseau seul est exactement l’entrée qui ne produit aucune erreur, aucun compteur et aucune réclamation jusqu’à ce que quelqu’un compare le guide à une horloge.

La ligne de diagnostic compte les lignes ignorées sans dire pourquoi

Un horodatage qui échoue à l’analyse ne lève pas d’exception. L’attribut n’est jamais affecté, le début reste à zéro, la ligne est refusée au moment de la construction, et un compteur s’incrémente. Ce compteur apparaît dans la boîte de dialogue Réglages EPG de la source, sous “Dernière récupération du guide”, formaté ainsi :

{0} channels · {1} programmes · {2} dropped · {3} ms

C’est le seul relevé que vous obtenez, honnête sur le total et muet sur la raison. Trois conditions sans rapport incrémentent le même nombre : un programme sans attribut de chaîne, un programme dont l’horodatage de début a échoué, et un programme tombé hors de la fenêtre de rétention.

C’est de la fenêtre que vient d’ordinaire un nombre élevé, et il n’y a pas une seule fenêtre. Elle commence toujours 24 heures avant l’instant de l’analyse. Elle se termine huit jours plus tard pour un guide par source, quatre jours plus tard quand le téléchargement décompressé dépasse 200 Mo, et deux jours plus tard pour le guide du foyer, qui garde moins parce qu’il contient toutes les sources à la fois. Un guide qui publie trois semaines de programmes rapportera donc un très grand nombre d’ignorés sur un import parfaitement sain, et le même fichier en rapportera un bien plus grand une fois le guide du foyer activé. Un nombre élevé est une raison de regarder, pas la preuve d’un problème d’heure.

Le plus gênant, c’est que notre mode de défaillance le plus bruyant se résume à un entier dans une boîte de dialogue que personne n’ouvre tant que quelque chose n’a pas déjà l’air cassé.

Un stop cassé vous coûte trois heures, pas un programme

Un décalage illisible sur l’horodatage de début fait ignorer la ligne. Un décalage illisible sur l’horodatage de fin, non.

Quand le début s’analyse et pas la fin, la fin reste à zéro, ce qui échoue au test “la fin est postérieure au début”, et la ligne reçoit une durée par défaut de trois heures. Un plafond séparé limite tout programme à six heures ; il reste inerte ici, puisque trois est sous six, et il ne mord que sur les lignes dont la fin s’analyse bel et bien mais atterrit très loin. Ni la valeur par défaut ni le plafond n’incrémentent le compteur de lignes ignorées. Un fichier dont les attributs de fin sont malformés là où ses attributs de début ne le sont pas produit donc un guide avec des heures de début correctes, des blocs de trois heures partout, des lignes qui se chevauchent, et un résumé d’import impeccable. C’est une forme courante derrière les signalements de guide où les heures ont l’air justes mais où rien ne s’aligne.

Six programmes entrent, quatre ressortent

Voici un fragment construit pour exercer tous les chemins d’un coup. Il a été exécuté contre l’analyseur livré plutôt que déroulé à la main. Chaque identifiant de chaîne utilise un domaine d’exemple réservé et chaque titre est inventé ; supposez que la fenêtre de rétention couvre ces dates, sinon la rétention emporte l’argument avant que les décalages aient leur tour.

<?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>

La ligne un, c’est le cas nominal : 15:00 à plus deux heures font 13:00 UTC, et elle court jusqu’à 14:00 UTC.

La ligne deux porte +02:0 sur les deux attributs. Le suffixe commence par un signe, il doit donc se lire comme un décalage ; la suite de chiffres fait quatre caractères et les deux derniers ne sont pas tous les deux des chiffres, donc elle échoue. Le début n’est jamais affecté et la ligne est refusée, ce qui porte le compteur de lignes ignorées à un.

La ligne trois a un début propre et la même fin cassée. Le début se résout à 15:00 UTC. La fin ne se résout pas, donc la valeur par défaut de trois heures s’applique et la ligne occupe de 15:00 à 18:00 UTC au lieu d’une heure. Rien n’est compté. Cette ligne est la raison pour laquelle le compteur de lignes ignorées n’est pas un indicateur de santé.

La ligne quatre, c’est le cas de l’heure d’écart dans sa forme la plus pure. Le suffixe est BST, qui ne commence pas par un signe : l’analyseur prend donc le chemin sans décalage et applique le fuseau supposé, c’est-à-dire UTC. Le grabber voulait dire 17:00 UTC. Le guide affiche 18:00 UTC. Aucun compteur ne bouge.

La ligne cinq porte le même libellé après un vrai nombre, et se résout correctement à 18:00 UTC. Les lignes quatre et cinq commencent donc toutes les deux à 18:00 UTC sur une même chaîne, et une seule des deux le voulait : voilà à quoi ressemble, vu de l’intérieur, un fichier derrière lequel il y a deux grabbers.

La ligne six fait huit caractères, sous le plancher de quatorze, donc les deux attributs échouent et le compteur de lignes ignorées passe à deux. C’est aussi la ligne que la DTD appellerait légale, une sous-chaîne initiale d’un horodatage complet : la moitié des dégâts comptés dans ce fragment vient donc de notre rétrécissement, pas du fichier.

Quatre lignes ont atteint le stockage, et deux de ces quatre sont fausses d’une manière que rien ne signale : l’une démarre avec une heure de retard, l’autre dure trois fois sa longueur réelle. Le compteur signale les deux autres et ne donne de raison ni pour l’une ni pour l’autre.

Le décalage est résolu par instant, pas une fois par fichier

Le cœur de la date est analysé sans aucun drapeau d’hypothèse, ce qui renvoie une valeur dont le fuseau n’est pas spécifié, et c’est délibéré. L’analyseur l’apparie ensuite explicitement à un décalage. L’alternative, passer une valeur non spécifiée à un constructeur qui comble le trou, fait appliquer par .NET le décalage propre à la machine hôte : le même fichier produirait alors une grille différente sur un appareil à Paris et sur un appareil à Tunis, et le stockage cesserait d’être transportable entre deux appareils d’un même foyer.

Sur le chemin sans décalage, le décalage du fuseau supposé est cherché pour cet instant précis plutôt qu’une fois pour tout le fichier. À cheval sur un changement d’heure de printemps dans un fuseau d’Europe de l’Ouest, deux horodatages séparés de deux heures et demie à l’horloge locale ressortent séparés de quatre-vingt-dix minutes en UTC, ce qui est correct et ce qu’un décalage unique au niveau du fichier raterait pour toutes les lignes postérieures au changement. Les deux heures locales qu’un changement rend inutilisables, l’heure qui a lieu deux fois et l’heure qui n’a jamais lieu, se résolvent toutes les deux au décalage standard, et une réanalyse renvoie la même valeur : un même fichier ne peut donc pas s’afficher comme deux guides différents sur deux imports consécutifs.

Il y a un trou honnête dans cette mécanique. Le paramètre de fuseau supposé existe, les deux chemins qui ingèrent un guide sur l’appareil le passent, et les deux passent null. Rien dans l’application livrée ne le définit jamais, et un test le fige. Un guide qui écrit des heures locales nues, sans suffixe, est lu comme de l’UTC, ce qui est le défaut documenté de la DTD, et il n’existe aucun réglage par source qui change la façon dont il est analysé. Tout ce que dit le paragraphe sur les changements d’heure ci-dessus est de la mécanique qu’aucun chemin de code livré n’atteint aujourd’hui.

La correction horaire est en heures entières, et deux endroits la désactivent

Il existe une correction, et elle travaille à une autre couche. Chaque source porte un décalage horaire en heures entières, de moins 48 à plus 48, modifiable dans cette même boîte de dialogue Réglages EPG sous “Décalage horaire (heures entières)”, avec le champ intitulé “Heures à ajouter”. Elle s’applique quand les programmes sont lus depuis le stockage, pas quand ils y entrent : la changer n’oblige donc à retélécharger quoi que ce soit et ne réécrit pas ce qui a été stocké.

Deux choses qu’elle refuse délibérément de faire. Elle est en heures entières seulement, donc elle ne peut pas corriger un fuseau à la demi-heure, et une source d’une région en +05:30 qui a été lue comme de l’UTC ne peut pas être remise d’aplomb par ce moyen. Et elle est forcée à zéro dans deux cas. Le premier, c’est le guide du foyer, normalisé en UTC à l’ingestion, où il ne reste rien qu’un décalage puisse corriger. Le second, c’est toute chaîne appartenant à une source agrégée, où la chaîne est portée par plusieurs membres à la fois et où le bon décalage dépend du membre qui la sert. Un nombre unique serait faux pour tous sauf un, alors le réglage n’est pas proposé, plutôt que proposé et menteur.

C’est cette dernière décision que je reprendrais en premier si les preuves changeaient. Elle se défend, et elle veut aussi dire que l’issue de secours manque exactement là où un foyer a le plus de sources et le plus de chances que l’une d’elles soit fausse.

Nos applications Windows et Xbox font toute cette analyse localement, sur une adresse de guide que vous avez déjà. La ligne de diagnostic et le réglage de décalage sont dans la même boîte de dialogue pour une raison : le premier nombre vous dit si le fichier est lu tout court, et le second est ce vers quoi vous tendez la main une fois que vous savez qu’il l’est.

Si vous écrivez votre propre analyseur, la règle qui vous sauvera est celle qui nous a pris le plus de temps à apprendre. Un suffixe qui commence par un signe vous a dit que c’est un nombre. Lisez-le ou faites échouer la ligne, mais ne retombez pas sur une valeur par défaut, parce qu’une valeur par défaut s’applique uniformément et avec aplomb à chaque horodatage du fichier, et une erreur uniforme et sûre d’elle est la seule panne que personne ne signale.

Ce que cet article a mesuré28 affirmations, chacune avec les preuves derrière elles
RéclamationPreuveCompté
La définition de type de document XMLTV dit qu’un horodatage sans fuseau horaire explicite doit être lu comme de l’UTC.xmltv.dtd, date field documentation: you can also append a timezone to the end; if no explicit timezone is given, UTC is assumed.SpécificationSans objet
La DTD XMLTV écrit son propre décalage d’exemple dans la forme de base à quatre chiffres, +0300.xmltv.dtd, date field documentation, example 19880523083000 +0300SpécificationSans objet
La DTD XMLTV permet qu’une date soit YYYYMMDDhhmmss ou une sous-chaîne initiale quelconque, et imprime 200007281733 BST, 200209 et 19880523083000 +0300 comme ses trois exemples.xmltv.dtd, date field documentation, including the annotation (BST == +0100.) on the first exampleSpécificationSans objet
ISO 8601 écrit un décalage UTC sous trois formes : de base (+0200), étendue (+02:00) et en heures seules (+02).ISO 8601-1, time offset from UTCSpécificationSans objet
Un DateTimeOffset .NET rejette un décalage dont l’amplitude dépasse 14 heures, et accepte exactement 14 heures..NET DateTimeOffset constructor, offset argument range -14:00 to +14:00 inclusiveSpécificationSans objet
Construire un DateTimeOffset à partir d’un DateTime dont le Kind vaut Unspecified applique le décalage local de la machine hôte..NET DateTimeOffset(DateTime) constructor remarks, which treat an Unspecified Kind as local timeSpécificationSans objet
Les abréviations de fuseau ne sont pas uniques : CST est portée à la fois par America/Chicago, America/Havana et Asia/Shanghai, et BST désigne British Summer Time mais a aussi été écrite pour Bangladesh Standard Time, cinq heures d’écart.IANA time zone database, theory.html on the ambiguity of zone abbreviations, plus the CST abbreviation on America/Chicago, America/Havana and Asia/ShanghaiSpécificationSans objet
Un suffixe d’horodatage commençant par + ou - doit s’analyser comme un décalage numérique, sinon l’horodatage entier échoue. Retomber sur le fuseau supposé est le défaut livré que cette règle a remplacé.n = 222 août 2026
Seules les suites de chiffres de longueur 2, 4 ou 5 avec un deux-points en position 2 sont acceptées, donc +02, +0200 et +02:00 se résolvent tous ; toute autre longueur ressort par la branche par défaut.n = 322 août 2026
Sept formes de décalage sont figées par des tests comme des échecs d’analyse : +2, +02:0, +0X30, +2400, +99:00, -0270 et un + seul.n = 722 août 2026
Les minutes au-dessus de 59 sont rejetées et l’amplitude totale est plafonnée à 14 heures incluses, donc +1400 se résout et +1401 non ; la lecture des deux chiffres exige des chiffres ASCII et analyse avec NumberStyles.None.n = 222 août 2026
Un libellé de fuseau placé après un vrai décalage est jeté au premier espace, donc +0100 BST se résout en +01:00 et une heure locale de 15:00 donne 14:00 UTC.n = 122 août 2026
Un nom de fuseau purement alphabétique n’atteint jamais la lecture du décalage et prend le chemin du fuseau supposé, donc 20260719150000 BST et 20260719150000 UTC se résolvent tous deux à 15:00 UTC.n = 222 août 2026
Deux des trois horodatages d’exemple imprimés par la DTD XMLTV sont refusés par notre analyseur : 200007281733 BST et 200209 échouent tous les deux, et seul 19880523083000 +0300 s’analyse, à 05:30 UTC.n = 322 août 2026
Un horodatage de moins de 14 caractères est refusé avant toute tentative d’analyse, donc une valeur ne portant qu’une date échoue.n = 122 août 2026
Le cœur de la date est exactement les 14 premiers caractères, donc un 15e chiffre orphelin atterrit dans le suffixe, échoue au test du signe, et est jeté en silence.n = 122 août 2026
La règle d’erreur bloquante se déclenche sur le premier caractère du suffixe, donc une forme à libellé en tête comme GMT+0200 prend le chemin du fuseau supposé et est lue comme de l’UTC, décalage ignoré.n = 122 août 2026
Le même compteur de lignes ignorées s’incrémente pour un attribut de chaîne manquant, un début inanalysable et un programme hors de la fenêtre de rétention.n = 322 août 2026
La fenêtre de rétention commence toujours 24 heures avant l’instant de l’analyse et se termine huit jours plus tard sur le chemin par source, quatre jours quand le téléchargement décompressé dépasse 200 Mo, et deux jours pour le guide du foyer.n = 322 août 2026
Un programme avec un début valide et une fin inanalysable est conservé avec une durée par défaut de trois heures et n’est pas compté comme ignoré ; le maximum séparé de six heures ne mord pas dessus.n = 122 août 2026
Le relevé de dernière récupération est formaté en chaînes, programmes, ignorés et millisecondes, sous le titre Dernière récupération du guide.n = 122 août 2026
La correction par source est en heures entières de -48 à +48, intitulée Décalage horaire (heures entières) avec le champ Heures à ajouter, appliquée à la lecture plutôt qu’à l’ingestion.n = 122 août 2026
La correction est forcée à zéro quand le guide du foyer est actif et quand la chaîne appartient à une source agrégée.n = 222 août 2026
Les deux chemins d’ingestion sur l’appareil construisent le pipeline d’analyse avec assumeTimezone à null, donc un horodatage nu est lu comme de l’UTC et le fuseau de la machine hôte n’entre jamais en jeu.n = 222 août 2026
Sur le chemin du fuseau supposé, le décalage est résolu par instant, donc deux heures locales séparées de deux heures et demie à cheval sur un changement d’heure de printemps ressortent séparées de quatre-vingt-dix minutes en UTC.n = 222 août 2026
L’heure locale répétée et l’heure locale inexistante se résolvent toutes deux au décalage standard, et une réanalyse renvoie la même valeur.n = 322 août 2026
Le fragment construit à la main dans cet article donne 1 chaîne, 4 lignes stockées et 2 ignorées ; une ligne stockée démarre avec une heure de retard et une autre dure trois heures au lieu d’une.n = 622 août 2026
Dans la plus grande des deux playlists de notre corpus M3U, 527 lignes sur 1 895 se terminent sans identifiant de chaîne exploitable, si bien qu’une ligne de guide n’a rien de fiable à quoi se rattacher.n = 189522 août 2026