nav_dugme codeBlog codeBlog
  • početna Početna stranica
  • Sačuvani članci Sačuvani članci
  • Članci
     (spisak)
  • Kontakt
Povratak na vrh stranice

Info & povezani članci Info o članku - dugme

Info

trejler_sat Datum objave: 07.08.2021.

trejler_olovka Poslednja izmena: 04.03.2024.

trejler_dokument Jezici: ----

trejler_teg_narandzasti Težina: 6/10

C++
C#
Java
Python
JavaScript
unix
računari
baze podataka
upit
strukture podataka
it termini
obrada teksta
teorija
zanimljivosti

Povezani članci

ASCII, Unicode i UTF - Predstavljanje znakova na računarimaStrukture podatakaHTTP statusni kodoviOsnove računarske grafike i formati slika u web dizajnuUvod u relacione baze podataka i SQLUvod u PHP i back-end programiranjePostfiksna notacija - kako računari računaju?Izbor prvog programskog jezikaKako napraviti syntax highlighterGNU/Linux - 1. deo - Uvod
Svi članci
Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build and test, it introduces security challenges, and it causes end-user and administrator frustration.
Ray Ozzie

UNIX Time - Predstavljanje datuma i vremena na računarima

Facebook LinkedIn Twitter Viber WhatsApp E-mail
zoom_plus zoom_minus bookmark
početna > Članci > Zanimljivosti

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

		07.08.2021.
18:12:46
Slika 1. - Predstavljanje datuma i vremena na računaru - varijanta 1.

.... jasno se mogu zapaziti dva podatka:

  • datum (i)
  • vreme

.... pri čemu su u pitanju složeni podaci, od kojih se svaki, naizgled, 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 ....

		
07. avgust 2021.
18:12:46
		
	
Slika 2. - Predstavljanje datuma i vremena na računaru - varijanta 2.

.... a ako bismo izabrali format koji bi operativni sistem tipično koristio da nije preveden na srpski jezik, prikaz bi ponovo bio drugačiji:

		
Sunday August 07 2021
06:12:46PM 
		
	
Slika 3. - Predstavljanje datuma i vremena na računaru - varijanta 3.

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 svakom slučaju (tj. u oba primera) - datum je "sedmi avgust".

Osmi mesec u godini je - osmi mesec u godini - bez obzira na to da li se u ispisu predstavlja kao "8", kao "avgust", ili kao "August", i bez obzira na to da li se podatak ispisuje pre ili posle dana u mesecu (a kada se prepozna koji podatak predstavlja mesec - dan i godina su obično "očigledni").

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 (naravno) mogle rešiti upotrebom međunarodnog standarda koji nalaže da se u zapisu datuma redom navode: godina, mesec i (na kraju) dan, kao i to da se sati beleže isključivo u formatu od 0 do 23 ....

		
2021-08-07
18:12:46
		
	
Slika 4. - Predstavljanje datuma i vremena na računaru - varijanta 4 (međunarodni format).

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

Sa formatom koji ste videli u prethodnom primeru: "YYYY-MM-DD HH-MI-SS", često ćete se sretati u DBMS sistemima, odnosno, u SQL upitima (u kojima se pojavljuju datumi i vreme).

Ipak, ono što smo naveli do sada, nije jedini (pa čak ni osnovni) problem koji se tiče 'tehnikalija'.

Kako god da treba formatirati podatke u ispisu, klasa za beleženje datuma i vremena koja bi bila definisana u sledećem obliku ....

C#
C++
Java
Python
JavaScript
		
public class DatumVreme
{
	public Int32 Dan, Mesec, Godina,
	             Sat, Minut, Sekund;

	public DatumVreme(Int32 Godina, Int32 Mesec, Int32 Dan,
	                  Int32 Sat,    Int32 Minut, Int32 Sekund)
	{
		this.Godina = Godina;
		this.Mesec  = Mesec;
		this.Dan    = Dan;
		this.Sat    = Sat;
		this.Minut  = Minut;
		this.Sekund = Sekund;
	}

	public String FormatiranjeDatum()
	{
        return Dan.ToString()    + "-" +
               Mesec.ToString()  + "-" +
               Godina.ToString() + "\r\n";
	}

	public String FormatiranjeVreme()
	{
        return Sat.ToString("00")    + ":" +
               Minut.ToString("00")  + "-" +
               Sekund.ToString("00") + "\r\n";
	}
}
		
	
		
class DatumVreme
{
	public:

	int Dan, Mesec, Godina,
	    Sat, Minut, Sekund;

	DatumVreme(int Godina, int Mesec, int Dan,
	           int Sat,    int Minut, int Sekund)
	{
		this.Godina = Godina;
		this.Mesec  = Mesec;
		this.Dan    = Dan;
		this.Sat    = Sat;
		this.Minut  = Minut;
		this.Sekund = Sekund;
	}

	string intUString(int n, int p)
	{
		string s = "", t = "";
		int    b = 0;
			
		while (n > 0) {
			int c = n % 10 + 48;
			n /= 10;
			s = (char)c + s;
			b++; 
		}
		
		while (b < p) {
			t += "0";
			p--;
		}
		
		return t + s;
	}

	string FormatiranjeDatum()
	{
		return intUString(Dan, 2)   + "." +
               intUString(Mesec, 2) + "," +
               intUString(Godina, 0)   + "\r\n";
	}

	string FormatiranjeVreme()
	{
		return intUString(Sat, 2)    + "." +
               intUString(Minut, 2)  + "," +
               intUString(Sekund, 2) + "\r\n";
	}
}
		
	
		
public class DatumVreme {
	public int Dan, Mesec, Godina,
	           Sat, Minut, Sekund;

	public DatumVreme(int Godina, int Mesec, int Dan,
	                  int Sat,    int Minut, int Sekund)
	{
		this.Godina = Godina;
		this.Mesec  = Mesec;
		this.Dan    = Dan;
		this.Sat    = Sat;
		this.Minut  = Minut;
		this.Sekund = Sekund;
	}

	String FormatiranjeDatum() {
		return String
		           .format("%2d-%2d-%d", Dan, Mesec, Godina)
		           .replace(" ", "0");
	}

	String FormatiranjeVreme() {
		return String
		           .format("%2d:%2d:%2d", Sat, Minut, Sekund)
		           .replace(" ", "0");
	}
}
		
	
		
# U primerima u ovom članku, objekte u Python-u
# razmenjivaćemo bez posredničkih klasa
		
	
		
// U primerima u ovom članku, objekte u JavaScript-u
// razmenjivaćemo bez posredničkih klasa
		
	
Slika 5. - Klasa za beleženje datuma i vremena.

.... 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 obavljaju više stotina miliona puta - u toku svakog dana (procena učestalosti je 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 koristi se "broj sekundi", ali, beleže se i ostali podaci. *

* Sa jedne strane, "broj sekundi" je (sam po sebi) sveobuhvatan podatak, međutim, često je potrebno da podaci o vremenu budu prikazani na ekranu ili odštampani (i sl), i stoga je potrebno da klase, preko kojih se beleže podaci o vremenu, obuhvate i metode za kreiranje različitih ispisa i, takođe (vrlo često), polja preko kojih se podaci beleže u "ljudskom"/"kalendarskom" formatu.

U svemu do sada navedenom, nismo se (još uvek) dotakli teme vremenskih zona - što pomalo komplikuje situaciju, ali (kao što ćemo pokazati u narednom poglavlju članka), "ne previše".

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 znatno veći (recimo - pet miliona), postavlja se pitanje šta takav podatak zapravo predstavlja?

Takvo pitanje postavlja se uvek (naravno), samo što se u situacijama kada se broj sekundi "uklapa u jedan dan", do odgovora dolazi mnogo lakše.

Prethodno 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, tiče se "neprirodnosti" 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 u bazama podataka: ako su datumi u bazi 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. **

* više o tome šta u datom kontekstu predstavlja termin "epoha" - u sledećem poglavlju članka.

** više o tome kako se implementiraju pomenute funkcije - u poslednjem poglavlju.

U prethodnom primeru, ako bi se podatak 5 x 106 pojavio samostalno (pri čemu bi takav podatak "nekako" trebalo da 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 5 x 106 poklapa sa 27. februarom (što takođe predstavlja 58. dan u godini), međutim - i dalje nije poznato:

  • kojoj godini pripada navedeni datum
  • šta bi (inače) bilo sa (npr) 78. danom u godini - da li bi datum bio 18.03. ili 19.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, pri čemu u računarskoj tehnici postoji izvestan broj karakterističnih datuma koji se koriste u različitim sistemima:

  • 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 timestamp.

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 timestamp koji smo koristili (5000000), odgovara datumu 27.02.1970. i vremenu 20:53:20.

Za početak smo razmotrili timestamp koji se intuitivno može pretvoriti u poznati format koji koristimo svakodnevno, ali, da bismo se što bolje upoznali sa postupkom, u nastavku ćemo koristiti timestamp-ove 50 x 106 i 150 x 106 ("malo više nula i jedinica" i, što je važnije (zarad boljeg razumevanja tematike), u pitanju su timestamp-ovi koji se ne uklapaju "u istu godinu sa timestamp-om 0").

Osnovne postavke algoritma objasnićemo preko timestamp-a 50 x 106 (direktno u nastavku), timestamp 150 x 106 će poslužiti kao primer pri određivanju godine, nakon čega ćemo se vratiti na timestamp (50 x 106), koji će poslužiti i kao primer pri određivanju datuma ....

U svakom slučaju, prvo treba podeliti timestamp sa 86400 * - i potom treba dodati 1. **

Umesto dodavanja jedinice, u implementaciji se prosto može napisati:

C#
C++
Java
Python
JavaScript
		
Int32 BrojDana = Math.Ceiling(timestamp / 86400);
		
	
		
#include<cmath>
int brojDana = ceil(timestamp / 86400);
		
	
		
int brojDana = Math.ceil(timestamp / 86400);
		
	
		
import math
brojDana = math.ceil(timestamp / 86400)
		
	
		
let brojDana = Math.ceil(timestamp / 86400);
		
	
Slika 6. - Računanje broja proteklih dana (u odnosu na početak epohe), shodno timestamp-u.

Preko gornje formule, pravilno smo odredili koliko je dana proteklo od 01.01.1970 u ponoć. **

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.)

* Deljenje je celobrojno i bez ostatka, a vrednost 86400 (da se podsetimo), predstavlja broj sekundi u jednom danu.

** Deljenjem sa 86400 dobija se broj celih dana koji su protekli od 01.01.1970. u ponoć, ali, ako je timestamp manji od 86400, datum nije "nulti januar" već prvi januar (i stoga ćemo navedenu pojavu zvati: broj 'započetih' dana).

*** U nastavku ćemo postaviti link ka (spoljnjem) članku u kome je detaljno opisan "najoptimalniji" algoritam za određivanje datuma i vremena.

Kada se 50 miliona podeli sa 86400, dobija se 579 (u smislu: 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.

Sa jedne strane, godinu smo odredili prilično proizvoljno/"odokativno", ali (sa druge strane), pretpostavljamo da čitaocima ne bi bilo teško da samostalno osmisle i univerzalni algoritam za "oduzimanje godina", međutim (kao što smo nagovestili), svakako ćemo o navedenom algoritmu detaljnije prodiskutovati.

Pre toga, kratak osvrt na vremenske zone (koji smo još ranije najavili) ....

Standard UTC (Coordinated Universal Time), praktično predstavlja vreme "po Griniču" (pri čemu se ne uzima u obzir letnje i zimsko računanje vremena).

Za prikaz datuma i vremena koji su vezani za određenu geografsku lokaciju, koja pripada nekoj drugoj vremenskoj zoni (pri čemu treba uvažiti letnje i zimsko računanje vremena), potrebno je dodati, ili oduzeti, odgovarajući broj sati (odnosno, dodati ili oduzeti odgovarajući umnožak broja 3600).

Takođe, zarad preglednosti, u nastavku ćemo se zadržati samo na određivanju datuma (što obuhvata i određivanje godine), i verujemo da čitaoci neće imati ikakvih problema da putem prethodno navedene logike samostalno osmisle algoritam za određivanje sati, minuta i sekundi (koji je znatno jednostavniji).

Određivanje godine

U svojstvu primera (pri određivanju godine), privremeno ćemo koristiti timestamp 150 x 106, kome odgovara godina koja se (očigledno) ne poklapa, ni sa 1970, ni sa 1971.

Kada se timestamp podeli sa 86400 (što u novom primeru daje rezultat 1737 (tj. 1736 dana + 1 dan)), potrebno je redom oduzimati 365 ili 366 (dana), za svaku godinu, sve dok broj dana ne postane manji ili jednak ("mogućem") broju dana u godini:

UNIX timestamp godina
Slika 7. - Određivanje datuma koji odgovara zabeleženom timestamp-u - korak 1. - određivanje godine.
  • 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.

* 1972. je bila prestupna godina, i stoga se ne oduzima 365, već 366.

Zarad prikaza procedure za određivanje datuma (koja sledi), vraćamo se na glavni primer, timestamp 50 x 106.

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

U implementaciji (pred kraj članka), nećemo koristiti petlju koja "prolazi kroz mesece" na prethodno opisani način, već, pomoćnu tabelu (isto važi i za određivanje godina, ali, pomoćne tabele biće (naravno) dizajnirane u skladu sa prethodno iznetim principima).

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 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 znači da traženi datum nije ni u martu ....
UNIX timestamp mesec i dan
Slika 8. - Određivanje datuma koji odgovara zabeleženom timestamp-u - korak 2. - određivanje meseca i dana.

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 *

* U pitanju je razlika brojeva 214 i 212, pri čemu broj 212 predstavlja zbir dana u prvih 7 meseci navedene godine (31 + 28 + 31 + 30 + 31 + 30 + 31 = 212).

Kao što smo već pomenuli, postupak se može uprostiti preko pomoćne tabele koja odgovara mesecima u godini (u ovom slučaju, 1970) ....

		
/* ----- 1970 ----- */
// 0   1   2   3    4    5    6    7    8    9   10   11   12
// 0  31  28  31   30   31   30   31   31   30   31   30   31
[  0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365  ]
		
	
Slika 9. - Pomoćna tabela koja prikazuje (u trećem redu), broj proteklih dana po isteku određenog meseca.
  • prvi red (komentar), sadrži redni broj meseca, tj. indekse niza
  • drugi red (takođe komentar), sadrži broj dana u svakom mesecu
  • treći red sadrži broj celih dana između početka godine i kraja određenog meseca

Pažljivom pretragom tabele, lako se pronalaze neophodne informacije:

  • mesec se nalazi na prvom indeksu koji odgovara prvoj vrednosti iz trećeg reda, koja je veća od broja dana (u primeru: 243 je prva vrednost veća od 214, 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:

		
//  0 (1970)   1 (1971)   2 (1972)   3 (1973)   4 (1974) ....
//     365        365        366        365        365   ....
[      365,       730,       1096,      1461,      1826  ....  ]
		
	
Slika 10. - Pomoćna tabela koja prikazuje broj proteklih dana po isteku određene godine (u odnosu na početak UNIX epohe).

Naravno, da bi postupak imao pravi smisao, pretraga mora biti efikasna (tj. nikako ne treba koristiti linearnu pretragu i sl).

Sve ćemo detaljnije razmotriti u nastavku (pred kraj članka), kad budemo prikazali implementaciju algoritma.

Pogledajmo usput i nekoliko zanimljivih kombinacija timestamp-a i datuma:

		
31536000    -  01.01.1971. 00:00:00
946684800   -  01.01.2000. 00:00:00
1000000000  -  09.11.2001. 03:46:40
1234567890  -  14.02.2009. 00:31:30
		
	
Slika 11. - Pregled zanimljivijih kombinacija timestamp-a i odgovarajućih 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), može delovati pomalo neintuitivno pri prvom susretu, primetićemo da je postupak i dalje veoma sličan u idejnom smislu, ali, obavlja se "u obrnutom smeru".

Postupak ćemo objasniti na primeru timestampa -35 x 106 (-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 usput 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" (potencijalna katastrofa koja je mogla nastati na samom početku 2000. godine zbog neadekvatnog formata podataka u stari(ji)m programima), predstavlja jedan od zanimljivijih fenomena u računarskoj istoriji (koga se stariji čitaoci, verujemo, sećaju - iako je sve donekle zaboravljeno u međuvremenu), 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 kako bi što bolje iskoristili skromne memorijske kapacitete, i - u kontekstu "praktično neizbežnih mera", * "stradao" je (između ostalog) 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).

* Upravo iz prvih godina računarske industrije potiču mnogi sjajni algoritmi koji se koriste i dan-danas, međutim, neke od mera (jednostavno rečeno) - samo su bile "iznuđene" i, na početku, doslovno neizbežne.

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 ili 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 nekome 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 (umesto sve č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 svakako su ('još onda') primetili da postoji potencijalni problem koji bi se mogao manifestovati dolaskom 2000. godine, ali, na raspolaganju nije bilo dovoljno memorije (ni iz daleka), očekivalo se da prvobitni programi neće dugo ostati u opticaju, ili - ukoliko programi 'eventualno' 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".

Da se izrazimo malo jednostavnije: usled kombinacije iskrenog, ali ne-baš-preterano-utemeljenog optimizma, i (naravno) lenjosti - koja je često "pritajeni faktor" u mnogim velikim poduhvatima - računalo se da za sve "ima vremena".

Međutim, mnogi od prvobitnih programa iz početnog perioda ostali su (tj. 'opstali 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.

Da dodatno postavimo stvari 'u perspektivu' i 'branimo' starije generacije programera (pre nego što se vratimo na ostatak priče): u današnje vreme, bilo koji programer koji piše program na "bilo kakvom" (bar koliko-toliko savremenom) računaru, i u toku pisanja programa uvidi da je za rešavanje određenog zadatka korisno implementirati određenu strukturu podataka koja povećava memorijsko zauzeće za nekoliko Megabajta, može svoju ideju sprovesti u delo, može isprobati program na svom računaru i, takođe (krajnje opravdano/legitimno), može očekivati da će program biti u stanju da se izvrši na računaru krajnjeg korisnika, bez ikakvog zastoja.

Na početku (u prvim godinama računarske industrije), tako nešto nije bilo izvodljivo (u najmanju ruku :))!

Šezdesetih godina 20. veka (a bogme i početkom sedamdesetih), tipičan kapacitet radne memorije velikih i skupih računara kavi su bili dostupni samo većim organizacijama (pri čemu se cena takvih računara izražavala desetinama i stotinama hiljada tadašnjih dolara), bio je između 32 i 64 Kilobajta i, u navedenim uslovima - i te kako je bilo potrebno štedeti i snalaziti se.

Možemo reći da postoji i izvesna dilema vezana za to da li bi "praočevi računarske industrije" iznedrili mnoge korisne i efikasne algoritme da ih na to "nije nevolja naterala" (pri čemu želimo da verujemo - da ipak bi).

Osvrnućemo se dodatno i na tekstualni format koji se nekada koristio za prikaz godina (iako pretpostavljamo da je 'očigledno') ....

Ako su godine zapisane na "skraćeni" način koji smo opisali na početku odeljka, nije teško podesiti da se u prikazu pojave i prve dve cifre ("19"). Na primer, ako je osoba rođena 14.04.1968, a datum se beleži preko sledećih podataka: dan = 14; mesec = 04; godina = 68;, ispis se lako može rešiti na sledeći način:

			
printf("%d.%d.19%d." dan, mesec, godina);
			
		
Slika 12. - Naredba izlaza, definisana po ugledu na programske kodove kakvi su u ranija vremena bili korišćeni za ispis godina koje su zapisane preko (samo) dve cifre.

.... i sve je naizgled "tip-top", ali - navedeni pristup daje korektan rezultat samo za određeni raspon godina, i ni u kom slučaju nije univerzalno rešenje.

Š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 1. 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 (budući da osobe rođene 1900. takođe 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 'glasova' koji su (u nešto većoj meri) skretali pažnju na to da bi ipak trebalo "preduzeti nešto po pitanju 2000", * 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.

* 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.

** Da se usput podsetimo: naziv "milenijumska buba" pomalo je nepravilan, budući da datum 01.01.2000. ne predstavlja početak III milenijuma (koji je zapravo počeo 01.01.2001), i stoga prethodno nismo napisali "poslednjih godina 20. veka".

U 'poslednjim godinama', 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, problemi se nisu rešili "sami od sebe".

"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 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).

Ako se pitate "zašto to sve nije već urađeno, bar sada, u 21. veku" i "zar se ne podrazumeva da su svi sistemi do sada postali 64-bitni", recimo da je odgovor na drugo pitanje, ujedno i odgovor na oba pitanja.

Kao što je, devedesetih godina (20. veka), i dalje u upotrebi bilo mnoštvo računarskih sistema iz ranijih decenija, danas je (verovatno već naslućujete odgovor koji sledi), još uvek u upotrebi mnoštvo 32-bitnih hardverskih i softverskih sistema, i pri tom se ne podrazumeva (ipak), da su do početka treće decenije 21. veka svi računarski sistemi 'postali 64-bitni'.

Problemi koji nastaju usled korišćenja zastarelih tehnologija, često su krajnje nezanemarljivi, ali, istovremeno (u praktičnom smislu), mora se uvažiti i to da je zamena većeg broja računara (uz prateći softver), veoma skup i veoma zahtevan poduhvat.

Međutim, potrebno je bar unaprediti kritične sisteme, a troškovi se uvek moraju uporediti sa štetom koja može nastati ukoliko se sistemi ne unaprede i ne ažuriraju (hoćemo reći: šteta koja može nastati - vrlo često je daleko veća).

Prelaskom na 64-bitne brojače, onaj deo čovečanstva koji (u smislu 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 će omogućiti inkrementaciju timestamp-ova u sledeće ~292 milijarde godina!

Zapravo, postoji i "sitan" kusur - cca. 277 miliona godina, tu je i "kusur na kusur" u iznosu od ~26 hiljada godina, a postoji i "ostatak kusura" - nekoliko stotina godina (pustićemo vas da sami dođete do tačnih vrednosti, ručno ili preko programa koje možete samostalno implementirati). :)

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 x 109 godina)

Ako do sada niste razumeli razliku u smeštajnom kapacitetu 16-bitnih, 32-bitnih i 64-bitnih promenljivih, verujemo da je sada jasnije. 🙂

Algoritmi

Kao što smo ranije nagovestili, u nastavku ćemo se pozabaviti nekolicinom popularnih algoritama koji se tiču obrade datuma i vremena na računarima (pri čemu ćemo najviše pažnje posvetiti, na kraju poglavlja, implementaciji algoritma za pretvaranje timestamp-a u strukturu podataka koja prikazuje datum i vreme).

Algoritam #1 - Kreiranje tajmera

Za početak ćemo razmotriti algoritam preko koga se lako može uvideti očigledna 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 na jednostavan način ....

Preko promenljive t1, očitava se timestamp koji odgovara početnom trenutku ....

C#
C++
Java
Python
JavaScript
		
DateTime t1 = DateTime.Now;
		
	
		
#include<ctime>
time_t t1, = time(NULL);
		
	
		
LocalDateTime t1 = LocalDateTime.now();
		
	
		
t1 = datetime.datetime.now()
		
	
		
let t1 = Date.now();

// Prvo očitavanje
// (JavaScript dodaje i milisekunde na timestamp)
		
	
Slika 13. - Kod koji se izvršava po pokretanju tajmera.

.... i potom, svaki put kada je potrebno prikazati proteklo vreme, očitava se novi timestamp:

C#
C++
Java
Python
JavaScript
		
DateTime t2 = DateTime.Now; // novo očitavanje
		
	
		
time_t t2  = time(NULL);
		
	
		
LocalDateTime t2 = LocalDateTime.now(); // novo očitavanje
		
	
		
t2 = datetime.datetime.now() # novo očitavanje
		
	
		
let t2 = Date.now(); // novo očitavanje
		
	
Slika 14. - Kod koji se izvršava pri svakom očitavanju tajmera.

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:

C#
C++
Java
Python
JavaScript
		
TimeSpan r = t2 - t1;

// Struktura se može direktno ispisati,
// ali, može se koristiti i sledeći kod:

Int32 r = t2.Second - t1.Second;

DatumVreme FormatiranjeTajmer(Int32 t)
{
	Int32 sat = t / 3600;
	Int32 min = t % 3600 / 60;
	Int32 sec = t % 60;

	return new DatumVreme(0, 0, 0, sat, minut, sekund);
}
		
	
		
double r = difftime(t2, t1);

DatumVreme FormatiranjeTajmer(int t)
{
	int sat = t / 3600;
	int min = t % 3600 / 60;
	int sec = t % 60;

	return DatumVreme(0, 0, 0, sat, minut, sekund);
}
		
	
		
int r = t2.getSecond() - t1.getSecond();

DatumVreme formatiranjeTajmer(int t) {
	int sat = t / 3600;
	int min = t % 3600 / 60;
	int sec = t % 60;

	return new DatumVreme(0, 0, 0, sat, minut, sekund);
}
		
	
		
r = t2 - t1;

# Rezultat se može ispisati direktno
		
	
		
let r = t2 - t1;

function formatiranjeTajmer(t) {
	t /= 1000;
	let sat = parseInt(t / 3600);
	let min = parseInt(t % 3600) / 60;
	let sec = t % 60;

	return {
		Sat:    sat,
		Minut:  min,
		Sekund: sec
	}
}
		
	
Slika 15. - Funkcija koja pretvara vremenski interval izražen u sekundama ('timestamp') - u strukturu "sat:minut:sekund".

Algoritam #2 - pretvaranje timestamp-a u format sat:minut:sekund

Iako smo se najviše bavili određivanjem datuma, prikazaćemo i kako se može izračunati vremenski trenutak u formatu sat:minut:sekund, ukoliko je timestamp manji od 86400:

C#
C++
Java
Python
JavaScript
		
sat    = timestamp / 3600;
minut  = (timestamp % 3600) / 60;
sekund = timestamp % 60;
		
	
		
sat    = timestamp / 3600;
minut  = (timestamp % 3600) / 60;
sekund = timestamp % 60;
		
	
		
sat    = timestamp / 3600;
minut  = (timestamp % 3600) / 60;
sekund = timestamp % 60;
		
	
		
sat    = timestamp / 3600
minut  = (timestamp % 3600) / 60
sekund = timestamp % 60
		
	
		
sat    = timestamp / 3600;
minut  = (timestamp % 3600) / 60;
sekund = timestamp % 60;
		
	
Slika 16. - Pretvaranje timestamp-a u strukturu sat:minut:sekund, pod uslovom da je timestamp manji od 864000.

Algoritam #3 - 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".

Želimo da pokažemo da 'zdrav razum' uvek treba da ima prednost pri izboru algoritama, struktura podataka i sl. :)

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 da se prvo dva timestamp-a pretvore u strukturu datuma koja se koristi svakodnevno ....

		
/* ----- trenutni datum ----- */
07.08.2021.

/* ----- datum rođenja ------ */
18.08.1979.
		
	
Slika 17. - Format podataka koji ćemo, pri računanju starosti osobe, koristiti radije nego timestamp.

.... 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 umanjuje se 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 - i pri tom je dan manji od dana rođenja

U primeru koji smo prikazali, do umanjivanja dolazi u drugom koraku i krajnji rezultat je 41.

Pošto smo razmotrili sve bitne detalje algoritma, možemo implementirati funkciju:

C#
C++
Java
Python
JavaScript
		
Int32 racunanjeStarosti(DatumVreme d1, DatumVreme d2) {
	Int32 s = d2.Godina - d1.Godina;

	if (d2.Mesec > d1.Mesec) return s;

	if (d2.Mesec < d1.Mesec) s--;
	if (d1.Dan   < d2.Dan)   s--;

	return s;
}
		
	
		
int racunanjeStarosti(DatumVreme d1, DatumVreme d2)
{
	int s = d2.Godina - d1.Godina;

	if (d2.Mesec > d1.Mesec) return s;

	if (d2.Mesec < d1.Mesec) s--;
	if (d1.Dan   < d2.Dan)   s--;

	return s;
}
		
	
		
int racunanjeStarosti(DatumVreme d1, DatumVreme d2) {
	int s = d2.Godina - d2.godina;

	if (d2.Mesec > d1.Mesec) return s;

	if (d2.Mesec < d1.Mesec) s--;
	if (d1.Dan   < d2.Dan)   s--;

	return s;
}
		
	
		
def racunanjeStarosti(d1, d2):
	s = d2.godina - d1.godina

	if d2.mesec > d1.mesec return s

	if d2.mesec < d1.mesec: s = s - 1
	if d1.dan   < d2.dan:   s = s - 1

	return s
		
	
		
function racunanjeStarosti(d1, d2) {
	let s = d2.godina - d1.godina;

	if (d2.mesec > d1.mesec) return s;

	if (d2.mesec < d1.mesec) s--;
	if (d1.dan   < d2.dan)   s--;

	return s;
}
		
	
Slika 18. - Implementacija algoritma za računanje starosti osobe.

Na kraju, pogledajmo i algoritam za pretvaranje timestamp-a u datum (koji je u celokupnoj diskusiji najbitniji).

Algoritam #4 - 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 obavlja se tako što 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 sledi određivanje meseca
  • za određivanje meseca, primenjuje se idejno sličan postupak koji podrazumeva da se, u svakom koraku, 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.)

* Za neprestupne godine - 365 dana; za prestupne godine - 366 dana.

** Pošto se do sada nismo 'zvanično' upoznali sa hash tabelama, koristićemo binarnu pretragu.

*** Ako vas odmah zanima ponešto efikasniji algoritam (najefikasniji postupak koji je nama poznat), i pri tom vam čitanje IT članaka na engleskom jeziku nije strano, možete posetiti sledeću adresu:

chrono-compatible low-level date algorithms

.... a mi ćemo u budućnosti posvetiti jedan članak ovom algoritmu.

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):

C#
C++
Java
Python
JavaScript
    	
static Int32 sekundeUMinutu  = 60;
static Int32 sekundeUSatu    = sekundeUMinutu * 60; // 3600
static Int32 sekundeUDanu    = sekundeUSatu * 24;   // 86400
static Int32 sekundeUGodiniN = sekundeUDanu * 365;  // 31536000
static Int32 sekundeUGodiniP = sekundeUDanu * 366;  // 31622400

static Int32[] daniUGodini = {
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1970 - 1979
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 1980 - 1989
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1990 - 1999
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2000 - 2010
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 2010 - 2019
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2020 - 2029
};

static Int32[] meseciN = {
    0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

static Int32[] meseciP = {
    0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
		
	
    	
int sekundeUMinutu  = 60;
int sekundeUSatu    = sekundeUMinutu * 60;   // 3600
int sekundeUDanu    = sekundeUSatu   * 24;   // 86400
int sekundeUGodiniN = sekundeUDanu   * 365;  // 31536000
int sekundeUGodiniP = sekundeUDanu   * 366;  // 31622400

int daniUGodini[] = {
	365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1970 - 1979
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 1980 - 1989
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1990 - 1999
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2000 - 2010
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 2010 - 2019
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2020 - 2029
};

int meseciN[] = {
	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

int meseciP[] = {
	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
		
	
    	
static int sekundeUMinutu  = 60;
static int sekundeUSatu    = sekundeUMinutu * 60; // 3600
static int sekundeUDanu    = sekundeUSatu * 24;   // 86400
static int sekundeUGodiniN = sekundeUDanu * 365;  // 31536000
static int sekundeUGodiniP = sekundeUDanu * 366;  // 31622400

static int[] daniUGodini = {
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1970 - 1979
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 1980 - 1989
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1990 - 1999
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2000 - 2010
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 2010 - 2019
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2020 - 2029
};

static int[] meseciN = {
    0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

static int[] meseciP = {
    0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

		
	
    	
import math

sekundeUMinutu  = 60
sekundeUSatu    = sekundeUMinutu * 60;   # 360
sekundeUDanu    = sekundeUSatu   * 24;   # 8640
sekundeUGodiniN = sekundeUDanu   * 365;  # 3153600
sekundeUGodiniP = sekundeUDanu   * 366;  # 3162240

daniUGodini = [
	365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  # 1970 - 1979
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  # 1980 - 1989
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  # 1990 - 1999
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  # 2000 - 2010
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  # 2010 - 2019
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  # 2020 - 2029
]

meseciN = [
	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
]

meseciP = [
	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
]
		
	
    	

let sekundeUMinutu  = 60;
let sekundeUSatu    = sekundeUMinutu * 60;   // 3600
let sekundeUDanu    = sekundeUSatu   * 24;   // 86400
let sekundeUGodiniN = sekundeUDanu   * 365;  // 31536000
let sekundeUGodiniP = sekundeUDanu   * 366;  // 31622400

let daniUGodini = [
	365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1970 - 1979
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 1980 - 1989
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 1990 - 1999
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2000 - 2010
    365, 365, 366, 365, 365, 365, 366, 365, 365, 365,  // 2010 - 2019
    366, 365, 365, 365, 366, 365, 365, 365, 366, 365,  // 2020 - 2029
];

let meseciN = [
	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
]

let meseciP = [
	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
]
		
	
Slika 19. - Strukture podataka koje se koriste pri pretvaranju timestamp-a u datum.

Vidimo da smo svakoj godini, odnosno, svakom mesecu, za početak dodelili pripadajući broj dana ....

		
//            1970   1971   1972   1973 ....
daniUGodini [  365,   365,   366,   365 ....]

//        0   1   2   3   4   5   6   7   8   9  10  11  12
meseciN [ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
meseciP [ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
		
	
Slika 20. - Sadržaj pomoćnih tabela koje se koriste pri pretvaranju timestamp-a u datum.

.... 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 i meseciP, u pitanju je broj celih dana između početka godine i kraja određenog meseca
		
//            1970   1971   1972   1973 ....		
daniUGodini [  365,   730,  1096,  1461 ....]
    
//        0   1   2   3    4    5    6    7    8    9   10   11   12
meseciN [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 ]
meseciP [ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 ]
		
	
Slika 21. - Mapiranje pomoćnih tabela.

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).

Dakle, u oba slučaja, vrednost koja nas zanima nalazi se na 'prethodnom' indeksu.

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.

C#
C++
Java
Python
JavaScript
    	
static void inicijalizacijaNiza(Int32[] niz) {
    for (int i = 1; i < niz.Length; i++) {
        niz[i] = niz[i] + niz[i - 1];
    }
}

static Int32 BinarnaPretraga(Int32 a, Int32[] niz) {
    Int32 le  = 0, de = niz.Length - 1;
    Int32 ind = (Int32)Math.Floor((Double)(le + de) * 0.5);

    while (le < de) {
        if (a == niz[ind]) return ind;
	
        if (a < niz[ind]) {
	        de = ind - 1;
        }
        else {
	        le = ind + 1;
        }

        ind = (Int32)Math.Floor((Double)(le + de) * 0.5);
    }

    return ind;
}
		
	
    	
void inicijalizacijaNiza(int niz[], int n) {
	for (int i = 1; i < n; i++) {
		niz[i] = niz[i] + niz[i - 1];
	}
}

void ispisNiza(int niz[], int n) {
	for (int i = 0; i < n; i++) {
		cout << niz[i] << " ";
	}
	
	cout << endl;
}

int binarnaPretraga(int a, int niz[], int n) {
	int le  = 0, de = n - 1;
	int ind = floor((le + de) * 0.5);
		
	while (le < de) {
		if (a == niz[ind]) return ind;
			
		if (a < niz[ind]) {
			de = ind - 1;
		}
		else {
			le = ind + 1;
		}

		ind = floor((le + de) * 0.5) ;
	}

	return ind;
}
		
	
    	
static void inicijalizacijaNiza(int[] niz) {
    for (int i = 1; i < niz.length; i++) {
        niz[i] = niz[i] + niz[i - 1];
    }
}

static int BinarnaPretraga(int a, int[] niz) {
    int le  = 0, de = niz.length - 1;
    int ind = (int)Math.floor((double)(le + de) * 0.5);

    while (le < de) {
        if (a == niz[ind]) return ind;
	
        if (a < niz[ind]) {
	        de = ind - 1;
        }
        else {
	        le = ind + 1;
        }

        ind = (int)Math.floor((double)(le + de) * 0.5);
    }

    return ind;
}
		
	
    	
def inicijalizacijaNiza(niz):
	r = range(1, len(niz))
	for i in r:
		niz[i] = niz[i] + niz[i - 1]

inicijalizacijaNiza(daniUGodini)
inicijalizacijaNiza(meseciN)
inicijalizacijaNiza(meseciP)


def binarnaPretraga(n, niz):
	le  = 0
	de  = len(niz) - 1;
	ind = math.floor((le + de) * 0.5)
		
	while (le < de):
		if n == niz[ind]: return ind
			
		if (n < niz[ind]):
			de = ind - 1
		else:
			le = ind + 1
		
		ind = math.floor((le + de) * 0.5)

	return ind;
		
	
    	
function inicijalizacijaNiza(niz) {
	for (let i = 1; i < niz.length; i++) {
		niz[i] = niz[i] + niz[i - 1];
	}
}

function binarnaPretraga(n, niz) {
	let le = 0, de = niz.length - 1;
	let ind = Math.floor((le + de) * 0.5);
		
	while (le < de) {
		if (n == niz[ind]) return ind;
			
		if (n < niz[ind]) {
			de = ind - 1;
		}
		else {
			le = ind + 1;
		}

		ind = Math.floor((le + de) * 0.5) ;
	}

	return ind;
}

		
	
Slika 22. - Pomoćne funkcije koje se koriste u glavnoj funkciji za pretvaranju timestampa u datum.

Naravno, za razliku od nizova meseciN i meseciP (u kojima postoji idiosinkratičan i nepravilan sled elemenata), niz daniUGodini može se kreirati automatski:

C#
C++
Java
Python
JavaScript
    	
Int32 poslednjaGodina = 2040;
Int32 epoha           = 1970;
Int32 brojElemenata   = poslednjaGodina - epoha;
Int32[] daniUGodini   = new Int32[brojElemenata];

/* ----- ver #1 ----- */

for (Int32 i = 0; i < brojElemenata; i++) {
	niz[i] = (i % 4 == 2)? 366 : 365;
}

/* ----- ver #2 ----- */

Int32 i;

for (i = 0; i < brojElemenata; i++) {
	niz[i] = 365;
}

i = 2;

while (i < brojElemenata) {
	niz[i] = 366;
	i     += 4;
}
		
	
    	
int poslednjaGodina = 2040;
int epoha           = 1970;
int brojElemenata   = poslednjaGodina - epoha;
int daniUGodini[brojElemenata];

/* ----- ver #1 ----- */

for (int i = 0; i < brojElemenata; i++) {
	niz[i] = (i % 4 == 2)? 366 : 365;
}

/* ----- ver #2 ----- */

int i;

for (i = 0; i < brojElemenata; i++) {
	niz[i] = 365;
}

i = 2;

while (i < brojElemenata) {
	niz[i] = 366;
	i     += 4;
}
		
	
    	
int poslednjaGodina = 2040;
int epoha           = 1970;
int brojElemenata   = poslednjaGodina - epoha;
int daniUGodini[]   = new int[brojElemenata];

/* ----- ver #1 ----- */

for (int i = 0; i < brojElemenata; i++) {
	niz[i] = (i % 4 == 2)? 366 : 365;
}

/* ----- ver #2 ----- */

int i;

for (i = 0; i < brojElemenata; i++) {
	niz[i] = 365;
}

i = 2;

while (i < brojElemenata) {
	niz[i] = 366;
	i     += 4;
}
		
	
    	
poslednja_godina = 2040
epoha            = 1970
broj_elemenata   = poslednja_godina - epoha
daniUGodini      = [ ]

# ----- ver #1 ----- #

for i in range(0, broj_elemenata):
	if i % 4 == 2:
		niz.append(366)
	else:
		niz.append(365)
}

# ----- ver #2 ----- #

for i in range(0, broj_elemenata):
	niz.append(365)

i = 2

while i < broj_elemenata:
	niz[i] = 366
	i = i + 4
		
	
    	
let poslednjaGodina = 2040;
let epoha           = 1970;
let brojElemenata   = poslednjaGodina - epoha;
let daniUGodini     = [];

/* ----- ver #1 ----- */

for (let i = 0; i < brojElemenata; i++) {
	niz.push((i % 4 == 2)? 366 : 365);
}

/* ----- ver #2 ----- */

let i;

for (i = 0; i < brojElemenata; i++) {
	niz.push(365);
}

i = 2;

while (i < brojElemenata) {
	niz[i] = 366;
	i     += 4;
}
		
	
Slika 23. - Funkcije za automatsko popunjavanje pomoćnih nizova koji sadrže broj dana u godinama.

Svaki četvrti element dobija vrednost 366.

Dodaćemo još i pomoćnu funkciju koja proverava da li je godina prestupna ....

C#
C++
Java
Python
JavaScript
    	
static Boolean DaLiJePrestupna(Int32 godina) {
    if (godina % 4 == 0) {
        if (godina % 100 == 0) {
            if (godina % 400 == 0) return true;
            return false;
        }
        else {
            return true;
        }
    }

    return false;
}
		
	
    	
bool daLiJePrestupna(int godina) {
	if (godina % 4   == 0) {
		if (godina % 100 == 0) {
			if (godina % 400 == 0) return true;
			return false;
		}
		else {
			return true;
		}
	}
	
	return false;
}
		
	
    	
static Boolean DaLiJePrestupna(int godina) {
	if (godina % 4   == 0) {
		if (godina % 100 == 0) {
			if (godina % 400 == 0) return true;
			return false;
		}
		else {
			return true;
		}
	}
	
	return false;
}
		
	
    	
def daLiJePrestupna(godina):
	if godina % 4 == 0:
		if godina % 100 == 0:
			if godina % 400 == 0: return True
			return False
		else:
			return True
	
	return False
		
	
    	
function daLiJePrestupna(godina) {
	if (godina % 4   == 0) {
		if (godina % 100 == 0) {
			if (godina % 400 == 0) return true;
			return false;
		}
		else {
			return true;
		}
	}
	
	return false;
}
		
	
Slika 24. - Pomoćna funkcija koja proverava da li je tražena godina prestupna.

.... kao i dve pomoćne klase (u C#-u i Javi), odnosno, pomoćnu strukturu u C++ - u * ....

C#
C++
Java
Python
JavaScript
    	
public class Datum {
    public int Godina, Mesec, Dan;

    public String Ispis()
    {
        return Godina.ToString() + "-" +
               Mesec.ToString() + "-" +
               Dan.ToString() + "\r\n";
    }
}

public class Rezultat {
    public int Rez, Korekcija;
}
		
	
    	
struct Datum {
	int godina, mesec, dan;
};

struct Rezultat {
	int rez, korekcija;
};
		
	
    	
public class Datum {
	public int Godina, Mesec, Dan;
	
	public String Ispis() {
        return String.format("%d-%d-%d", Godina, Mesec, Dan);
    }
}

public class Rezultat {
	public int Rez, Korekcija;
}
		
	
    	
# U primerima u ovom članku, objekte u Python-u
# razmenjivaćemo bez posredničkih klasa
		
	
    	
// U primerima u ovom članku, objekte u JavaScript-u
// razmenjivaćemo bez posredničkih klasa
		
	
Slika 25. - Pomoćne klase Datum i Rezultat.

.... i onda možemo početi.

* Usputno podsećanje: u Javi, svaka javna klasa mora biti zapisana u zasebnoj datoteci. U Python-u i JavaScript-u, funkcije mogu vraćati objekte neposredno, i stoga nećemo kreirati pomoćne klase.

Za početak, timestamp se deli sa 86400 (odnosno, sa brojem sekundi u jednom danu) ....

		
daniUGodini = floor(timestamp / brojSekundiUDanu)
		
	
Slika 26. - Svođenje timestamp-a na broj proteklih dana (u odnosu na početak epohe).

.... i potom se dobijena vrednost predaje funkciji PronalazenjeGodine, koja (preko tabele dani) ....

C#
C++
Java
Python
JavaScript
    	
static Rezultat PronalazenjeGodine(Int32 dan, Int32[] niz, Int32 epoha) {
    Rezultat rezultat = new Rezultat();

    if (dan <= 365) {
        rezultat.Rez       = epoha;
        rezultat.Korekcija = 0;
        return rezultat;
    }

    int ind = BinarnaPretraga(dan, niz);

    rezultat.Rez       = epoha + ind;
    rezultat.Korekcija = niz[ind - 1];

    return rezultat;
}
		
	
    	
struct Rezultat pronalazenjeGodine(int dan, int niz[], int epoha) {
	struct Rezultat rezultat;

	if (dan <= 365) {
		rezultat.rez       = epoha;
		rezultat.korekcija = 0;
		return rezultat;
	}

	int ind = binarnaPretraga(dan, niz, 100);

	rezultat.rez       = epoha + ind;
	rezultat.korekcija = niz[ind - 1];
	
	return rezultat;
}

		
	
    	
static Rezultat PronalazenjeGodine(int dan, int[] niz, int epoha) {
    Rezultat rezultat = new Rezultat();

    if (dan <= 365) {
        rezultat.Rez       = epoha;
        rezultat.Korekcija = 0;
        return rezultat;
    }

    int ind = BinarnaPretraga(dan, niz);

    rezultat.Rez       = epoha + ind;
    rezultat.Korekcija = niz[ind - 1];

    return rezultat;
}
		
	
    	
def pronalazenjeGodine(dan, niz, epoha):
	if dan <= 365:
		return {
			'godina':    epoha,
			'korekcija': 0
		}

	ind = binarnaPretraga(dan, niz)

	return {
		'godina':    epoha + ind,
		'korekcija': niz[ind - 1]
	}
		
	
    	
function pronalazenjeGodine(dan, niz, epoha) {
	if (dan <= 365) {
		return {
			godina:    epoha,
			korekcija: 0
		}
	}

	let ind = binarnaPretraga(dan, niz);

	return {
		godina:    epoha + ind,
		korekcija: niz[ind - 1]
	}
}
		
	
Slika 27. - Funkcija za pronalaženje godine, shodno unetom timestamp-u.

.... 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).

		
danUGodini -= rez.korekcija
		
	
Slika 28. - Korigovanje dana u godini.

Kao što vidimo, funkcija pronalazenjeGodine vraća jednostavan objekat sa dva polja, a jedno od polja (korekcija), može se iskoristiti za umanjivanje vrednosti promenljive daniUGodini.

Na sličan način, preko odgovarajuće funkcije ....

C#
C++
Java
Python
JavaScript
    	
static Rezultat pronalazenjeMeseca(Int32 dan, Boolean prestupna, Int32[] nizN, Int32[] nizP) {
	
    Int32[] niz;

    if (prestupna) {
        niz = nizP;
    }
    else {
        niz = nizN;
    }

    int ind = BinarnaPretraga(dan, niz);

    Rezultat rezultat = new Rezultat();
    rezultat.Rez       = ind;
    rezultat.Korekcija = niz[ind - 1];

    return rezultat;
} 
		
	
    	
struct Rezultat pronalazenjeMeseca(int dan, bool prestupna, int nizN[], int nizP[]) {
	
	int* niz;
	
	if (prestupna) {
		niz = nizP;
	}
	else {
		niz = nizN;
	}
	
	int ind = binarnaPretraga(dan, niz, 13);
		
	struct Rezultat rezultat;
	rezultat.rez       = ind;
	rezultat.korekcija = niz[ind - 1];
	
	return rezultat;
} 
		
	
    	
static Rezultat pronalazenjeMeseca(int dan, Boolean prestupna, int[] nizN, int[] nizP) {

    int[] niz;

    if (prestupna) {
        niz = nizP;
    }
    else {
        niz = nizN;
    }

    int ind = BinarnaPretraga(dan, niz);

    Rezultat rezultat = new Rezultat();
    rezultat.Rez       = ind;
    rezultat.Korekcija = niz[ind - 1];

    return rezultat;
} 
		
	
    	
def pronalazenjeMeseca(dan, prestupna, nizN, nizP):
	if prestupna == True:
		niz = nizP
	else:
		niz = nizN
	
	ind = binarnaPretraga(dan, niz)
		
	return {
		'mesec':     ind,
		'korekcija': niz[ind - 1]
	}
		
	
    	
function pronalazenjeMeseca(dan, prestupna, nizN, nizP) {
	if (prestupna) {
		niz = nizP
	}
	else {
		niz = nizN;
	}

	let ind = binarnaPretraga(dan, niz);
		
	return {
		mesec:     ind,
		korekcija: niz[ind - 1]
	}
} 
		
	
Slika 29. - Funkcija za pronalaženje meseca, shodno unetom timestamp-u.

.... 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:

C#
C++
Java
Python
JavaScript
    	
static Datum timestampUDatum(Int32 t) {
    Double   stamp;
    Rezultat rezultat = new Rezultat();
    Datum    datum    = new Datum();

    stamp          = (Double)t / sekundeUDanu;
    stamp          = Math.Ceiling(stamp);
    rezultat       = PronalazenjeGodine((Int32)stamp, daniUGodini, 1970);

    int godina     = rezultat.Rez;
    bool prestupna = DaLiJePrestupna(godina);
    int danUGodini = (Int32)stamp - rezultat.Korekcija;
    rezultat       = pronalazenjeMeseca(danUGodini, prestupna, meseciN, meseciP);
    int mesec      = rezultat.Rez;
    int dan        = danUGodini - rezultat.Korekcija;

    datum.Godina = godina;
    datum.Mesec  = mesec;
    datum.Dan    = dan;

    return datum;
}
		
	
    	
struct Datum timestampUDatum(int t) {
	double stamp;
	struct Rezultat rezultat;
	struct Datum    datum;
	
	stamp          = (double)t / sekundeUDanu;
	stamp          = ceil(stamp);
	rezultat       = pronalazenjeGodine(stamp, daniUGodini, 1970);
	
	int godina     = rezultat.rez;
	bool prestupna = daLiJePrestupna(godina);
	int danUGodini = stamp - rezultat.korekcija;
	rezultat       = pronalazenjeMeseca(danUGodini, prestupna, meseciN, meseciP);
	int mesec      = rezultat.rez;
	int dan        = danUGodini - rezultat.korekcija;

	datum.godina = godina;
	datum.mesec  = mesec;
	datum.dan    = dan;

	return datum;
}
		
	
    	
static Datum timestampUDatum(int t) {
    double   stamp;
    Rezultat rezultat = new Rezultat();
    Datum    datum    = new Datum();

    stamp    = (double)t / sekundeUDanu;
    stamp    = Math.ceil(stamp);
    rezultat = PronalazenjeGodine((int)stamp, daniUGodini, 1970);

    int godina        = rezultat.Rez;
    Boolean prestupna = DaLiJePrestupna(godina);
    int danUGodini    = (int)stamp - rezultat.Korekcija;
    rezultat          = pronalazenjeMeseca(danUGodini, prestupna, meseciN, meseciP);
    int mesec         = rezultat.Rez;
    int dan           = danUGodini - rezultat.Korekcija;

    datum.Godina = godina;
    datum.Mesec  = mesec;
    datum.Dan    = dan;

    return datum;
}
		
	
    	
def timestampUDatum(t):
	stamp      = t / sekundeUDanu
	stamp      = math.ceil(stamp)
	rez        = pronalazenjeGodine(stamp, daniUGodini, 1970)
	godina     = rez['godina']
	prestupna  = daLiJePrestupna(godina)
	danUGodini = stamp - rez['korekcija']
	rez        = pronalazenjeMeseca(danUGodini, prestupna, meseciN, meseciP)
	mesec      = rez['mesec']
	dan        = danUGodini - rez['korekcija']

	return {
		'godina': godina,
		'mesec':  mesec,
		'dan':    dan
	}
		
	
    	
function timestampUDatum(t) {
	let stamp, rez;
	stamp          = t / sekundeUDanu;
	stamp          = Math.ceil(stamp);
	rez            = pronalazenjeGodine(stamp, daniUGodini, 1970);
	let godina     = rez.godina;
	let prestupna  = daLiJePrestupna(godina);
	let danUGodini = stamp - rez.korekcija;
	rez            = pronalazenjeMeseca(danUGodini, prestupna, meseciN, meseciP);
	let mesec      = rez.mesec;
	let dan        = danUGodini - rez.korekcija;

	return {
		godina: godina,
		mesec:  mesec,
		dan:    dan
	}
}
		
	
Slika 30. - Glavna funkcija za pretvaranje timestamp-a u datum.

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.

Autor članka Nikola Vukićević Za web portal codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook LinkedIn Twitter Viber WhatsApp E-mail
početna > Članci > UNIX Time - Predstavljanje datuma i vremena na računarima
codeBlog codeBlog
Sajt posvećen popularizaciji kulture i veštine programiranja.
Napomena: Tekstovi i slike na sajtu codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt