Teknik Makale

HotXLS Grafik Tarih Ekseni: Takvime Sadık Tick'ler

1 Ocak 2026'da başlayan bir ay sonu bakiye grafiği, Nisan tick'ini 30 Nisan'a koymalıdır. Her tick'i öncekinden takvim aylarıyla ilerletirseniz etiketler 2026-01-31, 2026-02-28, 2026-03-28, 2026-04-28 olarak çıkar ve sonraki her tick 28'e çakılı kalır. Düz 30 günle ilerletirseniz Şubat tick'i 2 Mart'a düşer, Aralık tick'i ise 27 Aralık'a. Her iki tuzağı da atlatan kural söylemesi basittir: her tick'i eksen minimumundan hesaplayın ve ay sonlarını gerçek takvime kelepçelenin

Çeyrek kapanış grafiğinde tehlikeli olan ilk hatadır, çünkü resim hâlâ doğru görünür. Çizgi olması gerektiği yerde yükselir, etiketler eşit aralıklıdır ve Mart bakiyesi çeyrek sonundan üç gün önce durur. Grafiği defterle karşılaştıran kişi, genellikle mali işler sorumlusu, onu bulur; o ana kadar grafik yönetim kurulu dosyasına yapıştırılmış bile olabilir

Ay tick'leri biriktirildiklerinde neden kayar?

Ay tick'leri kayar, çünkü kelepçeleme bilgiyi çöpe atar. 31 Ocak bir kez 28 Şubat'a kelepçelendiğinde serinin 31'i istediği gerçeği kaybolur ve kelepçelenmiş değerden atılan her adım o kaybı devralır. HotXLS'te lxChart birimindeki TXLSChartDateAxisTransform.BuildTicks hiçbir zaman önceki bir tick'ten ilerlemez: i numaralı tick her zaman AddUnits(MinValue, UnitKind, Step * i, ...)'dır; dolayısıyla Mart, 31 Ocak'tan iki ay sonradır ve 31 Mart olarak çıkar. AddUnits, gün birimleri için sayacı doğrudan serial değere ekler; ay ve yıl birimleri için serial'i gerçek bir tarihe çevirir, IncMonth ya da IncYear çağırır ve geriye çevirir

Döngü her yönden sınırlanır. Sonuç dizisi, çağıran en fazla ne geçerse geçsin 4096 girdiyi aşmaz; Step * i, çarpma gerçekleşmeden MaxInt'e karşı denetlenir ve bir tick artamadığında ya da eksen maksimumunu geçtiğinde üretim derhal durur. Birkaç milyon yıllık açıklığı taşıyan düşmanca bir çalışma kitabı kısa bir dizi üretip döner

HotXLS grafik tarih ekseninin 31 Ocak 2026 çapasından sonra ay tick'lerini yerleştirmesinin iki yolu: her tick'i önceki sıkıştırılmış değerden ilerletmek 28'e çakılı kalır, BuildTicks ise AddUnits(MinValue, xcduMonths, Step kere i) hesabını eksen minimumundan yaptığı için tick'ler 28 Şubat, 31 Mart ve 30 Nisan'a düşer
Sıkıştırma bilgi çöpe attığı için TXLSChartDateAxisTransform hiçbir zaman önceki tick'ten ilerlemez ve her konumu eksen minimumundan yeniden hesaplayıp ay sonlarını gerçek takvime sıkıştırır

Transform herkese açıktır; dolayısıyla hiçbir grafik var olmadan aritmetiği kendiniz koşturabilirsiniz. Bu konsol parçacığı yukarıdaki seri için bir yıllık ay tick'leri kurar ve grafiğin kullanacağı etiketleri basar

uses
  SysUtils, lxChart;

var
  Ticks: TXLSChartDateValues;
  MinSerial, MaxSerial: Double;
  I, Count: Integer;
begin
  // 1900 tarih sistemi (Dates1904 = False), ay sonuna demirli seri
  MinSerial := TXLSChartDateAxisTransform.DateTimeToSerial(
    EncodeDate(2026, 1, 31), False);
  MaxSerial := TXLSChartDateAxisTransform.DateTimeToSerial(
    EncodeDate(2026, 12, 31), False);

  // Tek adım bir ay, 12 tick hedefle, 64'ü asla aşma
  Count := TXLSChartDateAxisTransform.BuildTicks(MinSerial, MaxSerial,
    1, xcduMonths, False, 12, 64, Ticks);

  for I := 0 to Count - 1 do
    Writeln(TXLSChartDateAxisTransform.FormatValue(Ticks[I], False));
  // 2026-01-31, 2026-02-28, 2026-03-31, 2026-04-30 ... 2026-12-31
end;

BuildTicks'te tam olarak tek bir yaklaşıklandırma vardır ve zararsız olan yerde oturur. İstenen adım sıfır olduğunda yöntem, bir ayı 30 gün ve bir yılı 365 sayarak bir adım tahmin eder, sonra ham adımı bir on kuvvetinin 1, 2 ya da 5 katına yuvarlar. O tahmin yalnızca tick'ler arasında kaç birim olduğuna karar verir. Konumlar hâlâ IncMonth ve IncYear'dan gelir. Kaba bir ay uzunluğu ekseni biraz daha yoğun ya da seyrek yapabilir; ama her tick, o iki işlevden birinin ürettiği bir tarihe düşer

Çalışma kitabı serial değerleri eksen koordinatı olarak kalır

Bir HotXLS tarih ekseni, çalışma kitabı serial değerlerini koordinat uzayı olarak tutar; çünkü Delphi'nin TDateTime'ında serial 60 için bir yuva yoktur. 1900 tarih sisteminde o serial, 29 Şubat 1900'dür — hiç var olmamış, Excel'in eski elektronik tablo programlarıyla uyum için tuttuğu bir gün. TXLSChartDateAxisTransform.TrySerialToDateTime onu gerçek bir takvimin yapabileceği tek yolla halleder: 60 altındaki serialler bir gün kayar ve serial 60, 28 Şubat'a katlanır. Tek bir noktayı etiketlemek için bu yeterlidir. Geometri için yanlıştır; çünkü serial 59 ile 60 aynı TDateTime'a düşer ve 28 Şubat ile 1 Mart, Excel'in iki birim arayla çizdiği yerde bir birim arayla kalır

Üç serial, sorunun iki yüzünü de gösterir. FormatValue hayalet yuvayı özel işler ve tam olarak Excel'in bastığını basar; TDateTime üzerinden gidiş dönüş ise onu yitirir

var
  S: Integer;
  D: TDateTime;
begin
  for S := 59 to 61 do
  begin
    TXLSChartDateAxisTransform.TrySerialToDateTime(S, False, D);
    Writeln(S, '  axis label: ',
      TXLSChartDateAxisTransform.FormatValue(S, False),
      '  TDateTime: ', FormatDateTime('yyyy-mm-dd', D));
  end;
  // 59  axis label: 1900-02-28  TDateTime: 1900-02-28
  // 60  axis label: 1900-02-29  TDateTime: 1900-02-28
  // 61  axis label: 1900-03-01  TDateTime: 1900-03-01
end;

1900'ün başından veri grafikleyen kimse yoktur; dolayısıyla pratik ders kendi kodunuz hakkındadır. Eksen sınırlarını ya da açıklama konumlarını hesaplıyorsanız, ilk parçacığın yaptığı gibi, serial değerlerinde hesaplayın ve uçta DateTimeToSerial ile çevirin. Çağırandan serial tabanlı ekseni TDateTime aritmetiğiyle karıştırırsanız, 1 Mart 1900'den önce tarihlenmiş bir açıklama noktasından bir gün uzağa düşer. İkinci dönem daha sık önem kazanır: 1904 çalışma kitabında serial 0, 1 Ocak 1904'tür ve transform, Dates1904 parametresi doğruyken sabit 1462 günlük kaydırmayı iki yönde de uygular. Her iki dönem ve hücre sayı formatlarıyla etkileşimleri Excel tarih serial değerleri, 1904 sistemi ve sayı formatları makalemizde işlenir

HotXLS tarih ekseninin TDateTime'a çevirmek yerine çalışma kitabı serial değerlerini tutması neden önemli: serial 60 hayalet 29 Şubat 1900'dur, TrySerialToDateTime 60 altını kaydırıp 60'ı 28 Şubat'a katladığında iki yan bir birim daha yakın düşer, FormatValue ise serial 60'ı 1900-02-29 diye etiketler
Katlama tek bir noktayı etiketlemek için sorun değil ama geometri için ölümcüldür, çünkü Mart 1900 öncesinde çizilen her şey sonrasına göre kayar; 1904 çalışma kitabı bunun yerine sabit 1462 günlük kaydırmayı uygular

HotXLS gün, ay ya da yılı otomatik nasıl seçiyor?

TXLSChartDateAxisTransform.DetectUnit, verideki boşlukların gerçekte gereksinim duyduğu en ince takvim birimini seçer. NaN ve sonsuz değerleri atar, kalanların bir kopyasını bir kez sıralar ve yinelenen tarihleri atlar. Herhangi bir ardışık boşluk bir takvim ayından kısaysa birim gündür; değilse, herhangi bir boşluk bir takvim yılından kısaysa aydır; değilse yıldır. Ay testi IncMonth(Previous, 1) > Current'tır ve o seçim tam olarak bu yazıdaki veri için önemlidir. 30 günlük bir eşik, 31 Ocak'tan 28 Şubat'a giden boşluğu "aydan kısa" sayar ve aylık bir seriyi güne çevirirdi. İki ayrı geçerli tarihten az kaldığında ölçülecek boşluk yoktur ve yöntem gün döndürür

ResolveUnits, saptanan birimi grafiğin bildirdiğiyle birleştirir. Açık bir taban birim kazanır. Eksik bir major ya da minor birim, saptanan birim tabandan daha kaba olduğunda onu devralır; değilse tabana düşer. İnsanların gözünden kaçan son kural şudur: etkin major ya da minor birim tabandan daha inceyse taban, eşleşmek için düşürülür. Taban birim, hiç tick var olmadan önce noktaların nasıl gruplanacağına karar verir; çünkü Normalize bir değeri gününe, ayının birine ya da 1 Ocak'a yapıştırır. Günlük major birim altındaki aylık bir taban, tick kodu onları hiç görmeden bir ay dolusu noktayı tek yuvaya katlardı

HotXLS tarih ekseni birimlerini otomatik nasıl seçiyor: DetectUnit NaN ve sonsuz değerleri atar, bir kez sıralar ve takvim boşluklarını 30 günlük eşik yerine IncMonth ile ölçer, böylece 31 Ocak, 28 Şubat, 31 Mart serisi aylık saptanır; ResolveUnits açık bir tabana riayet eder, major birim daha ince olduğunda ise tabanı düşürür
Normalize, hiç tick var olmadan önce her değeri gününe, ayının birine ya da 1 Ocak'a yapıştırır; çünkü günlük major birim altındaki aylık bir taban, bir aylık noktaları tek yuvaya çökertirdi

Kaç otomatik major tick görüneceği tarih aralığından fazlasına bağlıdır. Çizim uzunluğu, eksen fontunun ölçülen genişliği, etiket sayı formatı ve döndürülmüş etiketlerin öngörülen genişliği hepsi sayıyı sınırlar; dolayısıyla uzun mmmm yyyy etiketli dar bir grafik, mm/yy'li geniş olandan daha az tick alır. Etiketler, tick işaretleri ve kılavuz çizgileri tek bir tick dizisinden çizilir; böylece HTML, SVG ve PDF çıktısı uyuşur ve major tick üzerine düşen minor tick bir kez çizilir

dateAx öğesi neyi saklar, yokluk ne anlama gelir?

ChartML'de bir tarih eksenindeki her takvim ayarı isteğe bağlıdır ve birini yok bırakmak, varsayılanını yazmaktan farklı bir şey söyler. ECMA-376 Part 1, §21.2'deki (DrawingML Charts) CT_DateAx tipi, baseTimeUnit, majorTimeUnit, minorTimeUnit ve auto'nun her birinin bulunup bulunmamasına izin verir. Yok sayılan bir baseTimeUnit, Excel'e render sırasında karar vermesini söyler; açık bir days onu sabitler. TXLSXChartAxis her değeri bir varlık bayrağının yanında tutar: BaseTimeUnit ile BaseTimeUnitSet, MajorTimeUnit ile MajorTimeUnitSet, MinorTimeUnit ile MinorTimeUnitSet ve AutoDateAxis ile AutoDateAxisSet. Değer atamak bayrağını set eder, bayrağı False yapmak öğeyi yokluğa döndürür ve XLSX yazıcısı bir öğeyi yalnızca bayrağı set edilmişken üretir. auto yanlış okunmaya açıktır: otomatik kategori-mi-tarih-mi saptamasını denetler ve otomatik tick birimleriyle hiçbir işi yoktur

uses
  SysUtils, DateUtils, lxHandleX;

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Chart: TXLSXChart;
  Axis: TXLSXChartAxis;
  M: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Close');
    Sheet.Cells[1, 1].Value := 'Month end';
    Sheet.Cells[1, 2].Value := 'Balance';
    for M := 1 to 12 do
    begin
      Sheet.Cells[M + 1, 1].Value :=
        EncodeDate(2026, M, DaysInAMonth(2026, M));
      Sheet.Cells[M + 1, 2].Value := 1000 + M * 75;
    end;

    Chart := Sheet.AddLineChart('Month-end balance',
      'Close!$A$2:$A$13', 'Close!$B$2:$B$13', 15, 1, 32, 9);
    Axis := Chart.CategoryAxis;
    Axis.Kind := xlsxAxisDate;                  // c:dateAx yazar
    Axis.BaseTimeUnit := xlsxChartTimeDays;     // BaseTimeUnitSet'i de set eder
    Axis.MajorTimeUnit := xlsxChartTimeMonths;
    Axis.MajorUnit := 1;                        // > 0 değeri majorUnit'ü set edilmiş işaretler
    Axis.NumberFormat := 'mmm yyyy';
    Axis.NumberFormatSourceLinked := False;
    Axis.TextStyle.Rotation := -45;             // derece
    // AutoDateAxis hiç dokunulmadı: c:auto öğesi yazılmaz

    Book.SaveAs('month-end-balance.xlsx');
  finally
    Book.Free;
  end;
end;

Burada taban birimin gün olması bilinçlidir. Ay sonu tarihleri farklı gün numaralarına düşer ve aylık bir taban her noktayı ayının birine yapıştırırdı; bu da 31 Mart bakiyesini 1 Mart'a taşırdı. Tam gün anlam taşıdığında tabanı günlere sabitleyin ve aylık aralıklamayı major birime bırakın

Etiketler ve yönelim aynı varlık kurallarını izler. Eksenin numFmt öğesi sourceLinked'i varsayılanda true yapar; dolayısıyla TXLSXChartAxis format kodunu, source-linked değerini ve NumberFormatSet'i ayrı tutar ve boş bir kod asla eksik öğe sanılmaz. ChartML metin döndürmesini 1/60000 derece birimlerinde saklar ve pozitif değerler saat yönünde akar; TextStyle.Rotation düz derece alır ve -45'e ayarlı bir etiket SVG ile PDF çıktısında da aynı yöne yaslanır. Ters (maxMin) bir eksen, major ve minor tick'leri, kılavuz çizgilerini, veri noktalarını, trend çizgilerini ve hata çubuklarını birlikte aynalar. Okuma ters yöne işler: başka bir yerde yazılmış bir çalışma kitabı açın ve bayraklar, yazarın gerçekte ne belirttiğini söyler

var
  Book: TXLSXWorkbook;
  Axis: TXLSXChartAxis;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('forecast.xlsx') <> 1 then
      raise Exception.Create('Cannot open workbook');
    Axis := Book.Sheets[1].Charts[0].CategoryAxis;  // Sheets[] 1 tabanlı, Charts[] 0 tabanlı
    if Axis.Kind = xlsxAxisDate then
    begin
      if Axis.BaseTimeUnitSet then
        Writeln('baseTimeUnit = ', Ord(Axis.BaseTimeUnit))
      else
        Writeln('baseTimeUnit omitted: Excel chooses at render time');
      if Axis.AutoDateAxisSet then
        Writeln('auto = ', Axis.AutoDateAxis);
    end;
  finally
    Book.Free;   // tekrar kaydedin, yok sayılan öğeler yok sayılmış kalır
  end;
end;

ODS ve eski XLS dosyalarında takvim birimleri

Takvim modeli, her dosya biçiminin ifade edebildiği kadar ileri gider. ODF 1.2, eksen aralıklamasını gün, ay ya da yıl kavramı olmayan düz bir sayı olan chart:interval-major ile anlatır; dolayısıyla Excel'in aylık ekseni ODS olarak kaydedildiğinde birimini taşıyamaz. HotXLS, öteki ODF uygulamalarının zaten yok sayacağı standart dışı bir öznitelik uydurmak yerine birimi ODS dosyasının dışında bırakır. Aylık aralıklama dosyayı açanlar için önemliyse XLSX gönderin

BIFF8 çalışma kitapları tarih ekseni ayarlarını dokuz sabit 16 bitlik sözcük taşıyan AxcExt kaydında tutar. Otomatik bayrakları alanları kaldırmadan maskeler; dolayısıyla "otomatik minimum" işaretli bir kayıt hâlâ bir minimum değeri taşır. HotXLS okurken maskelenmiş minimum, maksimum, aralık, birim ve kesişim değerlerini korur ve değiştirmeden geri yazar, XLS nesne modelinde bilinmeyen birim kodlarını bozulmadan tutar ve bir grafik XLSX'e çevrilirken yalnızca geçerli tarih birimleri olan 0, 1 ve 2'yi (gün, ay, yıl) eşler

Bir tarih ekseni nadiren yalnız gezer. Bir Excel çalışma kitabı, tarih eksenli bir çizgiyi ikincil eksende bir sütun serisiyle eşleştirdiğinde korunan ChartML ve kombinasyon grafikleri makalemiz, HotXLS'in hiçbir şey değişmediğinde özgün grafik XML'ini nasıl yeniden oynattığını ve bir şey değiştiğinde tipli düzenlemeleri ona nasıl işlediğini anlatır; grafikler, resimler ve çizimler genel bakışımız yukarıda kullanılan çapa ve seri çağrılarını kapsar. HotXLS Delphi Component sayfası, Excel kurulu olmadan XLS ve XLSX grafik okuma, yazma ve render dahil, Delphi ve C++Builder için tam grafik özellik setini listeler