HotXLS izrisuje datumske osi grafikonov Excel s koledarsko aritmetiko in ne s fiksnim številom dni. Razred TXLSChartDateAxisTransform v enoti lxChart vsako koordinato vodi kot serijsko vrednost delovnega zvezka, vsako mesečno ali letno črtico izračuna neposredno iz minimuma osi prek IncMonth in IncYear ter konce mesecev stisne na pravi koledar, tako da serija, zasidrana na 31. januar, dobi črtice 28. februarja, 31. marca in 30. aprila, namesto da bi drsela. Isti transform samodejno izbere enote za dan, mesec ali leto in ga si delijo izrisovalniki grafikonov HTML, SVG in paginiran PDF
Hrošč, ki mu to prepreči, je znan vsakomur, ki je že grafikonal zaključek meseca. Izrisovalnik, ki mesec obravnava kot 30 dni, se do konca prvega leta zdrsne pet dni prezgodaj. Izrisovalnik, ki je pametnejši in uporablja koledarske mesece, vsako črtico pa pomika od prejšnje, odpove tišje: 31. januar postane 28. februar, naslednji korak pristane na 28. marca, vse črtice za tem pa ostanejo pripete na 28. Grafikon izgleda verjeten, oznake so napačne in nihče ne opazi, dokler kontrolor ne vpraša, zakaj je marčevsko stanje narisano tri dni pred koncem četrtletja
Zakaj mesečne črtice pri kopičenju drsijo?
Mesečne črtice drsijo, ker stiskanje v meje odvrže informacijo. Ko je 31. januar enkrat stisnjen na 28. februar, je dejstvo, da je serija hotela 31., izgubljeno, vsak korak, narejen iz stisnjene vrednosti, pa izgubo podeduje. TXLSChartDateAxisTransform.BuildTicks nikoli ne koraka od prejšnje črtice. Črtica i je vedno izračunana kot AddUnits(MinValue, UnitKind, Step * i, ...), kar je min + enota × indeks, merjeno od minimuma osi. AddUnits števec doda neposredno serijski vrednosti za enote dni, za enote mesecev in let pa serijsko vrednost pretvori v pravi datum, pokliče IncMonth ali IncYear in pretvori nazaj. Zanka je omejena tudi s vseh strani: tabelo rezultatov omeji na 4096 vnosov, ne glede na to, kaj klicnik zahteva, Step * i se preveri proti MaxInt, še preden se zmnoži, in ustvarjanje se ustavi, takoj ko črtica ne zraste ali preide maksimum osi
uses
SysUtils, lxChart;
var
Ticks: TXLSChartDateValues;
MinSerial, MaxSerial: Double;
I, Count: Integer;
begin
// datumski sistem 1900 (Dates1904 = False), serija zasidrana na konec meseca
MinSerial := TXLSChartDateAxisTransform.DateTimeToSerial(
EncodeDate(2026, 1, 31), False);
MaxSerial := TXLSChartDateAxisTransform.DateTimeToSerial(
EncodeDate(2026, 12, 31), False);
// korak enega meseca, cilj 12 črtic, nikoli več kot 64
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;
V BuildTicks je en iskren približek. Ko je zahtevani korak nič, metoda oceni velikost koraka tako, da mesec obravnava kot 30 dni in leto kot 365, surovi korak pa zaokroži na 1-, 2- ali 5-kratnik desetiške potence. Ta ocena odloča le o tem, koliko enot leži med črticami. Položaji črtic sami še vedno prihajajo iz IncMonth in IncYear, zato približek lahko spremeni gostoto črtic, črtice pa nikoli ne spravi s koledarja
Zakaj obdržati serijske vrednosti delovnega zvezka namesto pretvorbe v TDateTime?
Datumska os v HotXLS obdrži serijske vrednosti delovnega zvezka kot svoj koordinatni prostor, ker Delphijev TDateTime nima reže za serijo 60, fantomski 29. februar 1900, ki ga je datumski sistem 1900 podedoval kot čudažnost za združljivost. TrySerialToDateTime serijske vrednosti pod 60 premakne za en dan in serijo 60 zloži na 28. februar, kar je v redu za označevanje posamezne točke, geometriji pa usodno: če najprej pretvorite vsako točko v TDateTime, sta dva dneva ob fantomskem dnevu za eno enoto bliže kot ju nariše Excel, zato se vse, narisano pred marcem 1900, premakne glede na vse za njim. TXLSChartDateAxisTransform.FormatValue isto režo obravnava posebej in jo označi kot 1900-02-29, točno tako kot Excel. V delovnem zvezku 1904 je serija 0 datum 1. januar 1904, transform pa v obe smeri uporabi fiksni odmik 1462 dni; celotno zgodbo obeh epoh pripoveduje naš članek o datumskih serijskih vrednostih Excel, sistemu 1904 in številskih formatih
Kako HotXLS samodejno izbere dneve, mesece ali leta?
TXLSChartDateAxisTransform.DetectUnit izbere najbolj fino koledarsko enoto, ki jo vrzeli v podatkih dejansko potrebujejo. Metoda najprej odvrže vrednosti NaN in neskončne, preostale enkrat uredi v O(n log n) in preskoči podvojene datume. Če je katera koli sosednja vrzel krajša od enega koledarskega meseca, je enota dni; sicer, če je katera koli vrzel krajša od enega koledarskega leta, je enota mesecev; sicer pa let. Preizkus je IncMonth(Previous, 1) > Current in ne prag 30 dni, zato je serija koncev mesecev 31. januar, 28. februar, 31. marec pravilno prepoznana kot mesečni podatki. Z manj kot dvema različnima veljavnima datumoma ni vrzeli za merjenje in metoda pade nazaj na dneve
ResolveUnits nato prepozvano enoto spoji s tem, kar napove grafikon. Izrecna osnovna enota je spoštovana, manjkajoča glavna ali manjša enota pa podeduje prepozvano, kadar je ta grobejša od osnovne. Zadnji korak je tisti, ki ga ljudje spregledajo: če je dejavna glavna ali manjša enota finejša od osnovne enote, se osnovna zniža, da se prilagodi. Osnovna enota je tisto, na kar se točke podatkov normalizirajo, še preden sploh obstajajo črtice, Normalize pa vrednost pripne na njen dan, prvi dan njenega meseca ali 1. januar, zato bi mesečna osnova pod dnevno glavno enoto zrušila mesec točk v eno režo, še predno bi jih koda za črtice sploh videla. Tudi gostota črtic je omejena — z dolžino risbe, izmerjeno širino pisave osi, številskim formatom oznak in projekcijo zasukanih oznak — in vsak zaledni sistem riše oznake, črtice in črte mreže iz ene skupne, omejene tabele črtic, pri čemer se manjša črtica, ki sovpada z glavno, nariše le enkrat
Kaj shrani element dateAx in kaj pomeni izpuščenost?
V ChartML je vsaka koledarska nastavitev na datumski osi neobvezna in izpustiti eno ni isto kot zapisati njeno privzeto vrednost. Tip CT_DateAx v ECMA-376 Part 1, §21.2 (DrawingML Charts) dovoljuje, da se baseTimeUnit, majorTimeUnit, minorTimeUnit in auto vsak pokaže ali ne, izpuščeni baseTimeUnit pa pove Excelu, naj se odloči sam — kar je drugače kot izrecni days. TXLSXChartAxis zato vsako vrednost shrani ob zastavici prisotnosti: BaseTimeUnit z BaseTimeUnitSet, MajorTimeUnit z MajorTimeUnitSet, MinorTimeUnit z MinorTimeUnitSet in AutoDateAxis z AutoDateAxisSet. Dodelitev vrednosti nastavi njeno zastavico, počistitev zastavice vrne element v izpuščenost, pisec XLSX pa emitira element le, kadar je njegova zastavica nastavljena. Upoštevajte, da auto pomeni samodejno zaznavanje kategorij ali datumov in ne samodejne enote črtic
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)); // DateUtils
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; // zapiše c:dateAx
Axis.BaseTimeUnit := xlsxChartTimeDays; // hkrati nastavi BaseTimeUnitSet
Axis.MajorTimeUnit := xlsxChartTimeMonths;
Axis.MajorUnit := 1; // > 0 označi majorUnit kot nastavljen
Axis.NumberFormat := 'mmm yyyy';
Axis.NumberFormatSourceLinked := False;
Axis.TextStyle.Rotation := -45; // stopinje; shranjeno kot 1/60000
// AutoDateAxis nedotaknjen: element c:auto se ne zapiše
Book.SaveAs('month-end-balance.xlsx');
finally
Book.Free;
end;
end;
Oznake in usmerjenost sledijo istim pravilom, ki poznajo prisotnost. numFmt osi sede za naslovom in pred črticami, njegov atribut sourceLinked ima privzeto true, TXLSXChartAxis pa obdrži kodo formata, vrednost povezave z virom in NumberFormatSet ločeno, zato prazne kode nikoli ne zamenja z odsotnim elementom. Zasuk besedila je shranjen v 1/60000 stopinje, pozitivne vrednosti pa pomenijo smer urnega kazalca; TextStyle.Rotation sprejme navadne stopinje, izhod SVG uporabi kot, kot je, paginirana zaledna stran, kjer pozitivni koti tečejo v nasprotni smeri urnega kazalca, pa predznak obrne na eni skupni meji. Obrnjena os maxMin zrcali glavne in manjše črtice, črte mreže, točke podatkov, trendne črte in stolpce napak skupaj in nikoli le oznake. Ko odprete delovni zvezek, napisan drugje, vam zastavice povedo, kaj je avtor dejansko določil
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[0].Charts[0].CategoryAxis;
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; // znova shranite in izpuščeni elementi ostanejo izpuščeni
end;
end;
Kje se koledarski model ustavi?
Koledarski model se ustavi pri formatih, ki koledarskih enot ne znajo izraziti, in HotXLS se ne dela, da je drugače. ODF 1.2 ponuja chart:interval-major, ki je golo število brez pojma dni, mesecev ali let, zato mesečna os Excel, shranjena kot ODS, ne more nositi svoje enote, HotXLS pa ne izumi nestandardnega atributa, da bi jo pretvarjal. Na strani starejšega BIFF8 zapis AxcExt nosi devet fiksnih 16-bitnih besed, njegove samodejne zastavice pa polja le prekrijejo, namesto da bi jih odstranile; HotXLS prekrite vrednosti minimuma, maksimuma, intervala, enote in presečišča ohrani pri branju in povratnem zapisu, neznane kode enot obdrži nedotaknjene v modelu Classic in v XLSX preslika le veljavne datumske enote 0, 1 in 2 (dnevi, meseci, leta)
Datumski osi so ena plast modela grafikonov, ki mora preživeti tudi datoteke, ki jih ni ustvarila sama. Ko Excelov delovni zvezek združi črto na datumski osi s stolpčno serijo na sekundarni osi, naš članek o ohranjenem ChartML in kombiniranih grafikonih pokaže, kako HotXLS izvirni XML grafikona ponovno predvaja bajt za bajtom, kadar se nič ni spremenilo, in vanj spoji tipizirane spremembe, kadar se je kaj, naš pregled grafikonov, slik in risb pa pokriva API-ja za zasidranje in serije, uporabljena zgoraj. Vse to je del komponente HotXLS Delphi Component za Delphi in C++Builder, ki bere, piše in izrisuje grafikone XLS in XLSX brez avtomatizacije Excel