UNIX Time - Predstavljanje datuma i vremena na računarima
Uvod
Posle prethodnog članka u kome smo se bavili problematikom predstavljanja znakova u računarskim sistemima, reklo bi se da je diskusija o formatima za predstavljanje datuma i vremena, sledeći prirodan korak.
Sa računarskim kalendarima (a pogotovo sa časovnicima), susrećemo se svakodnevno, i pri tom deluje da računarski kalendari i časovnici funkcionišu vrlo slično kao kalendari i časovnici iz spoljnjeg sveta, ali, uz podrobnije udubljivanje u problematiku, nije teško uvideti da predstavljanje vremena na računarima nije 'baš skroz trivijalan' zadatak ....
Osnovna problematika
Ako se usmerimo na uobičajeni zapis vremenskog trenutka, u odnosu na početak Nove ere ....
.... jasno se mogu zapaziti dva podatka:
- datum (i)
- vreme
.... pri čemu su u pitanju složeni podaci, od kojih se svaki može dalje raščlaniti na nekoliko celobrojnih vrednosti (datum - na dan, mesec i godinu; vreme - na sat minut i sekund).
Isti podaci se mogu predstaviti i drugačije ....
.... a ako bismo izabrali format koji bi operativni sistem tipično koristio da nije preveden na srpski jezik, prikaz bi ponovo bio drugačiji:
Osnovni podatak (vremenski trenutak), uvek je isti, ali, u širem kontekstu, ako onaj ko tumači podatke nije upoznat sa formatom zapisa, vrlo lako može doći do zabune.
Zabuna najčešće nastaje sa mesecima i danima, kada je redni broj dana manji od 12, ali (u praktičnom smislu), nije previše teško uočiti razlike:
07.08.
- dan je na prvom mestu, mesec na drugom; za razdvajanje se koristi znak.
08/07
- na prvom mestu je mesec, na drugom mestu je dan; za razdvajanje se koristi znak/
U oba primera - datum je "sedmi avgust".
Međutim, usled navika - pogotovo kada se podaci unose i obrađuju 'ručno' i 'brzinski' (a posebno u situacijama kada je količina podataka za obradu velika, pa u svemu i zamor postane faktor) - neretko dolazi do grešaka ako korisnik naiđe na format na koji nije naviknut.
Nedoumice bi se svakako mogle rešiti upotrebom međunarodnog standarda koji nalaže da se u zapisu datuma prvo navodi godina, zatim mesec i, na kraju, dan (kao i to da se sati beleže isključivo u formatu od 0 do 23) ....
.... ali, praksa (očekivano) pokazuje da se različite države (odnosno, različite kulture), ne odriču tek tako navika stečenih tokom (doslovno) više stotina godina.
Ipak, ono što smo naveli do sada, nije jedini (pa čak ni osnovni) problem koji se tiče 'tehnikalija'.
Kako god da podaci treba da budu formatirani u ispisu, klasa za beleženje datuma i vremena koja bi bila definisana u sledećem obliku ....
.... predstavljala bi adekvatan i univerzalan način da se podaci čuvaju i naknadno ispisuju u bilo kom željenom formatu (mi smo izabrali format sa prve slike).
Međutim, ovakav 'prirodni' ('ljudski'/'kalendarski') način predstavljanja vremenskih trenutaka, ne omogućava jednostavno poređenje, sabiranje i oduzimanje datuma i vremena, a to su operacije koje se na računarima širom sveta dešavaju više stotina miliona puta - u toku svakog dana (verujemo da je procena više nego konzervativna).
Sa druge strane, ako se datum i vreme svedu na broj sekundi koje su protekle od .... "nekog" trenutka, različiti datumi se lako mogu porediti / sabirati / oduzimati ....
I jedan i drugi pristup imaju: i prednosti, i nedostatke, međutim (u praktičnom smislu), kao osnovni format se koristi "broj sekundi", ali, beleže se i ostali podaci. *
U najosnovnijem smislu, beleženje (i interpretacija) datuma, funkcioniše na sledeći način ....
S obzirom na to da jedan minut predstavlja 60 sekundi, vreme 01:30
može se zapisati kao 90
(sekundi), dok se vreme 01:01:30
može zapisati kao 3690
(sekundi), pri čemu smo računali da jedan sat predstavlja 60 minuta, odnosno, 3600 sekundi.
Sve dok je broj sekundi relativno mali (pogotovo ako je osetno manji od 86400
, što predstavlja broj sekundi u jednom danu), neće biti problema sa poređenjem, dodavanjem, oduzimanjem i pretvaranjem intervala iz oblika "celobrojne promenljive koja predstavlja ukupan broj sekundi", u zapis sati, minuta i sekundi.
Međutim, ako je broj sekundi relativno velik (recimo - pet miliona), postavlja se pitanje šta takav podatak zapravo predstavlja?
Navedena vrednost uvek će biti: 57 dana, 20 sati, 53 minuta i 20 sekundi
, ali, ako data vrednost treba da predstavlja konkretan kalendarski datum, mora se precizirati šta u 'odbrojavanju sekundi' znači vrednost 0
.
Još koja reč o prednostima i nedostacima različitih formata
Pre nego što pređemo na detaljnu diskusiju o osnovnom formatu za zapis datuma i vremena, koji se tipično koristi na računarima, osvrnimo se još jednom (ukratko) na prednosti i nedostatke različitih formata.
Osnovni nedostatak zapisa vremenskog trenutka u obliku (mili)sekundi koje su protekle od nekog (manje-više proizvoljno izabranog) datuma, je - "neprirodnost" takvog postupka, dok je osnovni nedostatak zapisa datuma i vremena preko šest nezavisnih podataka (GGGG-MM-DD SS-Mi-ss) - otežano obavljanje operacija poređenja, sabiranja, oduzimanja i sl.
Prednosti formata "dan-mesec-godina sat-minut-sekund" su: preglednost i prirodnost (s tim da je najverovatnije samo u pitanju stvar navike).
Što se tiče prednosti formata koji podrazumeva 'odbrojavanje sekundi' (ili milisekundi), ne moramo tražiti bolji primer od pretrage: ako su datumi u bazi podataka zapisani na "prirodan"/"ljudski" način (preko više nezavisnih celobrojnih vrednosti), pretraga slogova koji su vezani za datume pre ili posle određenog datuma, neće biti trivijalan zadatak, dok - ako su vremena i datumi zapisani preko jedinstvene celobrojne vrednosti - nije teško urediti indeks - strukturu podataka koja omogućava da se slogovi efikasno pretražuju preko 'vremenskih trenutaka' (uz upotrebu hash mapa ili dobro poznate binarne pretrage).
Kao što smo već nagovestili, u praksi (u klasama za zapis vremenskih trenutaka), tipično se definišu svi navedeni podaci, s tim da je timestamp (broj sekundi proteklih od početka epohe) * - glavni podatak, a ostala polja se popunjavaju preko funkcija za pretvaranje timestamp-a u format koji je prilagođen ljudima. **
U prethodnom primeru, ako bi se podatak 5x106
pojavio samostalno (pri čemu bi takav podatak trebalo da "nekako" predstavlja datum - a dodatnih informacija nema), može se pretpostaviti da odbrojavanje počinje od 01. januara u ponoć, i stoga nije teško ustanoviti da se broj sekundi 5x106
poklapa sa 26. februarom, međutim - i dalje nije poznato:
- kojoj godini pripada navedeni datum
- šta bi (inače) bilo, sa (na primer), 77. danom u godini - da li bi datum bio 17.03. ili 18.03. (jer nije poznato da li je godina prestupna)
UNIX timestamp
U računarskim sistemima, termin timestamp (u prevodu sa engleskog - "vremenski žig"), predstavlja broj sekundi između "početka epohe" i određenog (proizvoljnog) trenutka, iz čega praktično proizilazi da se problematika predstavljanja proizvoljnog datuma i vremena u obliku koji je razumljiv ljudima (i prilagođen određenoj kulturi, u smislu dan.mesec.godina.
, ili mesec/dan/godina
" sa ili bez vodećih nula i sl), svodi se na beleženje i interpretaciju timestamp-a.
U prethodno navedenom kontekstu, početak epohe (timestamp 0), može označavati različite datume, a poznatiji od takvih datuma u računarskoj tehnici (sa naznakom u kom sistemu se koriste), su:
- 01.01.AD - .NET
- 01.01.1601. - NTFS, COBOL, Windows
- 01.01.1900. - Network Time Protocol
- 01.01.1970. - UNIX time (takođe se koristi i u GNU/Linux distribucijama, operativnim sistemima koji su zasnovani na BSD-u, kao i u većini programskih jezika)
Kao veliki poštovaoci Linux-a (a takođe i programskih jezika), za upoznavanje sa formatom za predstavljanje datuma i vremena na računarima, izabrali smo upravo UNIX epohu.
Predstavljanje datuma posle 01.01.1970.
Prethodno je navedeno da UNIX epoha počinje u ponoć 01.01.1970. po UTC-u, * što praktično znači da se timestamp koji smo koristili (5000000), poklapa sa 26.02.1970.
u 20:53:20
.
Ukoliko je poznat timestamp, nije teško izračunati datum, međutim, kao opštiji (i poučniji) primer pronalaženja datuma koji odgovara timestamp-u, koristićemo timestamp: 50x106
(50000000; "jedna nula više" u odnosu na prethodni timestamp i, što je važnije (zarad boljeg razumevanja tematike) - timestamp koji se ne uklapa "u istu godinu sa timestamp-om 0").
Datum koji odgovara novom timestamp-u je 02.08.1971.
, što je rezultat do koga se dolazi preko relativno jednostavnog postupka čije ćemo osnovne smernice predstaviti direktno u nastavku, a detalje, u narednim odeljcima ....
Prvo treba podeliti * timestamp sa 86400
(da se podsetimo, radi se o vrednosti koja predstavlja broj sekundi u jednom danu) - i potom treba dodati 1. **
Umesto dodavanja jedinice, u implementaciji se prosto može napisati:
Preko gornje formule, pravilno smo odredili koliko je dana proteklo od 01.01.1970
u ponoć, ** a za određivanje tačnog datuma, moguće je koristiti jednu od dve opcije:
- pomoćne tabele
- prilično komplikovanu aritmetiku ***
(Za početak ćemo se držati jednostavnijeg algoritma koji podrazumeva korišćenje pomoćnih tabela.)
Kada se 50 miliona
podeli sa 86400
, dobija se 579 (578 + 1)
- što predstavlja broj 'započetih dana', posmatrano u odnosu na početak 1970.
Kada se od broja 579
oduzme 365
(1970. nije bila prestupna godina), dobija se 214
, a budući da smo (samo) jednom oduzimali broj dana u godini (pri čemu je preostala razlika koja je manja od broja dana u godini), može se zaključiti da timestamp odgovara godini - 1971
.
Određivanje godine
U svojstvu primera, privremeno ćemo koristiti još veći timestamp: 150 x 106
, kome odgovara godina koja se (očigledno) ne poklapa, ni sa 1970, ni sa 1971.
Timestamp se prvo deli sa 86400 (u novom primeru, dobija se rezultat 1737 (1736 dana + 1 dan)), i potom se redom oduzima 365 ili 366 (dana), za svaku godinu, sve dok broj dana ne postane manji ili jednak ("mogućem") broju dana u godini:
- 1970: 1737 - 365 = 1372 -> prelazak u 1971.
- 1971: 1372 - 365 = 1007 -> prelazak u 1972.
- 1972: 1007 - 366 * = 641 -> prelazak u 1973.
- 1973: 641 - 365 = 276 -> prelazak u 1974.
- 1974: 276 je manje od 365 (i stoga nema daljeg oduzimanja) -> godina 'ostaje' 1974.
Zarad prikaza procedure za određivanje datuma (koja sledi), vraćamo se na timestamp 5000000
: prethodno smo ustanovili da navedenom timestamp-u odgovara godina 1971
, ustanovili smo takođe da je preostalo još 214
dana - i potrebno je ustanoviti datum (tj. dan i mesec).
Određivanje datuma
Opšti postupak preko koga se može ustanoviti sa kojim datumom se poklapa određeni dan u godini, sastoji se iz sledećih koraka:
- za svaki mesec (redom, počevši od januara), potrebno je proveriti da li je ukupan broj 'preostalih' dana, veći od zbira: dana u trenutnom mesecu, i ukupnog broja dana u prethodnim mesecima
- ako je ukupan broj dana veći od zbira, prelazi se u sledeći mesec, a algoritam se vraća na prethodni korak (ispitivanje uslova)
- ako je ukupan broj dana manji ili jednak, mesec se određuje shodno tome koliko puta je algoritam 'prešao u sledeći mesec', a dan se računa kao razlika ukupnog broja dana, i ukupnog broja dana za prvih n meseci (pri čemu važi:
n = pronađeni_mesec - 1
)
Postupak se lakše može razumeti preko primera (koji već koristimo):
- ukupan broj dana (214), veći je od 31 (broj dana u 1. mesecu), što znači da traženi datum nije u januaru
- ukupan broj dana (214) veći je i od 59 (28 dana u 2. mesecu (s obzirom na to da godina nije prestupna) + broj dana u prethodnim mesecima (31)), što to znači da traženi datum nije ni u februaru
- ukupan broj dana (214) veći je i od 90 (31 dan u 3. mesecu + broj dana u prethodnim mesecima (59)), što to znači da traženi datum nije ni u martu ....
Preskočićemo određeni raspon meseci (da primer ne bi postao 'zamoran' za praćenje) i, na kraju, pošto je postupak prelaženja u sledeći mesec ponovljen 7 puta, ustanovili smo sledeće:
- 214. dan 1971. pripada avgustu
- u pitanju je 2. dan u mesecu *
Kao što smo već pomenuli, postupak se može uprostiti preko pomoćne tabele koja odgovara mesecima u godini (u ovom slučaju, 1970) ....
Prvi red (komentar), sadrži redni broj meseca, tj. indekse niza; drugi red (takođe komentar), sadrži brojeve dana u svakom mesecu. Treći red sadrži broj celih dana između početka godine i kraja određenog meseca, i stoga se posmatranjem tabele, lako može uvideti sledeće:
- mesec se nalazi na prvom indeksu koji odgovara prvoj vrednosti iz trećeg reda, koja je veća od broja dana (prva vrednost veća od 214 je 243 i poklapa se sa 8. mesecom)
- dan u mesecu dobija se oduzimanjem broja dana (214) i vrednosti iz prethodne kolone (212).
Preko (idejno) slične tabele, može se uprostiti i postupak pronalaženja godine:
.... i (naravno), da bi postupak imao pravi smisao, pretraga ne sme biti linearna (sve ćemo detaljnije razmotriti u nastavku, kada se budemo bavili implementacijom algoritma).
Pogledajmo usput i nekoliko zanimljivih kombinacija timestamp-a i datuma:
Predstavljanje datuma pre 01.01.1970.
Uz napomenu da razumemo, da predstavljanje datuma pre početka UNIX epohe (koje odlikuju negativne vrednosti timestamp-a), svakako može delovati pomalo neintuitivno pri prvom susretu, primetićemo da je postupak i dalje veoma sličan u idejnom smislu - samo što se obavlja "u obrnutom smeru".
Kao primer, koristićemo timestamp -35x106
(-35000000).
Ovoga puta, pri deljenju sa 86400
, ne dodaje se 1.
Bilo koja vrednost manja od 0, automatski znači da se obavezno prelazi unazad bar u 1969. godinu, a budući da je dobijeni količnik 405
- veći od 365, znači da smo se vratili u 1968.
Kada se od 405
oduzme 365
, preostaje 40
dana.
Budući da decembar ima 31 dan, posle oduzimanja (40 - 31), dobija se razlika 9
i može se zaključiti da je mesec koji odgovara datumu - novembar
.
Dan u mesecu, dobija se kada se od ukupnog broja dana u pronađenom mesecu (30), oduzme preostali broj dana (9), i stoga je traženi datum: 21.11.1968
.
Zanimljivi istorijski fenomeni vezani za predstavljanje vremena na računarima
Pre nego što pređemo na implementaciju algoritama koje smo prethodno opisivali, osvrnućemo se i na dva zanimljiva fenomena koji se tiču zapisa vremena na računarima ....
Problem 2000 ("Milenijumska buba"/Y2K)
Nekada čuvena i ne-baš-malo zabrinjavajuća "milenijumska buba"/"Y2K" (problem koji je u međuvremenu donekle zaboravljen), predstavlja jedan od zanimljivijih fenomena u računarskoj istoriji (koga se stariji čitaoci, verujemo, sećaju), ali, u pitanju je situacija koja nije imala toliko veze sa UNIX timestamp-om (ili nekim drugim), koliko sa opštim merama uštede pri zapisivanju datuma.
Problematika je (ukratko) sledeća: usled izrazito velikih cena memorije u ranom periodu eksploatacije računara, programeri su bili prinuđeni da se 'dovijaju' na razne načine i - u smislu "praktično neizbežnih mera" * - "stradao" je (između ostalog), i zapis datuma, odnosno (da budemo precizni), zarad uštede je odlučeno da pri beleženju datuma neće biti korišćen četvorocifreni zapis godina ("1958"), već dvocifreni ("58"), pri čemu će se prve dve cifre ("19") - "podrazumevati".
Verujemo da bi mnogi čitaoci mogli pomisliti da je u prethodno navedenim okolnostima, jedno od (naj)češće postavljanih pitanja bilo: "šta će se desiti kada godina dođe do '00'", međutim, zanimljivo je primetiti da takvom pitanju naizgled nije pridavan veći značaj (tokom jednog dužeg vremenskog perioda).
U prvo vreme, datumi su beleženi samo zarad ažurnosti i nije se očekivalo da će tako zabeleženi datumi učestvovati u bilo kakvom "odlučivanju" (na primer: u obračunavanju kamate na bankovnim računima shodno datumu, u pokretanju industrijskih procesa u određeno vreme - i drugim sličnim poduhvatima), ali, kao što verovatno već očekujete da ćemo navesti, nedugo posle prvih godina, računari su počeli da se koriste za kontrolu procesa u najrazličitijim oblastima ljudske delatnosti (ekonomija, industrija, energetika, zdravstvo, saobraćaj i manje-više sve ostalo što vam može pasti na pamet), zabeleženi datumi su postali "faktor odlučivanja", a problem je (bio) - u tome što format zapisa (datuma), nije promenjen.
Zarad preciznosti, navedimo šta je tačno problem u tehničkom smislu: iako su u međuvremenu mnogi od starih programa povučeni iz upotrebe (doduše, mnogi nisu, i postoji procena da je krajem veka u opticaju ostalo više desetina (pa čak i stotina) hiljada starih programa "na bitnim mestima"), podaci u bazama podataka - i dalje su bili zapisani onako kako smo već naveli - sa godinom koja je definisana preko dve cifre (a ne četiri).
U praktičnom smislu, moramo (bar donekle) razumeti ljude sa kraja pedesetih i početka šezdesetih godina 20. veka: programeri i drugi stručnjaci su svakako primetili da postoji potencijalni problem koji bi se mogao manifestovati dolaskom 2000. godine, ali - na raspolaganju ni iz daleka nije bilo dovoljno memorije, očekivalo se da prvobitni programi neće dugo opstati u opticaju, ili - ukoliko 'eventualno' programi ostanu u opticaju - prilično iskreno se verovalo u to da će buduće generacije programera biti u stanju da reše sve probleme bez iole veće muke, koristeći se "superiornim tehnologijama budućnosti" (odnosno, da se izrazimo malo jednostavnije - usled kombinacije iskrenog, ali ne-baš-preterano-utemeljenog optimizma i, naravno - lenjosti, koja je uvek "pritajeni faktor" kada god su u pitanju veliki poduhvati - računalo se da za sve "ima vremena").
Međutim, mnogi od prvobitnih programa iz početnog perioda, o(p)stali su u upotrebi mnogo duže nego što se očekivalo, kraj veka se približavao i, u drugoj polovini devedesetih, "već" se uveliko razmišljalo o svemu.
Šta je zapravo moglo da se desi dolaskom 2000?
U pravom smislu reči, odgovor na prethodno pitanje - niko ne zna, jer u svemu ima previše faktora (tj. previše "varijabli"). I dalje ostaju samo nagađanja, ali (u osnovnom smislu), pitanje je bilo: kako će računari odreagovati na godinu sa "dve nule na kraju"? Da li će godina biti shvaćena kao "2000", ili kao "1900"?!
Dvocifreni zapis godina nije problematičan kada je (na primer), u bazi podataka, za određenu osobu kao godina rođenja upisano "68" a kao trenutna godina očitava se "87" - i vreme je da osoba čiji se podaci razmatraju krene na fakultet, ali, šta ako "premotamo film" dvadeset godina unapred, dospemo u godinu "07" (namerno nećemo reći "2007"), i vreme je da se pozove nova generacija đaka u I razred?!
Što se računarskih sistema tiče, i dalje "nema problema": u bazama podataka će biti pronađene sve osobe čija je godina rođenja zabeležena kao "00", i svoj deci rođenoj 2000. uredno će biti prosleđeni pozivi za upis u osnovnu školu.
Ostaje samo pitanje - da li će takvim pozivom biti obuhvaćene i osobe koje su rođene 1900 (jer i osobe rođene 1900. imaju godinu rođenja zabeleženu kao "00")?!
Šalimo se (donekle) i znamo naravno da se pomenuti događaji nisu odigrali u praksi, ali, scenario jeste bio moguć i slična razmatranja bila su omiljena zabava mnogih analitičara i novinara krajem devedesetih godina (a bilo je i karikatura koje prikazuju bake i deke kako zajedno sa dečicom polaze u školu, muške bebe sa sedim bradama i sl).
Za još malo 'istorijskog konteksta', napomenimo da je već sredinom osamdesetih bilo onih koji su, u nešto većoj meri, skretali pažnju na to da bi ipak trebalo "preduzeti nešto po pitanju 2000" (kao što smo rekli, problem je zapravo primećen maltene na početku, ali je isto tako u praktičnom smislu ostao po strani, i u međuvremenu se o svemu nije baš mnogo vodilo računa), međutim, tek je u drugoj polovini devedesetih, situacija za prave "dobila na značaju", a u poslednjih nekoliko godina koje "počinju sa 19" *, zabrinutost je bila legitimna i bilo je brojnih pojedinaca koji civilizaciji, koja (već veoma ozbiljno) zavisi od računara, nisu predviđali svetlu budućnost.
Bilo je čak i pojedinaca koji su se spremali na "scenario kataklizme" (gomilanje namirnica, opremanje atomskih skloništa i sl), mediji su situaciju koristili da preko senzacionalističkih naslova povećaju tiraž, ali, na kraju se sve završilo - ne samo bez kataklizmičkih posledica (i/ili "slanja starijih sugrađana u školske klupe" i sl), već, bez gotovo ikakvih iole ozbiljnijih primećenih incidenata.
Naravno, "iza zavese", brojni programeri širom sveta bavili su se saniranjem zastarelih kodova, radilo se vredno, u sve je uloženo i mnogo novca (procenjuje se da je u pitanju manji broj stotina milijardi dolara), šteta je (ipak) predupređena, i čovečanstvo nije doživelo predviđenu kataklizmu (bar ne onu koja se ticala kraha računarskih sistema).
Problem 2038.
Kao što je 'milenijumska buba' pre početka 2000. ponešto zadavala glavobolje korisnicima računara i stručnjacima koji su učestvovali u rešavanju problema - i izazivala znatiželju, za 2038. godinu i UNIX timestamp vezuje se donekle slična, ali, ipak mnogo, mnogo manje dramatična situacija.
U četvrtak, 19. januara 2038, u 03:14:07 po UTC-u, doći će do resetovanja 32-bitnih brojača koji su zaduženi za UNIX timestamp.
U pomenutom trenutku, brojači će dostići vrednost 2147483647
, što predstavlja najveću vrednost koja se može dodeliti označenoj 32-bitnoj celobrojnoj promenljivoj.
I ovoga puta, za očekivati je da sve kritične (i ostale iole bitne) implementacije, budu "zakrpljene", što u većini slučajeva podrazumeva prebacivanje na 64-bitne brojače (mnogo toga već jeste urađeno, a vremena za sada ima još uvek više nego dovoljno).
Prelaskom na 64-bitne brojače, onaj deo čovečanstva koji (u smislu svojih računarskih potreba), ima veze sa UNIX timestamp-om, moći će ipak da 'odahne' (usudićemo se da kažemo da je tako). :)
Naime, 64-bitni brojači omogućiće inkrementaciju timestamp-ova u sledećih ~292 milijardi godina!
Računica koja se (u opštem smislu) tiče implementacije timestamp brojača preko različitih veličina celobrojnih promenljivih, vrlo je zanimljiva:
- 16-bitni timestamp ne omogućava odbrojavanje sekundi ni u trajanju od jednog dana (65535 < 86400)!
- preko 32-bitnog timestampa, moguće je odbrojavati sekunde u trajanju od ~68 godina
- preko 64-bitnog timestampa, moguće je odbrojavati sekunde u trajanju od .... tolikom da većina astronoma predviđa kraj Sunčevog sistema i ljudske civilizacije, mnogo (mnogo!) pre nego što 64-bitni brojači dođu do kraja (konkretna vrednost je: prethodno pomenutih ~292 milijardi godina)
Ako do sada niste razumeli razliku u smeštajnom kapacitetu 16-bitnih, 32-bitnih i 64-bitnih promenljivih, verujemo da sada razumete.
Kao što smo ranije nagovestili, u nastavku ćemo se pozabaviti nekolicinom popularnih algoritama koji se tiču obrade datuma i vremena na računarima (a najviše pažnje ćemo posvetiti, na kraju članka, implementaciji algoritma za pretvaranje timestamp-a u strukturu podataka koja prikazuje datum i vreme).
Algoritam #1 - Kreiranje tajmera
Za početak, razmotrićemo algoritam koji prikazuje očiglednu prednost upotrebe timestamp-a pri beleženju vremenskih trenutaka.
Pretpostavićemo da je potrebno napraviti tajmer, koji prikazuje koliko je sati : minuta : sekundi
proteklo od trenutka kada je tajmer pokrenut.
Zadatak se rešava jednostavno:
Preko promenljive t1
, očitava se timestamp koji odgovara početnom trenutku ....
.... i potom, svaki put kada je potrebno prikazati proteklo vreme, očitava se novi timestamp:
Prostim oduzimanjem dve vrednosti dobija se vreme u sekundama (koje je proteklo između dva očitavanja), a preko pomoćne funkcije, izračunata vrednost se lako može predstaviti u obliku sati, minuta i sekundi:
Algoritam #2 - Računanje starosti osobe
Razmotrićemo i jedan veoma uobičajen algoritam u kome direktna upotreba timestamp-a nije od prevelike pomoći, već je praktičnije operisati nad strukturom "godina-mesec-dan".
Kada je u pitanju određivanje starosti osobe, prostim oduzimanjem timestamp-a koji odgovara trenutku rođenja, od trenutnog timestamp-a, može se dobiti 'broj dana', ali, pitanje je šta sa takvom vrednošću dalje raditi?!
Ako se broj dana koji je prethodno izračunat 'nalepi' na početak UNIX epohe, ni u kom slučaju nećemo dobiti korektan rezultat, a ako ostavimo dobijenu razliku tako da nije povezana sa rasporedom prestupnih i neprestupnih godina - rezultat takođe neće biti korektan.
Umesto svega što smo naveli, praktičnije je prvo dva timestamp-a pretvoriti u strukturu datuma koja se koristi svakodnevno ....
.... nakon čega sledi postupak koji je prirodan i uobičajen.
Broj godina lako se dobija prostim oduzimanjem godine rođenja od trenutne godine (2021 - 1979 = 42), posle čega samo još preostaje pitanje, da li dobijenu vrednost treba umanjiti za 1 (ili ne treba).
Dobijeni rezultat potrebno je umanjiti za 1 u sledećim okolnostima:
- ako je trenutni mesec manji od meseca rođenja (ili)
- ako je trenutni mesec isti kao mesec rođenja, a dan je manji od dana rođenja
Pošto smo razmotrili sve bitne detalje algoritma, možemo implementirati i funkciju:
Na kraju, pogledajmo i algoritam za pretvaranje timestamp-a u datum (koji je u celokupnoj diskusiji najbitniji).
Algoritam #3 - pretvaranje UNIX timestamp-a u datum
Iako smo se sa algoritmom za pretvaranje timestamp-a u datum već upoznali (u najvećoj meri), ipak ćemo se ponovo osvrnuti na osnovne ideje pre nego što pređemo na implementaciju:
- timestamp se prvo deli sa 86400, čime se dobija broj 'započetih dana' (posmatrano u odnosu na 1.1.1970. u ponoć)
- računanje godine podrazumeva da se od prethodno dobijene vrednosti oduzima 365 ili 366, * jednom ili više puta - sve dok je preostali broj dana i dalje veći od broja dana u godini:
- ukoliko preostali broj dana dozvoljava oduzimanje (u određenom koraku), dolazi do oduzimanja i pri tom se godina uvećava za 1
- kada preostali broj dana postane manji od broja dana u godini, godina je izračunata kao:
1970 + broj_prelazaka
, nakon čega se prelazi na određivanje meseca
-
za određivanje meseca, primenjuje se idejno sličan postupak, tj. u svakom koraku se od preostalog broja dana oduzima broj dana koji odgovara mesecu do koga je algoritam došao (31 dan za januar, 28 ili 29 za februar, 31 za mart i sl):
- ukoliko preostali broj dana dozvoljava oduzimanje (u određenom koraku), dolazi do oduzimanja i pri tom se mesec uvećava za 1
- kada nastane situacija u kojoj preostali broj dana ne omogućava dalje oduzimanje, mesec je izračunat kao:
1 + broj_prelazaka
, nakon čega preostaje da se odredi dan u mesecu
- dan u mesecu, poklapa se sa preostalom vrednošću
Ceo postupak može se (naravno) ubrzati, korišćenjem pomoćnih tabela koje se pretražuju metodom binarne pretrage, ** nakon čega algoritam postaje sasvim efikasan.
(Kao što rekosmo, postoji i nešto efikasnije rešenje, *** koje nije baš jednostavno za razumevanje, ali, u članku ćemo razmatrati algoritam koji smo prethodno opisali.)
U primerima koje prikazujemo, tabele će biti popunjene ručno (zarad boljeg razumevanja), i potom mapirane automatski, s tim da se tabela koja predstavlja broj dana po godinama, lako može i popuniti automatski (prikazaćemo postupak u nastavku):
Vidimo da smo svakoj godini, odnosno, svakom mesecu, za početak dodelili pripadajući broj dana ....
.... ali, nizovi će biti mapirani tako da na kraju svaki element predstavlja broj proteklih dana:
- u slučaju niza
daniUGodini
, u pitanju je broj celih dana između početka UNIX epohe i kraja određene godine - u slučaju nizova
meseciN
imeseciP
, u pitanju je broj celih dana između početka godine i kraja određenog meseca
U svemu je bitno razumeti sledeće: ako datum pripada (na primer) 1973. godini - zarad oduzimanja treba očitati vrednost koja se poklapa sa godinom 1972 (vrednost 1096), a ako je datum u septembru, treba očitati vrednost koja se poklapa sa 8. mesecom (243, ili 244, u zavisnosti od toga da li je godina prestupna, ili nije).
Za mapiranje nizova možemo koristiti jednostavnu funkciju koja prolazi kroz sve elemente, od drugog do poslednjeg, i dodaje prethodni zbir (sa pozicije i - 1
), na vrednost elementa na poziciji i
.
Za pretragu, možemo koristiti dobro poznati algoritam binarne pretrage.
Naravno, za razliku od nizova meseciN
i meseciP
(u kojima postoji idiosinkratičan i nepravilan sled elemenata), niz daniUGodini
može se kreirati automatski:
Svaki četvrti element dobija vrednost 366.
Dodaćemo još i pomoćnu funkciju koja proverava da li je godina prestupna ....
.... kao i dve pomoćne klase (u C#-u i Javi), odnosno, pomoćnu strukturu u C++ - u * ....
.... i onda možemo početi.
Za početak, timestamp se deli sa 86400
(odnosno, sa brojem sekundi u jednom danu) ....
.... i potom se dobijena vrednost predaje funkciji PronalazenjeGodine
, koja (preko tabele dani
) ....
.... pronalazi godinu koja odgovara unetom timestamp-u.
Ali, ne samo to: vrednost promenljive daniUGodini
takođe će biti umanjena za vrednost iz tabele, koja odgovara pronađenoj godini (s tim - da ponovimo - da se vrednost koja se preuzima iz tabele, nalazi jedno mesto ulevo, odnosno, 'ispod' godine koja je za jedan manja od pronađene).
Na sličan način, preko odgovarajuće funkcije ....
.... može se pronaći i mesec koji odgovara datumu, posle čega nije teško uneti odgovarajuću korekciju, i time dobiti redni broj dana.
Sada možemo sagledati i glavnu funkciju:
Za kraj, preko jednostavnog formulara, možete videti kako sve funkcioniše u praksi.
Formular - UNIX timecode / (datum-vreme)
Donji formular takođe prikazuje i vreme (sate, minute, sekunde) ....
UNIX time stamp
Datum / vreme
.... međutim, implementacija ovakvog formulara, ipak je nešto što ćemo ostaviti našim cenjenim čitaocima koji žele da samostalno isprobaju dodatne opcije.