SeznámÃme se s novým vestavÄným objektem: Date. Tento objekt v sobÄ ukládá datum a Äas a poskytuje metody pro práci s nimi.
Můžeme jej napÅÃklad použÃt k uloženà Äasu vytvoÅenà nebo modifikace nÄÄeho, k mÄÅenà Äasu nebo jen k vypsánà dneÅ¡nÃho data.
VytvoÅenÃ
Abychom vytvoÅili objekt Date, zavoláme new Date() s jednÃm z následujÃcÃch argumentů:
new Date()-
Bez argumentů â vytvoÅà se objekt
Datepro aktuálnà datum a Äas:let nynà = new Date(); alert( nynà ); // zobrazà aktuálnà datum a Äas new Date(milisekundy)-
VytvoÅÃ objekt
Dates Äasem, který odpovÃdá poÄtu milisekund (1/1000 sekundy), které uplynuly od 1. ledna 1970 UTC+0.// 0 znamená 01.01.1970 UTC+0 let leden01_1970 = new Date(0); alert( leden01_1970 ); // nynà pÅidáme 24 hodin a zÃskáme 02.01.1970 UTC+0 let leden02_1970 = new Date(24 * 3600 * 1000); alert( leden02_1970 );Celé ÄÃslo, které pÅedstavuje poÄet milisekund, které uplynuly od zaÄátku roku 1970, se nazývá Äasové razÃtko nebo Äasová znaÄka (anglicky timestamp).
Je to ÄÃselná reprezentace data. Z Äasového razÃtka můžeme vždy vytvoÅit datum pomocÃ
new Date(ÄasovéRazÃtko)a existujÃcà objektDatemůžeme pÅevést na Äasové razÃtko pomocà metodydatum.getTime()(viz nÞe).Data pÅed 1. lednem 1970 majà záporná Äasová razÃtka, napÅ.:
// 31. prosinec 1969 let pros31_1969 = new Date(-24 * 3600 * 1000); alert( pros31_1969 ); new Date(datovýÅetÄzec)-
Jestliže je uveden jediný argument a je to ÅetÄzec, bude z nÄj automaticky vytvoÅeno datum. Algoritmus je stejný, jaký použÃvá
Date.parse. Probereme ho pozdÄji.let datum= new Date("2017-01-26"); alert(datum); // Äas nenà nastaven, takže se pÅedpokládá půlnoc GMT a // pÅizpůsobà se Äasovému pásmu, ve kterém je kód spuÅ¡tÄn. // Výsledek tedy může být: // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) // nebo: // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time) // V Äesku zÅejmÄ bude: // Thu Jan 26 2017 01:00:00 GMT+1100 (StÅedoevropský standardnà Äas) - pozn. pÅekl. new Date(rok, mÄsÃc, den, hodiny, minuty, sekundy, ms)-
VytvoÅà datum ze zadaných složek v mÃstnÃm Äasovém pásmu. Povinné jsou jen prvnà dva argumenty.
- Parametr
rokby mÄl mÃt 4 ÄÃslice. Z důvodu kompatibility se pÅijÃmajà i 2 ÄÃslice a považujà se za19xx, napÅ.98je zde totéž jako1998, ale důraznÄ se doporuÄuje použÃvat vždy 4 ÄÃslice. - Parametr
mÄsÃcmůže být od0(leden) do11(prosinec). - Parametr
denznamená den v mÄsÃci. NenÃ-li uveden, pÅedpokládá se1. - Nejsou-li uvedeny parametry
hodiny/minuty/sekundy/ms, pÅedpokládá se, že jsou0.
PÅÃklad:
new Date(2011, 0, 1, 0, 0, 0, 0); // 1. leden 2011, 00:00:00 new Date(2011, 0, 1); // totéž, hodiny atd. jsou standardnÄ 0Maximálnà pÅesnost je 1 ms (1/1000 s):
let datum = new Date(2011, 0, 1, 2, 3, 4, 567); alert( datum ); // 1.01.2011, 02:03:04.567 - Parametr
PÅÃstup ke složkám data
Existujà metody, kterými můžeme pÅistupovat k roku, mÄsÃci a dalÅ¡Ãm složkám objektu Date:
- getFullYear()
- Vracà rok (4-ÄÃslicový).
- getMonth()
- Vracà mÄsÃc od 0 do 11.
- getDate()
- Vracà den v mÄsÃci od 1 do 31. Název metody skuteÄnÄ vypadá trochu podivnÄ.
- getHours(), getMinutes(), getSeconds(), getMilliseconds()
- Vracejà odpovÃdajÃcà Äasové složky (
getHours()vracà hodiny,getMinutes()minuty,getSeconds()sekundy agetMilliseconds()milisekundy â pozn. pÅekl.).
getYear(), ale getFullYear()Mnoho JavaScriptových motorů implementuje nestandardnà metodu getYear(). Tato metoda je zastaralá. NÄkdy vracà 2-ÄÃslicový letopoÄet. ProsÃme nepoužÃvejte ji. Ke zjiÅ¡tÄnà roku je urÄena metoda getFullYear().
NavÃc můžeme zÃskat i den v týdnu:
- getDay()
- Vracà den v týdnu od
0(nedÄle) do6(sobota). Prvnà den je vždy nedÄle. V nÄkterých zemÃch to tak nenÃ, ale nedá se to zmÄnit.
VÅ¡echny výše uvedené metody vracejà složky relativnà k mÃstnÃmu Äasovému pásmu.
Existujà také jejich UTC-protÄjÅ¡ky, které vracejà den, mÄsÃc atd. pro Äasové pásmo UTC+0: getUTCFullYear(), getUTCMonth(), getUTCDay(). StaÄà vložit "UTC" rovnou za "get".
Jestliže je vaÅ¡e mÃstnà Äasové pásmo vzhledem k UTC posunuto, pak následujÃcà kód zobrazà různé hodiny:
// nynÄjšà datum
let datum = new Date();
// hodina ve vaÅ¡em nynÄjÅ¡Ãm Äasovém pásmu
alert( datum.getHours() );
// hodina v Äasovém pásmu UTC+0 (londýnský zimnà Äas)
alert( datum.getUTCHours() );
KromÄ uvedených metod existujà i dvÄ speciálnÃ, které nemajà UTC-variantu:
- getTime()
-
Vracà Äasové razÃtko data â poÄet milisekund, které uplynuly od 1. ledna 1970 UTC+0.
- getTimezoneOffset()
-
Vracà rozdÃl mezi UTC a mÃstnÃm Äasovým pásmem v minutách:
// jestliže jste v Äasovém pásmu UTC-1, vypÃÅ¡e 60 // jestliže jste v Äasovém pásmu UTC+3, vypÃÅ¡e -180 // (v Äesku vypÃÅ¡e v letnÃm Äase -120, v zimnÃm -60 - pozn. pÅekl.) alert( new Date().getTimezoneOffset() );
Nastavenà složek data
NásledujÃcà metody umožÅujà nastavit složky data a Äasu:
setFullYear(rok, [mÄsÃc], [den])setMonth(mÄsÃc, [den])setDate(den)setHours(hodina, [minuty], [sekundy], [ms])setMinutes(minuty, [sekundy], [ms])setSeconds(sekundy, [ms])setMilliseconds(ms)setTime(milisekundy)(nastavà celé datum z milisekund uplynulých od 1. 1. 1970 UTC)
Každá z nich kromÄ setTime() má UTC-variantu, napÅ. setUTCHours().
Jak vidÃme, nÄkteré metody umožÅujà nastavit vÃce složek najednou, napÅ. setHours. Složky, které nejsou uvedeny, nebudou zmÄnÄny.
PÅÃklad:
let dnes = new Date();
dnes.setHours(0);
alert(dnes); // stále dnes, ale hodina se zmÄnà na 0
dnes.setHours(0, 0, 0, 0);
alert(dnes); // stále dnes, nynà 00:00:00
Automatická oprava
Automatická oprava je velmi Å¡ikovná vlastnost objektů Date. Můžeme nastavit hodnoty mimo rozsah a ty se automaticky pÅizpůsobÃ.
PÅÃklad:
let datum = new Date(2013, 0, 32); // 32. leden 2013 ?!?
alert(datum); // ...je 1. únor 2013!
Datové složky mimo povolený rozsah se automaticky pÅepoÄÃtajÃ.
ÅeknÄme, že potÅebujeme zvýšit datum â28. únor 2016â o 2 dny. Může to být â2. bÅezenâ nebo v pÅÃpadÄ pÅestupného roku â1. bÅezenâ. Na to nemusÃme myslet. StaÄà pÅiÄÃst 2 dny. O zbytek se postará objekt Date:
let datum = new Date(2016, 1, 28);
datum.setDate(datum.getDate() + 2);
alert( datum ); // 1. bÅezen 2016
Tato vlastnost se Äasto použÃvá k zÃskánà data, které nastane po uplynutà zadaného Äasu. NapÅÃklad zÃskejme datum, které bude za â70 sekund od nynÄjÅ¡kaâ:
let datum = new Date();
datum.setSeconds(datum.getSeconds() + 70);
alert( datum ); // zobrazà správné datum
Můžeme nastavit i nulové nebo záporné hodnoty. NapÅÃklad:
let datum = new Date(2016, 0, 2); // 2. leden 2016
datum.setDate(1); // nastavÃme 1. den v mÄsÃci
alert( datum );
datum.setDate(0); // minimálnà den je 1, takže se pÅedpokládá poslednà den pÅedchozÃho mÄsÃce
alert( datum ); // 31. prosinec 2015
Konverze data na ÄÃslo, rozdÃl dat
Když je objekt Date konvertován na ÄÃslo, pÅevede se na Äasové razÃtko, stejné, jaké vracà metoda datum.getTime():
let datum = new Date();
alert(+datum); // poÄet milisekund, totéž jako datum.getTime()
Důležitý vedlejšà efekt: data lze od sebe odeÄÃtat, výsledkem je jejich rozdÃl v milisekundách.
To můžeme využÃt k mÄÅenà Äasu:
let zaÄátek = new Date(); // spustÃme mÄÅenà Äasu
// udÄláme práci
for (let i = 0; i < 100000; i++) {
let dÄlejNÄco = i * i * i;
}
let konec = new Date(); // zastavÃme mÄÅenà Äasu
alert( `Cyklus trval ${konec - zaÄátek} ms` );
Date.now()
Jestliže chceme jenom mÄÅit Äas, nepotÅebujeme objekt Date.
Existuje speciálnà metoda Date.now(), která vracà aktuálnà Äasové razÃtko.
Je sémanticky ekvivalentnà new Date().getTime(), ale nevytváÅà doÄasný objekt Date. Je tedy rychlejšà a nezatÄžuje sbÄraÄ odpadků.
PoužÃvá se vÄtÅ¡inou proto, že je pohodlnÄjÅ¡Ã, nebo tehdy, když záležà na výkonu, napÅÃklad v JavaScriptových hrách nebo jiných specializovaných aplikacÃch.
Tohle je tedy pravdÄpodobnÄ lepÅ¡Ã:
let zaÄátek = Date.now(); // poÄet milisekund od 1. ledna 1970
// udÄláme práci
for (let i = 0; i < 100000; i++) {
let dÄlejNÄco = i * i * i;
}
let konec = Date.now(); // hotovo
alert( `Cyklus trval ${konec - zaÄátek} ms` ); // odeÄÃtáme ÄÃsla, ne data
Benchmarky
Jestliže chceme spolehlivý benchmark funkce nároÄné na CPU, mÄli bychom být opatrnÃ.
NapÅÃklad zmÄÅme dvÄ funkce, které poÄÃtajà rozdÃl mezi dvÄma daty: která z nich je rychlejÅ¡Ã?
Taková mÄÅenà výkonu se Äasto nazývajà âbenchmarkyâ.
// máme datum1 a datum2, která funkce vrátà rychleji jejich rozdÃl v ms?
function rozdÃlOdeÄtenÃm(datum1, datum2) {
return datum2 - datum1;
}
// nebo
function rozdÃlPomocÃGetTime(datum1, datum2) {
return datum2.getTime() - datum1.getTime();
}
Tyto dvÄ funkce dÄlajà pÅesnÄ totéž, ale jedna z nich použÃvá explicitnà datum.getTime(), aby zÃskala datum v milisekundách, zatÃmco druhá se spoléhá na transformaci data na ÄÃslo. Jejich výsledek je vždy stejný.
Která je tedy rychlejš�
Jako prvnà můžeme dostat nápad, že je spustÃme mnohokrát za sebou a zmÄÅÃme rozdÃl Äasů. V naÅ¡em pÅÃpadÄ jsou tyto funkce velmi jednoduché, takže to musÃme udÄlat alespoÅ 100 000krát.
ZmÄÅme to:
function rozdÃlOdeÄtenÃm(datum1, datum2) {
return datum2 - datum1;
}
function rozdÃlPomocÃGetTime(datum1, datum2) {
return datum2.getTime() - datum1.getTime();
}
function bench(f) {
let datum1 = new Date(0);
let datum2 = new Date();
let zaÄátek = Date.now();
for (let i = 0; i < 100000; i++) f(datum1, datum2);
return Date.now() - zaÄátek;
}
alert( 'Äas funkce rozdÃlOdeÄtenÃm: ' + bench(rozdÃlOdeÄtenÃm) + ' ms' );
alert( 'Äas funkce rozdÃlPomocÃGetTime: ' + bench(rozdÃlPomocÃGetTime) + ' ms' );
Páni! Použità getTime() je mnohem rychlejÅ¡Ã! Je to proto, že tam nenà žádná typová konverze, a proto je pro motory mnohem snadnÄjšà funkci optimalizovat.
Dobrá, nÄco tedy máme. To vÅ¡ak jeÅ¡tÄ nenà dobrý benchmark.
PÅedstavme si, že v dobÄ spuÅ¡tÄnà bench(rozdÃlOdeÄtenÃm) CPU provádÄl paralelnÄ nÄco jiného a to mu ubÃralo zdroje. A v dobÄ spuÅ¡tÄnà bench(rozdÃlPomocÃGetTime) tahle práce skonÄila.
V modernÃch multiprocesnÃch operaÄnÃch systémech je to dosti reálný scénáÅ.
Výsledkem je, že prvnà benchmark mÄl ménÄ zdrojů CPU než druhý. To mohlo vést ke Å¡patným výsledkům.
Pro spolehlivÄjšà benchmarkové testovánà bychom mÄli celý balÃÄek benchmarků spustit nÄkolikrát za sebou.
NapÅÃklad takto:
function rozdÃlOdeÄtenÃm(datum1, datum2) {
return datum2 - datum1;
}
function rozdÃlPomocÃGetTime(datum1, datum2) {
return datum2.getTime() - datum1.getTime();
}
function bench(f) {
let datum1 = new Date(0);
let datum2 = new Date();
let zaÄátek = Date.now();
for (let i = 0; i < 100000; i++) f(datum1, datum2);
return Date.now() - zaÄátek;
}
let Äas1 = 0;
let Äas2 = 0;
// spustÃme bench(rozdÃlOdeÄtenÃm) a bench(rozdÃlPomocÃGetTime) stÅÃdavÄ, každou 10krát
for (let i = 0; i < 10; i++) {
Äas1 += bench(rozdÃlOdeÄtenÃm);
Äas2 += bench(rozdÃlPomocÃGetTime);
}
alert( 'Celkový Äas funkce rozdÃlOdeÄtenÃm: ' + Äas1 );
alert( 'Celkový Äas funkce rozdÃlPomocÃGetTime: ' + Äas2 );
Modernà JavaScriptové motory zaÄÃnajà aplikovat pokroÄilé optimalizace jen na âhorký kódâ, který se provádà mnohokrát (nenà tÅeba optimalizovat to, co se provádà jen vzácnÄ). Ve výše uvedeném pÅÃkladu tedy prvnà spuÅ¡tÄnà nejsou dobÅe optimalizovaná. Můžeme chtÃt pÅidat zahÅÃvacà kolo:
// pÅidáno pro âzahÅátÃâ pÅed hlavnà smyÄkou
bench(rozdÃlOdeÄtenÃm);
bench(rozdÃlPomocÃGetTime);
// nynà benchmark
for (let i = 0; i < 10; i++) {
Äas1 += bench(rozdÃlOdeÄtenÃm);
Äas2 += bench(rozdÃlPomocÃGetTime);
}
Modernà JavaScriptové motory provádÄjà množstvà optimalizacÃ, které mohou vylepÅ¡it výsledky âumÄlých testůâ ve srovnánà s âbÄžným použitÃmâ, zvláštÄ když provádÃme benchmark nÄÄeho velmi malého, napÅ. práce operátoru nebo vestavÄné funkce. Pokud tedy chcete skuteÄnÄ porozumÄt výkonu, pak si prosÃme prostudujte, jak funguje JavaScriptový motor. A pak už pravdÄpodobnÄ nebudete mikrobenchmarky vůbec potÅebovat.
Výborný balÃk Älánků o V8 naleznete na https://mrale.ph.
NaÄÃtánà data z ÅetÄzce
Metoda Date.parse(ÅetÄzec) dokáže naÄÃst datum z ÅetÄzce.
ÅetÄzec by mÄl mÃt formát RRRR-MM-DDTHH:mm:ss.sssZ, kde:
RRRR-MM-DDâ je datum: rok-mÄsÃc-den.- Znak
"T"se použÃvá jako oddÄlovaÄ. HH:mm:ss.sssâ je Äas: hodiny, minuty, sekundy a milisekundy.- Nepovinná Äást
'Z'oznaÄuje Äasové pásmo ve formátu+-hh:mm. Samotné pÃsmenoZznamená UTC+0.
Jsou možné i kratšà varianty, napÅ. RRRR-MM-DD nebo RRRR-MM nebo jen RRRR.
Volánà Date.parse(ÅetÄzec) rozebere ÅetÄzec v uvedeném formátu a vrátà Äasové razÃtko (poÄet milisekund od 1. ledna 1970 UTC+0). NenÃ-li formát správný, vrátà NaN.
PÅÃklad:
let ms = Date.parse('2012-01-26T13:51:50.417-07:00');
alert(ms); // 1327611110417 (Äasové razÃtko)
Z tohoto Äasového razÃtka můžeme ihned vytvoÅit objekt pomocà new Date:
let datum = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );
alert(datum);
ShrnutÃ
- Datum a Äas v JavaScriptu pÅedstavuje objekt Date. Nemůžeme vytvoÅit âjenom datumâ nebo âjenom Äasâ: objekty
Dateobsahujà vždy obojÃ. - MÄsÃce se poÄÃtajà od nuly (ano, leden je nultý mÄsÃc).
- Dny v týdnu ve funkci
getDay()se také poÄÃtajà od nuly (to je nedÄle). Datese automaticky opravuje, když jsou nastaveny složky mimo správný rozsah. To je dobré pro pÅiÄÃtánÃ/odeÄÃtánà dnů/mÄsÃců/hodin.- Data lze od sebe odeÄÃtat, výsledkem je jejich rozdÃl v milisekundách. To je proto, že
Datese pÅi konverzi na ÄÃslo pÅevede na Äasové razÃtko. - Äasové razÃtko Äasu, který je právÄ teÄ, rychle zÃskáme pomocÃ
Date.now().
VÅ¡imnÄte si, že na rozdÃl od mnoha jiných systémů se Äasová razÃtka v JavaScriptu poÄÃtajà v milisekundách, ne v sekundách.
NÄkdy potÅebujeme pÅesnÄjšà mÄÅenà Äasu. Samotný JavaScript neobsahuje způsob, jak mÄÅit Äas v mikrosekundách (1 milióntina sekundy), ale vÄtÅ¡ina prostÅedà jej poskytuje. NapÅÃklad prohlÞeÄe majà metodu performance.now(), která vracà poÄet milisekund od zaÄátku naÄÃtánà stránky s pÅesnostà na mikrosekundy (3 ÄÃslice za desetinnou Äárkou):
alert(`NaÄÃtánà zaÄalo pÅed ${performance.now()}ms`);
// NÄco jako: "NaÄÃtánà zaÄalo pÅed 34731.26000000001ms"
// .26 jsou mikrosekundy (260 mikrosekund)
// vÃce než 3 ÄÃslice za desetinnou Äárkou jsou chyba pÅesnosti, jedinÄ prvnà 3 jsou správné
Node.js má modul microtime a jiné způsoby. Technicky témÄÅ každé zaÅÃzenà a každé prostÅedà umožÅuje zÃskat lepšà pÅesnost, jenom ne pomocà Date.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)