EPG neden yanlış saati gösterir: XMLTV zaman damgaları ve ofsetler

Tam saat katları kadar şaşan bir rehber neredeyse hiçbir zaman gizem değildir. Dosyada duran ama hesaba katılmayan bir sonektir.

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

On dört rakam, bir boşluk, sonra o satırın 15:00'te mi yoksa 13:00'te mi başladığını belirleyen beş karakter. On dört rakam bir an değildir. Hiçbir yere bağlanmamış bir duvar saati okumasıdır; doğru bir program rehberi de ya bu son beş karakterin okunmasına ya da yokluklarının, dosyayı yazan grabber'ın kastettiği gibi yorumlanmasına bağlıdır.

Bir şeyi değiştirmeden önce kaymayı ölçün. Her kanaldaki her program aynı tam saat kadar şaşıyorsa tek bir şey bozuktur ve bu neredeyse her zaman dosyada duran ama hesaba katılmayan bir ofsettir. Bazı kanallar doğru, bazıları yanlışsa ya da bir satır şaşmış, iki yanındaki satırlar yerindeyse bu başka bir arızadır ve aşağıdaki ofset işlerinin hiçbiri ona dokunmaz.

Oynatıcımız rehberi sizin cihazınızda, gösterdiğiniz adres üzerinden ayrıştırır. Rehber adreslerini biz derliyoruz ve katalog işleri için aynı ayrıştırıcı bizim tarafımızda da derlenmiş adresler üzerinde çalışıyor. Sizin verdiğiniz bir rehber adresi ise sizin makinenizde indirilip okunur; rehber verinizi biz hiçbir zaman tutmayız.

Rehberin tamamının kayması ile tek bir yanlış program ayrı hatalardır

Tekdüze bir kaymanın üç makul sebebi var ve bunları bir dakikada birbirinden ayırabilirsiniz.

Birincisi, dosyanın taşıdığı ama okuyucunun attığı bir ofset. İkincisi, dosyanın hiç taşımadığı bir ofset: grabber yerel duvar saati değerlerini soneksiz yazmıştır ve okuyucunun bunu bilmesinin bir yolu yoktur. Üçüncüsü, biri kaynakta biri sizde olmak üzere iki kez uygulanan bir düzeltme. İnsanların ilk elini attığı ayar budur ve ayarladıkları değerin tam iki katı kadar kayma üretir. İki saatlik düzeltmeyi ayarladıktan sonra rehberiniz bir saat şaşmaktan üç saat şaşmaya geçtiyse üçüncü durumdasınız; yapılacak şey, başka hiçbir şeyi teşhis etmeden önce düzeltmeyi sıfıra geri almaktır.

Ölçmek için iki karşılaştırma ile bir saat yeter. Şu anda yayında olan bir program seçin, yazan başlangıç saatinin gerçekten ne kadar uzakta olduğunu not edin; sonra aynı kaynağın ikinci bir kanalında, sonra da başka bir kaynağın kanalında tekrarlayın. Bir kaynağın iki kanalında da aynı hata varsa ve diğer kaynakta hata yoksa iş o kaynağın dosyasına daralır. Hata her yerde aynıysa suç cihazın saatindedir ve bu bambaşka bir tamir işidir.

Tekdüze olmayan bir kayma zaten saat dilimi sorunu değildir. Alışıldık sebep, rehber satırının yanlış kanala bağlanmış olmasıdır: başka bir istasyonun doğru yayın akışı, sizinkinin yanlış akışı gibi görünür. Bu bir kimlik arızasıdır ve nasıl göründüğü ayrı bir yazının konusu. Çalma listesinde bir kimliğin bulunması, o kimliğin rehberinizle eşleşmesiyle aynı şey değildir: ölçtüğümüz iki çalma listesinin büyüğünde 1.895 satırın 527'si hiç kullanılabilir kimlik taşımadan bitiyordu. Tekdüze olmayan diğer sebepler başlangıç saatleri değil süreler; onları aşağıda ele alıyoruz.

İşaretle başlayan bir sonek, size sayı olduğunu zaten söylemiştir

Zaman damgası okuyucumuzun uyguladığı kural ve tam üstünde duran yorum, harfi harfine:

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

O yorum bizim kendi hatamızı anlatıyor. Bu okuyucunun daha eski bir sürümünde bunu yanlış yaptık: okunamayan bir ofsete, hiç ofset yokmuş gibi davranıyordu. Alta düş, UTC varsay, devam et. Belirti, dosyanın yazdığı ofsetin tam boyu kadar kaymış bir rehberdi; her kanalda, akıştaki her gün için, üstelik tertemiz bir içe aktarma özeti ve bakılacak tek bir log satırı bile olmadan. Sebep, eksik kalan tek bir erken return'dü. Bedeli ise arızanın uygulamanın içinden hiç fark edilememesiydi: hiçbir sayaç kıpırdamadı, hiçbir log satırı yazılmadı ve biri rehberi bir saatin karşısına tutana kadar rehber yanlış kaldı.

Satırı geri çevirmek o tek program için daha kötü, rehberin tamamı için çok daha iyidir. Atlanan bir program gözle görülür bir boşluk bırakır. Sessizce yeniden yorumlanmış bir akış ise kendinden emin ve baştan sona yanlış bir yayın akışıdır; ne kadar bakarsanız bakın bunu size söylemez.

Bu kuralın bir deliği var ve açıkça söylemekte fayda var. Kontrol yalnızca sonekin ilk karakterine bakıyor. Etiketi işaretten sonra değil önce yazan GMT+0200 gibi bir sonek işaretle başlamadığı için sessiz yoldan gidiyor. Sürüme çıkmış ayrıştırıcıdan geçirdiğinizde yerel saatle 15:00 için 15:00 UTC döndürüyor; yani ofset dizenin içinde öylece duruyor ve yok sayılıyor. Bu biçimi sabitleyen bir test yok, kendimize ait bir XMLTV korpusumuz da yok; dolayısıyla tanığın olmaması, bunun nadir olduğunun kanıtı değil.

Üç ofset biçimi okunuyor, yedisi geri çevriliyor

İşaretten sonra okuyucu ilk boşluğa kadar olan her şeyi alıp rakam dizisinin uzunluğunu ölçüyor. Yalnızca üç uzunluk bir anlam taşıyor: yalnızca saat için iki karakter, saat ile dakikanın bitişik yazıldığı hâl için dört, üçüncü karakter iki nokta üst üste olduğunda beş. +02, +0200 ve +02:00 aynı iki saate çözülüyor. Toplamda üç karakterin altındaki her şey switch'e varmadan geri çevriliyor, diğer bütün uzunluklar da öntanımlı daldan düşüyor.

Üç biçim de ISO 8601; üçünün de okunmasının sebebi bu. +0200 temel biçim, +02:00 genişletilmiş biçim, +02 yalnızca saat biçimi; XMLTV DTD de kendi örneğini +0300 diye temel biçimde yazıyor. Bir biçimi kutsayıp diğer ikisini sessizce UTC saymak, başka türlü yazmayı seçmiş her akışı bir saat ya da daha fazla şaşırtırdı.

Bunun etrafında iki tolerans var. İşaretten önceki boşluk isteğe bağlı, çünkü sonek on beşinci karakterden sonrasının tamamı diye tanımlı; yani 20260719150000+0200 da aynı şekilde çalışıyor. Gerçek bir ofsetin ardından gelen saat dilimi etiketi de hasar sayılmıyor, atılıyor: +0100 BST boşlukta kesiliyor, +0100 okunuyor ve yerel saatle 15:00 için 14:00 UTC veriyor. Buçuklu saat dilimleri de bedavaya geliyor, çünkü dakikalar kendi iki hanesi olarak ayrıştırılıyor; +05:30 doğru çözülüyor.

On beşinci karakter sınırının kimsenin tasarlamadığı bir sonucu var. Tarih çekirdeği tam olarak ilk on dört karakter alınıyor; bu yüzden on beşinci bir rakam taşıyan 202607191500000 gibi bir zaman damgası ilk on dördünü sorunsuz ayrıştırıyor ve başıboş 0'ı sonek okuyucusuna veriyor, o da işaret olmayan bir karakter görüp ofsetsiz yoldan gidiyor. Ayrıştırıcıdan geçirdiğinizde bu değer 15:00 UTC döndürüyor ve fazladan rakam tek kelime edilmeden atılıyor. Sondaki bir Z için bu zararsız bir sonuç, çünkü aynı düşüş onu da karşılıyor ve Z zaten UTC demek; başka her şey içinse sessiz bir kırpma. Z durumunun zararsız olmasının tek sebebi, sürüme çıkmış uygulamada varsayılan saat dilimini kimsenin ayarlamıyor olması; aşağıda bunun kendi bölümü var.

Şu yedisi testlerle başarısızlık olarak sabitlenmiş ve her biri gerçek bir hasar sınıfına karşılık geliyor:

  • +2, tek haneli saat, switch'e varamayacak kadar kısa
  • +02:0, bir hanesi eksik iki noktalı biçim
  • +0X30, rakam dizisinin içinde bir harf
  • +2400, yirmi dört saat
  • +99:00, doksan dokuz saat
  • -0270, yetmiş dakika
  • +, bir işaret ve başka hiçbir şey

Bu geri çevirmelerin çoğunu iki koruma üretiyor. 59'un üzerindeki dakikalar doğrudan reddediliyor ve toplam büyüklük 14 saatte kesiliyor; çünkü altta yatan tarih tipi bunun ötesindeki hiçbir şeyi kabul etmiyor ve kod yorumunun deyişiyle, "a guide claiming one is corrupt rather than exotic": böyle bir iddiada bulunan rehber egzotik değil, bozuktur. Sınır dahil, yani +1400 UTC'de gece yarısını bir saat geçeye çözülüyor, +1401 başarısız oluyor. Hane testi ayrıca özellikle ASCII rakam istiyor ve hiçbir sayı stili açık olmadan ayrıştırıyor; böylece hoşgörülü, kültüre duyarlı bir ayrıştırmadan baştaki bir işaret, bir binlik ayırıcı ya da tam genişlikte bir rakam sızamıyor.

DTD'nin üç örnek zaman damgasından ikisini okuyucumuz geri çeviriyor

DTD yalnızca on dört haneli biçime izin vermiyor. Bir tarihin YYYYMMDDhhmmss ya da bunun baştan başlayan bir parçası olabileceğini söylüyor ve üç örnek yazıyor: 200007281733 BST, 200209 ve 19880523083000 +0300. Ayrıştırıcımızdan yalnızca üçüncüsü sağ çıkıyor ve 05:30 UTC'ye çözülüyor. İkincisi uzunluk tabanına takılıyor; o taban, ayrıştırma denenmeden önce on dört karakterden kısa olan her şeyi geri çeviriyor. Birincisi tabanı geçiyor ama yine de başarısız oluyor, çünkü okuyucu ilk on dört karakteri tarih çekirdeği diye dilimliyor ve 200007281733 B bir tarih değil.

Bu daraltma bizim yaptığımız bir tercih ve devreye girdiğinde suç dosyada değil. Dakika hassasiyetli bir zaman damgasında dilimlenecek saniye yok, yıl ile aydan ibaret bir zaman damgası da işe yarar hiçbir anlamda program başlangıcı değil; ikisini de kabul eden bir okuyucu eksik alanları uyduruyor olurdu. Bunları geri çevirmek savunulabilir. Hasar diye sunmak değil; aşağıdaki adım adım incelemede son satır, bizim attığımız ama kurallara tamamen uyan XMLTV.

DTD'nin ilk örneği, bu yazıdaki asimetrinin diğer yarısını tek bir parantezde taşıyor. 200007281733 BST orada (BST == +0100.) diye açıklanmış; yani biçimi tanımlayan belge, okuyucumuzun çözmeyi reddettiği kısaltmanın ta kendisini çözüyor.

+0100 BST'ye neden inanıyoruz da tek başına BST'ye inanmıyoruz

Burada gözden kaçmış gibi duran ama aslında karar olan bir asimetri var. +0100 BST soneki artı bir saate çözülüyor, çünkü sayı orada duruyor ve etiket çöpe gidiyor. Tek başına BST soneki UTC'ye çözülüyor; bu da göründüğü her İngiliz yaz tarihinde bir saat yanlış demek. İkisi de testlerle sabitlenmiş, yani ikisi de kasıtlı.

Adlandırılmış saat dilimleri hiçbir zaman çözülmüyor ve kod yorumu sebebini söylüyor: "the abbreviations are ambiguous across regions", yani kısaltmalar bölgeden bölgeye belirsiz. BST hem British Summer Time, hem de arada beş saat olmasına rağmen Bangladesh Standard Time için yazılmış. CST ise tz veritabanının kendi içinde ayakta kalan çakışma: aynı anda Chicago, Havana ve Şanghay taşıyor. Bir adı çözmek, bunlardan birini seçmek, yani tahmin etmek demek; bir pazarda doğru, diğerinde yanlış olan bir tahmin ise her yerde aynı şekilde yanlış olan bir kuraldan daha kötü.

Biz tahmin yerine, sabit bir kuralla öngörülebilir biçimde yanlış olan cevabı seçtik ve bu, saatlerini sayıyla yazmak yerine böyle etiketleyen her akışta yaz tarihlerinde bir saate mal oluyor. DTD'nin kendi örneğini açıklamış olması, bu tercihi bu yazı yazılmadan önce göründüğünden daha zor savunulur hâle getiriyor: biçimin kendi belgesi bir kısaltmayı çözmüş, yukarıdaki paragraf da bizim hâlâ çözmüyor oluşumuzun bütün gerekçesi.

Bu gerçek bir kısıt ve bu biçimin ne kadar yaygın olduğunu bilmiyoruz; çünkü tek başına bir saat dilimi adı, tam olarak hata da sayaç da şikâyet de üretmeyen türden bir girdi, ta ki biri rehberi bir saatle karşılaştırana kadar.

Tanılama satırı atlananları sayıyor ama sebebini söylemiyor

Ayrıştırılamayan bir zaman damgası istisna fırlatmıyor. Öznitelik hiç atanmıyor, başlangıç sıfırda kalıyor, satır kurulma anında geri çevriliyor ve bir sayaç artıyor. O sayaç, kaynağın rehber ayarları penceresinde "Son rehber getirme" başlığı altında şu biçimde görünüyor:

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

Elinize geçen tek gösterge bu; toplam konusunda dürüst, sebep konusunda suskun. Birbiriyle ilgisiz üç durum aynı sayıyı artırıyor: channel özniteliği olmayan bir program, start zaman damgası başarısız olan bir program ve saklama penceresinin dışına düşen bir program.

Büyük bir sayı genellikle bu pencereden geliyor ve tek bir pencere de değil. Her zaman ayrıştırma anının 24 saat gerisinden başlıyor. Kaynak bazlı bir rehberde sekiz gün ileride, sıkıştırılmamış indirme 200 MB'ı aştığında dört gün ileride, ev geneli depoda ise iki gün ileride bitiyor; sonuncusu her kaynağı bir arada tuttuğu için daha azını saklıyor. Yani üç haftalık program listesi yayımlayan bir rehber, tamamen sağlıklı bir içe aktarmada bile çok büyük bir atlama sayısı bildirecek; aynı dosya, ev geneli depo açıldığında bunun çok daha büyüğünü bildirecek. Yüksek bir sayı bakmak için bir sebeptir, saat sorununun kanıtı değil.

Rahatsız edici tarafı şu: en gürültülü arıza biçimimiz, bir şeyler zaten ters görünmedikçe kimsenin açmadığı bir pencerede duran tek bir tam sayı.

Bozuk bir stop size bir programa değil, üç saate mal olur

Başlangıç zaman damgasındaki okunamayan bir ofset satırı düşürür. Bitiş zaman damgasındaki okunamayan bir ofset düşürmez.

Başlangıç ayrıştırılıp bitiş ayrıştırılamadığında bitiş sıfırda kalıyor, "bitiş, başlangıçtan sonradır" testini geçemiyor ve satıra üç saatlik öntanımlı bir süre veriliyor. Ayrı bir sınırlayıcı her programı altı saatle sınırlıyor; üç, altının altında olduğu için burada hareketsiz kalıyor ve yalnızca bitişi ayrıştırılan ama çok uzağa düşen satırlarda devreye giriyor. Ne öntanımlı süre ne de sınırlayıcı atlama sayacını artırıyor. Yani stop öznitelikleri bozuk, start öznitelikleri sağlam olan bir dosya; başlangıç saatleri doğru, her yeri üç saatlik bloklarla dolu, satırları üst üste binen ve içe aktarma özeti tertemiz bir rehber üretiyor. "Saatler doğru görünüyor ama hiçbir şey oturmuyor" diye gelen rehber şikâyetlerinin arkasında sık sık bu şekil yatıyor.

İçeri altı program giriyor, dışarı dört program çıkıyor

Aşağıda, her yolu aynı anda çalıştırmak için kurulmuş bir parça var. Elle takip edilmedi, sürüme çıkmış ayrıştırıcıda gerçekten çalıştırıldı. Her kanal kimliği ayrılmış bir örnek alan adı kullanıyor, her başlık uydurma; ayrıca saklama penceresinin bu tarihleri kapsadığını varsayın, yoksa ofsetlere sıra gelmeden saklama tartışmayı bitirir.

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

Birinci satır mutlu yol: artı iki saatteki 15:00, 13:00 UTC eder ve 14:00 UTC'ye kadar sürer.

İkinci satırın iki özniteliğinde de +02:0 var. Sonek işaretle başlıyor, demek ki ofset olarak okunmak zorunda; rakam dizisi dört karakter ve son ikisinin ikisi birden rakam değil, dolayısıyla başarısız oluyor. Başlangıç hiç atanmıyor, satır geri çevriliyor ve atlama sayacı bire çıkıyor.

Üçüncü satırın başlangıcı temiz, bitişi aynı bozuk hâlde. Başlangıç 15:00 UTC'ye çözülüyor. Bitiş çözülmüyor, dolayısıyla üç saatlik öntanımlı süre devreye giriyor ve satır bir saat yerine 15:00 ile 18:00 UTC arasını kaplıyor. Hiçbir şey sayılmıyor. Atlama sayacının bir sağlık ölçütü olmadığının sebebi bu satır.

Dördüncü satır, bir saatlik durumun en saf hâli. Sonek BST ve işaretle başlamıyor; okuyucu ofsetsiz yoldan gidiyor ve varsayılan saat dilimini, yani UTC'yi uyguluyor. Grabber 17:00 UTC demek istemişti. Rehber 18:00 UTC gösteriyor. Hiçbir sayaç kıpırdamıyor.

Beşinci satır aynı etiketi gerçek bir sayıdan sonra taşıyor ve doğru şekilde 18:00 UTC'ye çözülüyor. Yani dördüncü ve beşinci satır tek bir kanalda 18:00 UTC'de başlıyor ama bunu yalnızca biri istiyordu; arkasında iki farklı grabber olan bir dosya içeriden böyle görünür.

Altıncı satır sekiz karakter, on dört karakterlik tabanın altında; iki öznitelik de başarısız oluyor ve atlama sayacı ikiye çıkıyor. Bu aynı zamanda DTD'nin geçerli sayacağı satır, tam bir zaman damgasının baştan bir parçası; yani bu parçada sayılan hasarın yarısı dosyadan değil, bizim daraltmamızdan geliyor.

Dört satır depoya ulaştı ve bu dördün ikisi, hiçbir şeyin bildirmediği bir biçimde yanlış: biri bir saat geç başlıyor, biri gerçek uzunluğunun üç katı sürüyor. Sayaç diğer ikisini bildiriyor ve ikisi için de sebep vermiyor.

Ofset dosya başına bir kez değil, her an için ayrı çözülür

Tarih çekirdeği hiçbir varsayım bayrağı açılmadan ayrıştırılıyor; geriye türü belirsiz bir değer dönüyor ve bu kasıtlı. Okuyucu sonra onu açıkça bir ofsetle eşliyor. Alternatif, yani belirsiz bir değeri boşluğu kendi dolduran bir kurucuya vermek, .NET'in makinenin kendi ofsetini uygulamasına yol açardı; aynı dosya Paris'teki bir cihazda ve Tunus'taki bir cihazda farklı bir yayın akışı üretir, depo da aynı evdeki iki cihaz arasında taşınabilir olmaktan çıkardı.

Ofsetsiz yolda, varsayılan saat diliminin ofseti dosyanın tamamı için bir kez değil, o belirli an için aranıyor. Batı Avrupa saat diliminde ilkbahar geçişinin iki yakasında, yerel saatte iki buçuk saat arayla duran iki zaman damgası UTC'de doksan dakika arayla çıkıyor. Doğrusu bu ve dosya düzeyinde tek bir ofsetin geçişten sonraki her satırda yanlış yapacağı şey tam olarak bu. Geçişin kullanılmaz kıldığı iki yerel saat, yani iki kez yaşanan saat ile hiç yaşanmayan saat, ikisi de standart ofsete çözülüyor; yeniden ayrıştırma da aynı değeri döndürüyor, dolayısıyla tek bir dosya art arda iki içe aktarmada iki farklı rehber olarak görünemiyor.

Bu düzeneğin açıkça söylenmesi gereken bir boşluğu var. Varsayılan saat dilimi parametresi duruyor, cihazda rehberi içe alan iki yol da onu geçiriyor ve ikisi de null geçiriyor. Sürüme çıkmış uygulamada onu ayarlayan hiçbir şey yok, bir test de bunu sabitliyor. Yerel duvar saati değerlerini soneksiz yazan bir rehber UTC olarak okunuyor, ki DTD'nin belgelenmiş öntanımlı davranışı da bu; nasıl ayrıştırıldığını değiştiren kaynak bazlı bir denetim ise yok. Yukarıdaki geçiş paragrafında anlatılan her şey, sürüme çıkmış hiçbir kod yolunun şu anda uğramadığı bir düzenek.

Saat düzeltmesi tam saatlerle çalışır ve iki yer onu kapatır

Bir düzeltme var ve başka bir katmanda çalışıyor. Her kaynak, eksi 48 ile artı 48 arasında tam saat cinsinden bir saat kaydırması taşıyor; aynı rehber ayarları penceresinde "Saat kaydırması (tam saat)" başlığı altında, "Eklenecek saat" etiketli alandan düzenleniyor. Program listeleri depoya girerken değil, depodan okunurken uygulanıyor; yani değiştirmek için hiçbir şeyi yeniden indirmeniz gerekmiyor ve saklanan veri yeniden yazılmıyor.

Bilerek yapmadığı iki şey var. Yalnızca tam saatlerle çalışıyor, dolayısıyla buçuklu bir saat dilimini düzeltemiyor; UTC olarak okunmuş, +05:30 bölgesindeki bir kaynağı bununla geri getiremezsiniz. Bir de iki durumda sıfıra zorlanıyor. Birincisi ev geneli rehber deposu: içe alınırken UTC'ye normalleştiriliyor ve geriye kaydırmanın düzeltebileceği bir şey kalmıyor. İkincisi birleştirilmiş bir kaynağa ait herhangi bir kanal: kanalın arkasında aynı anda birkaç üye var ve doğru ofset, o an hangi üyenin yayını verdiğine bağlı. Tek bir sayı, üyelerden biri hariç hepsi için yanlış olurdu; bu yüzden denetimi sunup yalan söyletmektense hiç sunmuyoruz.

Kanıt değişirse ilk gözden geçireceğim karar bu sonuncusu. Savunulabilir bir karar, ama aynı zamanda şu demek: acil çıkış kapısı tam da bir evin en çok kaynağa ve içlerinden birinin yanlış olması için en çok ihtimale sahip olduğu yerde yok.

Windows ve Xbox uygulamalarımız bu ayrıştırmanın tamamını yerelde, zaten elinizde olan bir rehber adresi üzerinde yapıyor. Tanılama satırı ile kaydırma denetiminin aynı pencerede durmasının bir sebebi var: ilk sayı size dosyanın okunup okunmadığını söyler, ikincisi de okunduğunu öğrendikten sonra uzanacağınız şeydir.

Kendi okuyucunuzu yazıyorsanız, sizi kurtaracak kural bizim öğrenmesi en uzun süren kuralımızdır. İşaretle başlayan bir sonek, size sayı olduğunu zaten söylemiştir. Ya onu okuyun ya da satırı düşürün; ama öntanımlı bir değere düşmeyin, çünkü öntanımlı değer dosyadaki her zaman damgasına tekdüze ve kendinden emin biçimde uygulanır ve tekdüze, kendinden emin bir yanlışlık, kimsenin bildirmediği tek arıza türüdür.

Bu makale neyi ölçtü?28 iddiaların her biri arkasında deliller barındırıyor
TalepKanıtsayıldı
XMLTV belge tipi tanımı, açık bir saat dilimi taşımayan bir zaman damgasının UTC olarak okunacağını söylüyor.xmltv.dtd, date field documentation: you can also append a timezone to the end; if no explicit timezone is given, UTC is assumed.ŞartnameGeçerli değil
XMLTV DTD kendi örnek ofsetini dört haneli temel biçimde, +0300 olarak yazıyor.xmltv.dtd, date field documentation, example 19880523083000 +0300ŞartnameGeçerli değil
XMLTV DTD bir tarihin YYYYMMDDhhmmss ya da bunun baştan başlayan bir parçası olmasına izin veriyor ve üç örnek olarak 200007281733 BST, 200209 ve 19880523083000 +0300 yazıyor.xmltv.dtd, date field documentation, including the annotation (BST == +0100.) on the first exampleŞartnameGeçerli değil
ISO 8601 bir UTC ofsetini üç biçimde yazar: temel (+0200), genişletilmiş (+02:00) ve yalnızca saat (+02).ISO 8601-1, time offset from UTCŞartnameGeçerli değil
Bir .NET DateTimeOffset, büyüklüğü 14 saati aşan bir ofseti reddeder, tam 14 saati kabul eder..NET DateTimeOffset constructor, offset argument range -14:00 to +14:00 inclusiveŞartnameGeçerli değil
Kind değeri Unspecified olan bir DateTime'dan DateTimeOffset kurmak, üzerinde çalıştığı makinenin yerel ofsetini uygular..NET DateTimeOffset(DateTime) constructor remarks, which treat an Unspecified Kind as local timeŞartnameGeçerli değil
Saat dilimi kısaltmaları benzersiz değil: CST'yi aynı anda America/Chicago, America/Havana ve Asia/Shanghai taşıyor; BST hem British Summer Time hem de aralarında beş saat olan Bangladesh Standard Time için yazılmış.IANA time zone database, theory.html on the ambiguity of zone abbreviations, plus the CST abbreviation on America/Chicago, America/Havana and Asia/ShanghaiŞartnameGeçerli değil
+ veya - ile başlayan bir zaman damgası soneki sayısal ofset olarak ayrıştırılmak zorundadır, yoksa zaman damgasının tamamı başarısız olur. Varsayılan saat dilimine düşmek, bu kuralın yerine geçtiği ve sürüme çıkmış olan kusurdur.n = 222 Ağu 2026
Yalnızca 2, 4 ya da 2. indekste iki nokta üst üste bulunan 5 uzunluğundaki rakam dizileri kabul ediliyor; bu yüzden +02, +0200 ve +02:00 çözülüyor, diğer bütün uzunluklar öntanımlı daldan düşüyor.n = 322 Ağu 2026
Yedi ofset biçimi testlerle ayrıştırma hatası olarak sabitlenmiş: +2, +02:0, +0X30, +2400, +99:00, -0270 ve tek başına +.n = 722 Ağu 2026
59'un üzerindeki dakikalar reddediliyor ve toplam büyüklük, sınır dahil, 14 saatte kesiliyor; yani +1400 çözülüyor, +1401 çözülmüyor. İki haneli okuyucu ASCII rakam istiyor ve NumberStyles.None ile ayrıştırıyor.n = 222 Ağu 2026
Gerçek bir ofsetten sonra gelen saat dilimi etiketi ilk boşlukta atılıyor; böylece +0100 BST, +01:00 olarak çözülüyor ve yerel saatle 15:00, UTC'de 14:00 oluyor.n = 122 Ağu 2026
Tek başına harflerden oluşan bir saat dilimi adı ofset okuyucusuna hiç ulaşmıyor ve varsayılan saat dilimi yolundan gidiyor; bu yüzden 20260719150000 BST de 20260719150000 UTC de 15:00 UTC olarak çözülüyor.n = 222 Ağu 2026
XMLTV DTD'nin yazdığı üç örnek zaman damgasından ikisini okuyucumuz geri çeviriyor: 200007281733 BST ve 200209 başarısız oluyor, yalnızca 19880523083000 +0300 ayrıştırılıyor ve 05:30 UTC'ye çözülüyor.n = 322 Ağu 2026
14 karakterden kısa bir zaman damgası, hiçbir ayrıştırma denenmeden geri çevriliyor; yani yalnızca tarih içeren bir değer başarısız oluyor.n = 122 Ağu 2026
Tarih çekirdeği tam olarak ilk 14 karakter; bu yüzden başıboş bir 15. rakam sonekin içine düşüyor, işaret testini geçemiyor ve sessizce atılıyor.n = 122 Ağu 2026
Kesin hata kuralı sonekin ilk karakterine bakıyor; bu yüzden GMT+0200 gibi önce etiketi yazan bir biçim varsayılan saat dilimi yolundan gidiyor ve ofset yok sayılarak UTC olarak okunuyor.n = 122 Ağu 2026
Aynı atlama sayacı; eksik bir channel özniteliği, ayrıştırılamayan bir start ve saklama penceresinin dışında kalan bir program için artıyor.n = 322 Ağu 2026
Saklama penceresi her zaman ayrıştırma anının 24 saat gerisinden başlıyor; kaynak bazlı yolda sekiz gün ileride, sıkıştırılmamış indirme 200 MB'ı aştığında dört gün ileride, ev geneli depoda ise iki gün ileride bitiyor.n = 322 Ağu 2026
Geçerli bir start ve ayrıştırılamayan bir stop taşıyan program, üç saatlik öntanımlı bir süreyle saklanıyor ve atlanmış sayılmıyor; ayrı duran altı saatlik üst sınır burada devreye girmiyor.n = 122 Ağu 2026
Son getirme göstergesi kanal, program, atlandı ve milisaniye olarak biçimleniyor ve Son rehber getirme başlığı altında duruyor.n = 122 Ağu 2026
Kaynak bazlı düzeltme -48 ile +48 arasında tam saat cinsinden; Saat kaydırması (tam saat) başlığı ve Eklenecek saat alanıyla sunuluyor ve içe alma anında değil, okuma anında uygulanıyor.n = 122 Ağu 2026
Ev geneli depo etkinken ve kanal birleştirilmiş bir kaynağa aitken düzeltme sıfıra zorlanıyor.n = 222 Ağu 2026
Cihazdaki her iki içe alma yolu da ayrıştırma hattını assumeTimezone null ile kuruyor; bu yüzden çıplak bir zaman damgası UTC olarak okunuyor ve makinenin saat dilimi hiç işin içine girmiyor.n = 222 Ağu 2026
Varsayılan saat dilimi yolunda ofset her an için ayrı çözülüyor; böylece ilkbahar geçişinin iki yakasında yerel saatle iki buçuk saat arayla duran iki zaman, UTC'de doksan dakika arayla çıkıyor.n = 222 Ağu 2026
Yerel saatte iki kez yaşanan saat de hiç yaşanmayan saat de standart ofsete çözülüyor ve yeniden ayrıştırma aynı değeri döndürüyor.n = 322 Ağu 2026
Bu yazıdaki elle kurulmuş parça 1 kanal, 4 kayıtlı satır ve 2 atlama veriyor; kayıtlı satırlardan biri bir saat geç başlıyor, biri de bir saat yerine üç saat sürüyor.n = 622 Ağu 2026
M3U korpusumuzdaki iki çalma listesinin büyüğünde, 1.895 satırın 527'si kullanılabilir bir kanal kimliği olmadan bitiyor; yani bir rehber satırının tutunacağı güvenilir bir şey kalmıyor.n = 189522 Ağu 2026