Otvorite prora?unsku tablicu, kliknite na ?eliju koja prikazuje 2026-06-19, i traka formule i dalje ?ita datum. Pro?itajte istu ?eliju iz Delphi-ju i dobit ?ete broj 46192. Oba prikaza su to?na, jer Excel nikada nije pohranio datum u tu ?eliju. Pohranio je serijski broj, broj dana, i prilo?io oblik broja koji govori zaslonu da prika?e taj broj kao kalendarski datum. U vrijednosti ?elije nema tipa datuma. Postoji broj i pravilo prikaza, a pravilo prikaza je jedina stvar koja razlikuje datum od obi?ne koli?ine
To razdvajanje je korijen svakog buga s datumom koji knji?nica za prora?unske tablice mora izbje?i. Sam serijski broj ne govori koji je dan, jer ne govori ?to je bio nulti dan. Isti broj ozna?ava dva datuma s razmakom od ?etiri godine ovisno o jednoj zastavici radne knjige. A broj koji bi se trebao pro?itati kao datum pro?itat ?e se kao obi?na koli?ina osim ako ne?to ne pregleda njegov format i prepozna uzorak datuma. Tako je izgra?en model datuma u HotXLS-u, i to s razlogom
?elija s datumom je broj plus format
Excel pohranjuje datum kao broj dana od epohe, s vremenom dana u decimalnom dijelu. Podne na serijskom broju nosi .5. Cjelobrojni dio je broj dana. Ni?ta u pohranjenoj vrijednosti ne ozna?ava je kao vremensku. Ono ?to je ozna?ava je format broja ?elije: ECMA-376 to naziva numFmt, a ?elija ?iji format koda ispisuje uzorak datuma ili vremena prikazuje se kao datum. Skinite format i ista ?elija prikazuje broj; temeljna vrijednost se nikada nije promijenila
Zbog toga ?itanje vrijednosti ?elije daje Variant koji mo?e biti varDate ili obi?an Double, i za?to je format broja na istoj ?eliji signal koji odlu?uje ?to je tre?a strana mislila. Kada HotXLS otvori XLSX datoteku, ?elija prenosi i svoju vrijednost Value i svoj indeks formata broja NumberFormatIndex u TXLSXCell, a indeks formata je ono ?to konzultirate kako biste saznali je li broj datum
var
Book: TXLSXWorkbook;
Cell: TXLSXCell;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('timesheet.xlsx') <> 1 then
raise Exception.Create('Cannot open workbook');
Cell := Book.Sheets[0].Cells[1, 1]; // row 1, col 1 (1-based)
// Value may arrive as varDate or as a plain numeric serial;
// the format index is the signal that tells them apart.
Writeln('raw value : ', VarToStr(Cell.Value));
Writeln('numFmt idx: ', Cell.NumberFormatIndex);
Writeln('format : ', Cell.NumberFormat);
finally
Book.Free;
end;
end;
Dvije epohe, s razmakom od 1462 dana
Zadani sustav datuma, onaj koji koristi svaka radna knjiga na Windowsima, broji od samog kraja 1899. godine, tako da serijski broj 1 falls on the first day of 1900. Drugi sustav potje?e s ranog Macintosh ra?unala i broji od po?etka 1904. godine, tako da je njegov serijski broj 1 ?etiri godine i jedan dan kasnije. Radna knjiga bilje?i koji sustav koristi u jednoj zastavici. U OOXML paketu ta je zastavica date1904 na dijelu radne knjige; HotXLS je prikazuje kao svojstvo Date1904 radne knjige
Razmak izme?u dviju epoha je to?no 1462 dana. To su ?etiri kalendarske godine, tri od 365 dana i jedna od 366 dana, ukupno 1461 dan, plus jo? jedan dan za pomak izme?u dviju konvencija nultog dana. Broj je fiksan i mo?ete ga nositi u glavi. Njegova va?nost je u tome ?to nije nula. Serijski broj kopiran iz radne knjige iz 1904. i interpretiran pod pravilima iz 1900., ili obrnuto, smje?ta svaki datum 1462 dana dalje, ?to se predstavlja kao datumi koji su pogre?ni za ne?to vi?e od ?etiri godine i lako se mo?e zamijeniti za o?te?ene podatke
Budu?i da je Delphi-jev vlastiti TDateTime usidren na konvenciju iz 1900. godine, knji?nica koja mapira Excel serijske brojeve na TDateTime mora napraviti pomak za 1462 u oba smjera kad god radna knjiga ima zastavicu 1904. ?itaju?i serijski broj iz 1904., oduzmite 1462 prije nego ?to ga tretirate kao TDateTime; pi?u?i TDateTime u radnu knjigu iz 1904., oduzmite 1462 od serijskog broja kako bi Excel prikazao dan koji ste zamislili. HotXLS primjenjuje ovaj pomak interno kada serijalizira vrijednosti datuma za radnu knjigu ?iji je Date1904 postavljen, tako da se vrijednost koju dodijelite kao TDateTime vra?a na isti kalendarski dan na zaslonu
Namjerna neobi?nost prijestupne godine 1900.
Postoji poznata neobi?nost u sustavu 1900. Excel tretira 1900. kao prijestupnu godinu i prihva?a 29. velja?e 1900. kao stvarni datum, serijski broj 60. Godina 1900. nije bila prijestupna godina, jer su sekularne godine prijestupne samo kada su djeljive s 400, a 1900. nije. Fantomski dan je namjerno kompatibilno pona?anje naslije?eno iz rane prora?unske tablice koja je isporu?ena s tim bugom, zadr?ano od tada kako bi serijska aritmetika ostala identi?na kroz desetlje?a datoteka
Prakti?na posljedica je mala, ali stvarna: za bilo koji datum na dan ili nakon 1. o?ujka 1900. godine, serijski broj je za jedan ve?i nego ?to bi dala strogo to?na raspodjela dana, jer je nepostoje?i 29. velja?e potro?io broj. Knji?nica prora?unskih tablica reproducira tu neobi?nost umjesto da je ispravlja, jer je uskla?ivanje s Excelovom aritmetikom cijeli posao. Njezino ispravljanje stavilo bi svaki moderni datum jedan dan dalje od onoga ?to Excel prikazuje, ?to je lo?iji ishod nego no?enje ?etrdeset tisu?a dana starog odstupanja za jedan koje nijedan stvarni datum u poslovnoj uporabi nikada ne doti?e. Sustav 1904 nema ekvivalentan fantomski dan, ?to je jedan od razloga za?to su mu neke tvrtke povijesno davale prednost
Otkrivanje datuma iz numFmt
Kada broj stigne iz datoteke koju je napisao netko drugi, njegov format je jedini dokaz da se radi o datumu. ECMA-376 dodjeljuje blok ugra?enih ID-ova formata ?ije je zna?enje fiksirano specifikacijom, a formati datuma i vremena zauzimaju poznate raspone. ID-ovi od 14 do 22 su op?i lokalni formati datuma i vremena, poznati m/d/yyyy, h:mm i njihovi srodnici. ID-ovi od 45 do 47 su formati proteklog vremena. Jo? dva pojasa, od 27 do 36 i od 50 do 58, su specifi?ni lokalni formati datuma i vremena koji se koriste za CJK kalendare, definirani u ECMA-376 18.8.30. ?elija ?iji ID formata broja spada u bilo koji od ovih raspona je ?elija datuma ili vremena
Ugra?eni ID-ovi pokrivaju uobi?ajene slu?ajeve, ali ne i prilago?ene. Kada radna knjiga definira vlastiti kod formata, recimo nestandardni redoslijed ili lokalizirani naziv mjeseca, ID je iznad ugra?enog raspona i upu?uje na tablicu formata brojeva radne knjige. Za njih prepoznavanje datuma zna?i ?itanje niza koda formata i tra?enje tokena datuma. HotXLS spaja obje provjere u jedan interni predikat, XlsxNumFmtIsDate, koji odmah vra?a true za ugra?ene raspone datuma, a ina?e analizira prilago?eni kod formata kroz XlsxFormatCodeIsDate. Javno dostupna strana toga je niz NumberFormat ?elije i njezin indeks NumberFormatIndex, koji vam daju i razrije?eni kod formata i ID koji trebate testirati
Zašto parser formata ne može samo tražiti d i m
Analiza koda formata za tokene datuma izgleda jednostavno dok se ne sjetite ?to jo? ?ivi u formatu broja. Naivna potraga za slovima koja ozna?avaju datume, d, m, y, h i s za dan, mjesec, godinu, sat i sekundu, pogre?no ?e se aktivirati na dvjema strukturama koje uop?e nisu tokeni datuma
Prvi je literal citiranog niza. Format broja mo?e ugraditi literalni tekst u dvostruke navodnike, pa financijski format poput #,##0 "MM" dodaje znakove M i M broju bez ikakvog vremenskog zna?enja. Skener koji broji slova unutar navodnika kao tokene mjeseca pogre?no bi ozna?io taj valutni format kao datum. Drugi je odjeljak u zagradama. Formati brojeva nose direktive u uglatim zagradama, nazive boja poput [Red], uvjete usporedbe poput [>1000], lokalne oznake i markere proteklog vremena [h] i [mm]. Neki sadr?aji u zagradama dr?e slova datuma, a neki ne, a tretiranje teksta u zagradama isto kao i tijela formata dovodi i do la?no pozitivnih rezultata i do propu?tenih slu?ajeva
Ispravan parser prolazi kroz kod formata znak po znak, prate?i nalazi li se unutar citiranog literala i koliko je duboko unutar ugnije??enih zagrada, a tako?er po?tuje i kosu crtu (backslash) koja citira jedan sljede?i znak. Samo neizbje?no slovo datuma prona?eno izvan bilo kojeg znakovnog literala i izvan bilo kojeg odjeljka u zagradi ra?una se kao stvarni token datuma. To je upravo na?in na koji skenira XlsxFormatCodeIsDate: navodnik prebacuje stanje unutar literala koje potiskuje otkrivanje tokena do zatvaraju?eg navodnika, kosa crta preska?e sljede?i znak, a broja? dubine zagrada potiskuje otkrivanje unutar [...] segmenata. Rezultat je da se #,##0 "MM" ispravno ?ita kao format broja, dok se kratki prilago?eni kod koji ne sadr?i ni?ta osim jednog m ili d izvan navodnika i dalje ispravno prepoznaje kao datum
Čitanje datuma iz datoteka trećih strana
Sve gore navedeno konvergira na jedan tijek rada: pretvaranje broja koji je napisala neka druga aplikacija natrag u datum kojem mo?ete vjerovati. Serijski broj daje vam broj dana, zastavica radne knjige Date1904 govori iz koje se epohe mjeri broj, a ID formata broja ?elije ili prilago?eni kod jedini je dokaz da je broj uop?e bio namijenjen kao datum. Ispustite bilo koji od ova tri elementa i dobit ?ete uvjerljiv pogre?an odgovor umjesto vidljive pogre?ke
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Cell: TXLSXCell;
r: Integer;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('vendor-export.xlsx') <> 1 then
raise Exception.Create('Cannot open export');
// The 1904 flag is workbook-wide: read it once, apply it to
// every serial the workbook hands back.
if Book.Date1904 then
Writeln('workbook uses the 1904 date system')
else
Writeln('workbook uses the 1900 date system');
Sheet := Book.Sheets[0];
for r := 1 to 10 do
begin
Cell := Sheet.Cells[r, 1];
// A date is only a date when its format says so; the same numeric
// value with a plain format is just a quantity.
Writeln(Format('row %d value=%s numFmt=%d code="%s"',
[r, VarToStr(Cell.Value), Cell.NumberFormatIndex, Cell.NumberFormat]));
end;
finally
Book.Free;
end;
end;
Naslije?ena BIFF strana ima jednu dodatnu zamku vrijednu spomena. U starijem .xls toku, niz susjednih numeri?kih ?elija mo?e se spakirati u jedan zapis s vi?e ?elija, MULRK, koji pohranjuje nekoliko vrijednosti sa svojim referencama formata u jednoj strukturi. Stanice s datumima pohranjene na taj na?in nisu manje datumi zbog toga ?to su spakirane, pa isti test ID formata mora dosegnuti unutar zapisa s vi?e ?elija i primijeniti se po ?eliji, a pomak iz 1904. i dalje vlada svakim serijskim brojem koji daje. ?ita? koji pregledava samo samostalne zapise brojeva, a preska?e spakirane, tiho ?e pretvoriti stupac datuma u stupac cijelih brojeva
Pretvaranje serijskih brojeva u TDateTime u praksi
Jednom kada provjera formata potvrdi datum i kada je poznata zastavica Date1904, pretvorba je mehani?ka. Vrijednost koju HotXLS ve? vra?a kao varDate je TDateTime koji mo?ete izravno koristiti. Vrijednost koja sti?e kao obi?an Double, ?to se doga?a kada je izvor zapisao serijski broj bez prepoznatog formata datuma, pretvara se ?itanjem kao broj dana na osi 1900 i, za radnu knjigu iz 1904., prvim oduzimanjem pomaka od 1462 dana kako bi se epohe uskladile. Idu?i drugim putem, dodjeljivanje TDateTime ?eliji pohranjuje serijski broj temeljen na 1900., a HotXLS primjenjuje isti pomak od 1462 dana pri spremanju kada radna knjiga ima zastavicu 1904., tako da spremljena datoteka prikazuje datum koji ste namjeravali, a ne onaj koji pluta ?etiri godine dalje
Postavite zastavicu namjerno kada generirate radnu knjigu. Zadana vrijednost ostavlja Date1904 neto?nim, ?to odgovara Excelu za Windowse i gotovo je uvijek ono ?to ?elite; postavite je na to?no samo kada reproducirate radnu knjigu podrijetlom s Maca ili kada nizvodni sustav izri?ito o?ekuje os 1904. Jedno pravilo koje sprje?ava cijelu klasu ?etverogodi?njih pogre?aka je dosljednost: odaberite epohu jednom po radnoj knjizi, zapi?ite svaki datum pod njom i pro?itajte svaki serijski broj pod zastavicom koju datoteka stvarno nosi
Datumi su jedan stupac u ?iroj pri?i o tome ?to ?elija doista sadr?i. Susjedni sloj metapodataka, naslov, autor i vremenske oznake koji putuju uz mre?u, pokriven je u na?em ?lanku o metapodacima radne knjige i svojstvima dokumenta, gdje se iste vrijednosti Created i Modified pohranjuju kao TDateTime s istom konvencijom nepode?eno-jednako-nula. Kada je datum rezultat izra?una, a ne pohranjena vrijednost, pravila procjene u na?em ?lanku o pogonu formula i prilago?enim funkcijama odre?uju serijski broj koji format zatim prikazuje. Obje rade na istom modelu datuma koji se isporu?uje u softveru HotXLS Component za Delphi i C++Builder, koji ?ita i pi?e Excel XLS i XLSX datume bez Excel automatizacije