Data si timpul in JavaScript

Cum lucram cu data calendaristica in JavaScript

Daniel Turcu

20 minute read

Despre obiectul Data

Faceti urmatorul calcul:


let acum = Date.now();

// impartim la 1000 (rotunjim)
let s = Math.round(acum / 1000);

// impartim apoi la 60 de doua ori
let m = Math.round(s / 60);
let h = Math.round(m / 60);

// mai impartim o data la 24 
let z = Math.round(h / 24);

// si la final mai impartim si la 365
let a = Math.round(z / 365);

// apoi scadem rezultatul din anul curent
let start = (new Date()).getFullYear() - a;



// Ar trebui sa obtinem 1970

Un obiect de tipul Date are un numar intern (o primitiva) care specifica timpul scurs de la 1 ianuarie 1970 UTC (in milisecunde). Calculele de mai sus ne vor duce intotdeauna la anul 1970 pentru ca este chiar definitia datei in JavaScript.

Printr-un obiect de tipul Date, avem la dispozitie metode care ne permit afisarea si conversia datei in diferite formate.


Timezone

Am vazut mai sus momentul genezei in JavaScript: 1 ianuarie 1970 UTC. Ce-i cu acel UTC agatat de o data, de altfel, normala?

Intern, JavaScript mentine valorile in UTC ( Coordinated Universal Time ), insa functiile de manipulare a datei lucreaza cu timpul local (adica ia in calcul timpul dat de sistemul de operare).

Fiecare dintre noi ne uitam la ceas (…ul de la telefon) si citim: 8 AM sau 8 dimineata. Indiferent unde suntem pe glob, cand zicem 8 AM, este dimineata chiar daca in partea opusa a pamantului e noapte; acolo poate este ora 8 PM. Acestea sunt datele locale (local time).

Acum sa ne gandim ca sunam pe cineva dintr-o tara care are alt timezone. La finalul convorbirii stabilim sa ne auzim din nou la ora 21:00. Va trebui sa-l intrebam cat e ora la el, sa ne gandim cate ore sunt pana la ora 21 (in timpul local) apoi sa adunam acel numar la ora curenta din tara lui si ii comunicam ora de intalnire.

O alta alternativa ar fi sa comunicam in acelasi limbaj al timpului, cum ar fi UTC.

De multe ori exista confuzie intre UTC si GMT (Greenwich Mean Time), ceea ce este si normal, pentru ca indica acelasi moment. Definitiile sunt diferite: GMT indica ora 12:00 cand soarele este la cel mai inalt punct deasupra Observatorului Regal Greenwich din Londra. GMT este un punct de referinta pentru toate celelalte zone (GMT+2). UTC-ul este masurat atomic.

Ca o curiozitate, din cauza rotatiilor pamantului, secundele nu sunt egale in GMT. In UTC toate secundele au aceeasi lungime (fiind masurate atomic) iar ca sa nu se decaleze fata de GMT cu mai mult de o secunda, sunt introduse asa numitele leap seconds din cand in cand. (la 6 luni se anunta daca se adauga sau nu o secunda la UTC; de exemplu, din 2017 nu s-a mai adaugat, insa in 2015 si 2016 au fost adaugate aceste leap seconds).

In JavaScript lucram cu UTC nu cu GMT.

Cand lucram cu Date/Time ne mai lovim si de DTS (Daylight Savings Time). O mai denumim ora de vara/ora de iarna. Obiectul Date in JavaScript nu mentine intern nimic legat de DTS, pur si simplu e o valoare in milisecunde de la 1 ianuarie 1970, insa daca afisam data ca string, de exemplu, aceasta va lua in calcul timpul sistemului, deci va tine cont de DTS.


Crearea unei variabile de tipul Date

Pentru a lucra cu Date/Time avem functia constructor Date() pusa la dispozitie de limbaj.


let d1 = new Date();    
// Fri Apr 03 2020 23:33:45 GMT+0300 (Eastern European Summer Time)

let d2 = new Date('2019-10-21');
// Mon Oct 21 2019 03:00:00 GMT+0300 (Eastern European Summer Time)

let d2e = new Date('2019-21-10');
// Invalid Date

Observam ca nu este permisa construirea unei date folosind un string cu formatul yyyy-dd-mm. Trebuie sa fim atenti cu datele care pot provoca confuzie si erori in aplicatie, cum ar fi 2012-10-12; este 10 decembrie sau 12 octombrie.


ISO 8601

Exista un standard care ne ajuta sa evitam confuziile de genul celei evidentiate mai sus (2012-10-12). Acesta este standardul ISO 8601 pentru Date/Time:


Date	                2020-04-04
Date and time in UTC	2020-04-04T09:07:50+00:00   // +00 sau Z este acelasi lucru
                        2020-04-04T09:07:50Z        // UTC time (fara offset)

ISO foloseste pentru ore sistemul 24h. O zi incepe cu ora 00 si se termina cu 23; ora 24 care este folosit pentru a indica miezul noptii la nivelul unei zile calendaristice.

Alte exemple ISO:


hh:mm:ss.sss	sau	    hhmmss.sss
hh:mm:ss	    sau	    hhmmss
hh:mm	        sau	    hhmm
hh

Specificarea orei in sistemul UTC:


<time>Z         // timpul UTC

<time>±hh:mm    // cu offset (timezone)
<time>±hhmm
<time>±hh

Ora specificata in UTC pentru timezone-ul Romaniei (Bucuresti): 2020-10-21T22:10+02:00. Asta inseamna ca am specificat ora locala din Romania 22:10. Adica ora in UTC va fi 20:00:


let d3 = new Date(`2020-10-21T22:10+02:00`);

d3.getUTCHours(); // 20

Conventiile ISO pentru formatul datei:

valoare ISO definitie
YYYY anul in format cu 4 digiti
MM luna (ianuarie = 01, decembrie = 12 )
DD ziua lunii (0 - 31)
- delimitator data
T la dreapta lui T se afla timpul
HH orele (0 - 23)
mm minute (0 - 59)
ss secunde (0 - 59)
sss milisecunde (0 - 999)
: delimitator timp
Z Z la final indica timpul in format UTC. Lipsa lui Z indica timpul local

Atentie la crearea unei variabile de tipul Date din forma simplificata:


let d4 = new Date(`2010-10-14`);

Daca sunteti in New York (GMT-5 de exemplu) data va avea valoarea Thu Oct 13 2010 19:00:00 GMT-0500. Daca suntem in Romania valoarea zilei se pastreaza: Thu Oct 14 2010 03:00:00 GMT+0300

Asta se intampla pentru ca nu am specificat timpul, iar JavaScript creeaza variabila in formatul UTC. Deci, daca vrem sa cream o data in timpul local, va trebui sa specificam si timpul (chiar daca nu ne intereseaza in apicatie, este de ajuns sa punem momentul 0 al zilei: 00:00:00).

Vom crea o noua data astfel:


let d5 = new Date('2010-10-14T00:00');

Pentru Bucuresti avem valoarea Thu Oct 14 2010 00:00:00 GMT+0300 care in UTC se traduce astfel:


let d6 = new Date('2010-10-14T00:00');

d6.toUTCString(); // 'Wed, 13 Oct 2010 21:00:00 GMT'

Persoana care locuieste in New York va crea o data cu timpul local specificat, chiar daca JavaScript stocheaza intern valoarea in UTC:


let d6ny = new Date('2010-10-14T00:00');

console.log(d6ny);  // 'Thu Oct 14 2010 00:00:00 GMT-0500'
d6ny.toUTCString(); // 'Thu, 14 Oct 2010 05:00:00 GMT'

Deci cand timpul local in New York este 00:00, la Greenwich este ora 05:00, adica data in format UTC este

Thu, 14 Oct 2010 05:00:00 GMT


Alte moduri de creare a datelor

Am vazut cum cream variabile Date/Time folosind string-uri. Din cauza erorilor ce pot aparea datorita modului in care browser-ele pot interpreta string-urile de initializare, un mod mai bun de a initializa o data este sa folosim parametri dedicati pentru an, luna, zi, ora, etc. Sau putem folosi timestamps care, la fel, nu provoaca confuzie.


let d7 = new Date(2010, 10, 24, 10, 30, 00);

// new Date(yyyy, mm, dd, hh, mm, ss)

Daca verificati data returnata de d7 avem rezultatul Wed Nov 24 2010 10:30:00 GMT+0200.

Daca nu observati nimic ciudat, mai uitati-va o data la luna: Nov. Luna este specificata pornind de la 0 la 11 (Ianuarie - 0, Decembrie - 11).

Nu uitam ca daca folosim stringuri ISO nu este la fel:


let d7iso = new Date('2010-10-24T10:30:00');

console.log(d7iso);
// 'Sun Oct 24 2010 10:30:00 GMT+0300'

Orice data creata cu constructorul Date(), folosind parametri, va fi considerat timp local:


let d8 = new Date(2010, 10, 24);

console.log( d8 );                  // 'Wed Nov 24 2010 00:00:00 GMT+0200'
console.log( d8.toUTCString() );    // 'Tue, 23 Nov 2010 22:00:00 GMT'

Daca totusi vrem sa cream date folosind UTC putem sa utilizam metoda UTC() pusa la dispozitie pe obiectul Date:


let d8utc = new Date( Date.UTC(2010, 10, 24) );

console.log( d8utc );                  // 'Wed Nov 24 2010 02:00:00 GMT+0200'
console.log( d8utc.toUTCString() );    // 'Wed, 24 Nov 2010 00:00:00 GMT'

JavaScript stie acum ca am folosit UTC asa ca atunci cand afisam timpul local va adauga doua ore (pentru Romania); d8utc.toUTCString() va afisa fix ce am trimis noi contructorului.

Putem crea o data care sa reprezinte timpul curent folosind contructorul fara parametri.


let d8now = new Date();

console.log(d8now); // 'Sat Mar 04 2020 17:47:38 GMT+0300'


Timestamps

Timestamp in JavaScript masoara milisecundele scurse de la 1 ianuarie 1970 UTC.

Daca avem datele stocate in timestamps, putem crea un obiect de tipul Date folosindu-l ca parametru pentru constructorul Date():


let d9 = new Date(1290630600000);

console.log(d9);    // 'Wed Nov 24 2010 22:30:00 GMT+0200'

Timestamps-urile sunt utile si pentru a compara doua date. Mai intai obtinem valoarea in milisecunde si apoi comparam aceste doua numere.

Putem obtine timestamp-ul folosind metoda getTime() (Date.prototype.getTime):

d0.getTime().

Aceeasi valoare o putem folosi cu valueOf() care ne returneaza valoarea primitivei interne, este chiar timestamp-ul de care avem nevoie:


let d10a = new Date(2010, 10, 24);
let d10b = new Date(2010, 10, 26);

let d10a_ts = d10a.getTime();   // sau d10a.valueOf();
let d10b_ts = d10b.getTime();   // sau d10b.valueOf();

let diff_ts = d10b_ts - d10a_ts;    // diferenta in milisecunde
let diff_h = Math.round( diff_ts / 1000 / 60 / 60 );  // diferenta in ore

console.log(diff_h);    // 48

Mai sus am obtinut diferenta in ore intre doua date, transformandu-le in timestamps.


Problema: Cate zile au trecut de la data 12.12.2012?

Putem folosi metoda Date.now() ca sa aflam timestam-ul curent.


let d11now_ts = Date.now();

let d11then = new Date(2012,11,12);  // nu uitam ca valoarea pentru luna incepe de la 0 - ianuarie
let d11then_ts = d11then.getTime();  // timestamp-ul - aceeasi valoare cu valueOf()

let z11 = Math.round( (d11now_ts - d11then_ts) / 1000 / 60 / 60 / 24 );

// 2671

valueOf() este o metoda standard prezenta pe prototipul lui Object: Object.prototype.valueOf. Constructorul Data are implementata o varianta specifica pentru lucrul cu Date/Time, adica Date.prototype.valueOf returneaza valoarea interna a primitivei, adica timestamp-ul. In cazul nostru poate inlocui utilizarea lui getTime(), care este metoda recomandata pentru obtinerea valorii timestamp.

Varianta la calculul de sus:


let d12now_ts = Date.now();     // timestamp
let d12then = new Date(2012,11,12);  // 'Wed Dec 12 2012 00:00:00 GMT+0200'

let z12 = Math.round( (d12now_ts - d12then) / 1000 / 60 / 60 / 24 );

// 2671

Un calcul in minus si aceeasi valoare ca rezultat. Functioneaza si daca inlocuim timestamp-ul curent cu data curenta, adica in loc de Date.now() utilizam new Date():


let d13now = new Date();          // 'Sat Apr 04 2020 18:17:37 GMT+0300'
let d13then = new Date(2012,11,12);  // 'Wed Dec 12 2012 00:00:00 GMT+0200'

let z12 = Math.round( (d13now - d13then) / 1000 / 60 / 60 / 24 );

// 2671

d13now - d13then returneaza automat diferenta dintre timestamp-uri. Se intampla asta pentru ca folosim doua tipuri de date referinta intr-un context numeric iar JavaScript va apela intern functia valueOf care returneaza exact timestamp.

Haideti acum sa adunam o saptamana (in milisecunde) la data de azi:


let weekMiliSeconds = 7 * 24 * 60 * 60 * 1000;    // 7z*24h*60m*60s*1000ms = 604800000

let d14now = new Date();    // data de azi

// urmam procedeul de mai sus
let nextWeekDate_ts = d14now + weekMiliSeconds; // timestamp-ul peste o saptamana (!)

let nextWeekDate = new Date(nextWeekDate_ts);

Rezultatul final nu este ceea ce asteptam:

"Sat Apr 04 2020 18:42:40 GMT+0300 (Eastern European Summer Time)604800000"

Este o concatenare intre data curenta ca string si timestampul transformat in string. Aici valueOf() nu ne ajuta pentru ca avem un string + number care prin mecanismul de coercion (conversie implicita) al lui javascript ajunge sa concateneze string + string:

Putem simplifica ce se intampla prin codul de mai jos:


console.log("200" - 10);    // 190

console.log("200" + 10);    // 20010

Cum putem rezolva problema cu adunarea unei saptamani?


let weekMiliseconds = 7 * 24 * 60 * 60 * 1000;    // 7z*24h*60m*60s*1000ms = 604800000

let d14now = new Date();    // data de azi

// let nextWeekDate_ts = d14now + weekMiliseconds; // (!) concatenare intre string si number = string

// fortam context numeric pentru d14now
// acum se forteaza apelarea interna a lui valueOf pentru d14now
let nextWeekDate_ts = +d14now + weekMiliseconds; 

// a doua varianta foloseste explicit getTime() ( sau valueOf() )
let nextWeekDate_ts_v2 = d14now.getTime() + weekMiliseconds; 

let nextWeekDate = new Date(nextWeekDate_ts);       // 'Sat Apr 11 2020 18:53:17 GMT+0300'
let nextWeekDate_v2 = new Date(nextWeekDate_ts_v2); // 'Sat Apr 11 2020 18:53:17 GMT+0300'



Formatarea pentru afisare

Nu putem specifica in JavaScript un format de tipul ‘dd MM yyyy’ (nu vorbim aici de utilizarea altor librarii, precum Moment.js , care are o metoda format()). In JavaScript avem la dispozitie obiectul Intl.DateTimeFormat pe al carui prototip exista metoda format() care chiar daca nu functioneaza cu string-uri de formatare precum dd MM yyyy, ne ajuta cu formatarea datelor. O sa-i dedicam o sectiune separata mai spre finalul articolului.

Sa vedem cateva exemple de afisari standard implicite:


let d15 = new Date(2012, 9, 24);

d15.toString()
// "Wed Oct 24 2012 00:00:00 GMT+0300 (Eastern European Summer Time)"

d15.toDateString()
// "Wed Oct 24 2012"

d15.toTimeString()
// "00:00:00 GMT+0300 (Eastern European Summer Time)"


d15.toUTCString()
// "Tue, 23 Oct 2012 21:00:00 GMT"

d15.toGMTString()
// "Tue, 23 Oct 2012 21:00:00 GMT"

d15.toISOString()
// "2012-10-23T21:00:00.000Z"

d15.toLocaleString()
// "10/24/2012, 12:00:00 AM"

d15.toLocaleDateString()
// "10/24/2012"

d15.toLocaleTimeString()
// "12:00:00 AM"

Functiile toLocale ne permit intr-o oarecare masura configurarea modului de afisare al datelor.

De exemplu:


let d15base = new Date(2012, 9, 24);

const options = { weekday: 'Long', year: 'numeric', month: 'long', day: 'numeric' };

let d15final = d15base.toLocaleDateString('ro-RO', options);

console.log(d15base);   // Wed Oct 24 2012 00:00:00 GMT+0300
console.log(d15final);  // miercuri, 24 octombrie 2012


Control total al formatarii

Sa vedem cum putem prelua controlul modului de afisare. Practic putem sa ne construim singuri o functie care sa se ocupe de modurile custom care poate nu sunt puse la dispozitie de JavaScript si obiectele lui.

Daca unul din aceste moduri de afisare sunt de ajuns, atunci le putem utiliza si am scapat de problema cu efort minim, altfel ar trebui sa folosim metodele care ne ofera valorile pentru an, luna, zi, ora, etc.

Sa zicem ca avem nevoie de formatul custom dd month yyyy, adica 24 octombrie 2012.

Sa vedem ce avem la dispozitie de la JavaScript:


let d16 = new Date(2012, 9, 24);

// pentru a obtine ziua
let z16 = d16.getDate();        // 24

// pentru a obtine anul
let y16 = d16.getFullYear();    // 2012

// pentru a obtine luna 
let m16 = d16.getMonth();       // 9 -  nu este ce am cerut

Pentru a obtine luna ca denumire ar trebui sa avem o functie/obiect/array care sa faca corespondenta intre luna ca numar si luna ca denumire:


const getMonthAs = (monthNumber = 0, type = 0) => {
    // type = 0: Ianuarie
    // type = 1: Ian

    let fullNames = ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'];

    // vom implementa doar primul tip
    switch (type) {
        case 0: 
        default:
            return fullNames[monthNumber];

    }

}

Acum avem tot ce ne trebuie ca sa formatam data in formatul dorit. Mai intai vom incepe o functie formatter:


const formatData = (data, format) => {
    switch (format) {
        case 'dd month yyyy': 
        default:
            return `${ data.getDate() } ${ getMonthAs( data.getMonth(), 0) } ${ data.getFullYear() }`;
    }

}

Acum utilizam functia de formatare a datelor:


let d16 = new Date(2012, 9, 24);

formatDataAs(d16, 'dd month yyyy');     // "24 Octombrie 2012"



Cum comparam doua date?

Vom defini doua date diferite si le comparam:


let d17a = new Date(2012, 9, 24);
let d17b = new Date(2012, 9, 28);

console.log( d17a < d17b );      // true
console.log( d17a > d17b );      // false
console.log( d17a == d17b );     // false

Sa rulam acum urmatorul cod:


let d18a = new Date(2012, 9, 24);
let d18b = new Date(2012, 9, 24);

console.log( d18a == d18b );      // (!) false
console.log( d18a > d18b );       // false       
console.log( d18a < d18b );       // (!) false      

Se pare ca atunci cand cream doua date care ne asteptam sa fie egale, niciun comparator nu le multumeste.

Insa mai avem doi operatori de verificat: <= si >=


console.log( d18a <= d18b );    // true
console.log( d18a >= d18b );    // true

Deci daca cele doua conditii sunt adevarate, atunci datele sunt egale (!). Nu prea ne place modul asta de a verifica daca doua date sunt egale, asa ca vrem sa mai verificam ceva:


console.log( d18a.getTime() === d18b.getTime() );  // true (timestamp = 1351026000000)

// sau
console.log( +d18a === +d18b );     // true
// inducem un context numeric, deci fortam utilizarea functiei valueOf (adica timestamp-urile)

// echivalent cu 
console.log( d18a.valueOf() === d18b.valueOf() )

Putem sa facem o functie care sa ne ajute cu verificarea egalitatii datelor:


const equalDates = (d1, d2) => {
    return d1.getTime() === d2.getTime()
}

console.log( equalDates(d18a, d18b) );


Modificari ale datelor

Avem mai multe metode prin care putem sa alteram obiectele de tip Date/Time.


d18a.setDate(25); 

console.log(d18a);  // 'Thu Oct 25 2012 00:00:00 GMT+0300'

Functiile de actualizare returneaza o valoare timestamp cu noua valoare a datei:


let rez = d18a.setDate(25);   

console.log(rez);   // 1351112400000

Alte metode utile pentru modificarea datelor:


var d = new Date(2020, 01, 31);

d.setFullYear(2021);
d.setMonth(10);
d.setDate(30);

d.setHours(21);
d.setMinutes(15);
d.setSeconds(00);
d.setMilliseconds(500);

// deprecated
d.setYear(80);  // returneaza anul 1980
// versus
d.setFullYear(80); // se refera la anul 80

Pentru fiecare dintre aceste metode, exista in oglinda varianta UTC (cu exceptia lui setYear()):


var d = new Date(2020, 01, 31);

d.setUTCFullYear(2021);
d.setUTCMonth(10);
d.setUTCDate(30);

d.setUTCHours(21);
d.setUTCMinutes(15);
d.setUTCSeconds(00);
d.setUTCMilliseconds(500);


Adunari cu Date/Time

Am vazut mai sus un exemplu in care am adunat o saptamana la data curenta. Am calculat cate milisecunde sunt intr-o saptamana si am adunat la data curenta, ca timestamp. Mai departe stim sa obtinem o data daca avem timestamp-ul:

let d = new Date(timestamp);

Sa vedem acum alte moduri de rezolvare a aceleiasi probleme: adunarea unei saptamani la o anumita data. Similar cu adunarea/scaderea zilei putem sa utilizam aceleasi tehnici pentru ani, luni, ore, minute, etc.

// sa adunam 7 zile la data '24 octombrie 2012'

let d19base = new Date(2012, 9, 24); 

// am vazut ca putem folosi metodele set pentru modificarea unui obiect Date
// vom crea o noua data pornind de la data de baza
let d19final = new Date(d19base);

// acum putem adauga linistiti 7 zile la d19final
d19final.setDate( d19base.getDate() + 7 );      // d19base ramane nealterat

console.log( d19final );    // 'Wed Oct 31 2012 00:00:00 GMT+0200'

Similar cu a aduna zile, putem aduna orice alte unitati (ore, minute, luni, …).

Daca ne uitam la rezultatul final (Wed Oct 31 2012) pare ca am fost atenti sa nu depasim luna octombrie cand am adaugat numarul de zile (24 + 7 = 31).

Haideti sa vedem ce se intampla daca in loc de 7 adunam 10 zile la aceeasi data. Deci vrem sa facem calculul:

24 octombrie 2012 + 10 zile

34 octombrie nu exista. Sa vedem cum se descurca JavaScript cu asta:


let d20base = new Date(2012, 9, 24); 
let d20final = new Date(d20base);

d20final.setDate( d20base.getDate() + 10 );

console.log(d20final);      // 'Sat Nov 03 2012 00:00:00 GMT+0200'

Nu trebuie sa ne facem griji cu aceste depasiri, JavaScript se ocupa de asta (macar atat …).


O alta abordare pentru aceeasi problema:


let d21base = new Date(2012, 9, 24); 

// Extragem fiecare valoare in parte
const y = d21base.getFullYear();
const m = d21base.getMonth();
const z = d21base.getDate();

// Cream o noua data cu adunarea saptamanii facuta in constructor 
const d21final = new Date(y, m, z + 7);

console.log(d21final);      // 'Wed Oct 31 2012 00:00:00 GMT+0200'

Am vazut trei moduri prin care putem face operatii de adunare/scadere cu tipul Date:

  • transformarea in timestamp-uri

  • utilizarea metodelor set

  • crearea unei noi date cu calculele facute in constructor


Metoda Date.now() utilizata in articol nu e suportata de IE<8.


Sa implementam afisare custom pentru dd-mm-yyyy

Pentru solutii de afisare custom aveti la dispozitie intregul limbaj JavaScript si in functie de cerinte alegeti ce vi se potriveste. De exemplu, vrem sa imbunatatim functia de formatare a datei inceputa ceva mai sus.

Vrem sa adaugam posibilitatea sa putem formata o data ca dd-mm-yyyy ( 24-10-2012 ).

Imbunatatim functia:


const formatData = (data, format) => {
    switch (format) {
        case 'dd-mm-yyyy':
            return `${ data.getDate() }-${ data.getMonth()+1 }-${ data.getFullYear() }`;

        case 'dd month yyyy': 
        default:
            return `${ data.getDate() } ${ getMonthAs( data.getMonth(), 0) } ${ data.getFullYear() }`;
    }

}

Simplu nu? Sa facem cateva teste (nu uitati sa rulati codul care creeaza functiile getMonthAs() si formatData()):


let d22 = new Date(2012, 9, 24);

console.log( formatData(d22) );                     // 24 Octombrie 2012
console.log( formatData(d22, 'dd-mm-yyyy') );       // 24-10-2012
console.log( formatData(d22, 'dd month yyyy') );    // 24 Octombrie 2012

Gata. Sau mai putem imbunatati ceva? Parca ar mai trebui facut un test:


let d23 = new Date(2012, 5, 4);

console.log( formatData(d23) );                     // 4 Iunie 2012
console.log( formatData(d23, 'dd-mm-yyyy') );       // 4-6-2012
console.log( formatData(d23, 'dd month yyyy') );    // 4 Iunie 2012

Nu mai avem doi digiti pentru zile/luni. Suntem multumiti de rezultat? Rezultatul pentru dd month yyyy putem sa-l lasam asa cum este, insa in cazul formatarii dd-mm-yyyy poate am putea sa imbunatatim putin afisarea. Ar cam trebui sa avem raspunsul:

04-06-2012


// un digit afisat ca doi digiti, precedat de 0
// 4 -> 04
const to2 = (d) => d <= 9 ? `0${d}` : `${d}`;

// utilizam to2() pentru zi si luna pe cazul 'dd-mm-yyyy'
const formatData = (data, format) => {
    switch (format) {
        case 'dd-mm-yyyy':
            return `${ to2(data.getDate()) }-${ to2(data.getMonth()+1) }-${ data.getFullYear() }`;

        ...
    }

}

let d23 = new Date(2012, 5, 4);

console.log( formatData(d23, 'dd-mm-yyyy') );   // 04-06-2012


Stocarea datelor

Cum persistam valorile de tip Date/Time? Este aproape obligatoriu ca serverul sa primeasca datele in format UTC. Putem trimite serverului un string in format standard ISO sau un timestamp.

Lasam browserul sa se ocupe de afisarea datelor in timpul local iar serverele trebuie sa foloseasca UTC.

In JavaScript putem utiliza functiile toJSON() sau toISOString():


let d24 = new Date(2012, 9, 24, 22, 30);

let d24iso = d24.toISOString();
let d24json = d24.toJSON(); 

console.log( d24iso );      // 2012-10-24T19:30:00.000Z
console.log( d24json );     // 2012-10-24T19:30:00.000Z


Intl.DateTimeFormat

De ceva timp avem la dispozitie un obiect util pentru formatarea tipurilor Date: Intl: DateTimeFormat.

IE 10 si mai jos nu au access la acest obiect. Edge il are implementat, iar versiunile Chrome/Firefox mai noi de 2013/2014 au, deasemenea, access la el.

Pe obiectul global gasim obiectul Intl. Pe acest obiect mai gasim niste proprietati, printre care si Intl.DateTimeFormat. Daca mergem mai departe gasim si Intl.DateTimeFormat.format().

Intl.DateTimeFormat este o functie constructor ce ne ajuta sa formatam valorile Date/Time luand in calcul si limba sau tara pentru care se face afisarea.

Acum sa trecem la treaba. Mai intai sa privim un tabel cu valori, s-ar putea sa ne foloseasca in cod:

Property Values
“weekday” “narrow”, “short”, “long”
“year” “2-digit”, “numeric”
“month” “2-digit”, “numeric”, “narrow”, “short”, “long”
“day” “2-digit”, “numeric”
“hour” “2-digit”, “numeric”
“minute” “2-digit”, “numeric”
“second” “2-digit”, “numeric”
“timeZoneName” “short”, “long”

Vom crea o data in format UTC si o vom afisa diferit in functie de tara:


const d25 = new Date( Date.UTC(2012, 9, 24, 8, 30) );  // '2012-10-24T08:30:00.000Z'

const d25en = new Intl.DateTimeFormat('en-US');
const d25ro = new Intl.DateTimeFormat('ro-RO');
const d25ja = new Intl.DateTimeFormat('ja-JP');

console.log(d25en.format(d25));     // 10/24/2012
console.log(d25ro.format(d25));     // 24.10.2012
console.log(d25ja.format(d25));     // 2012/10/24

Sa observam similitudinile cu functiile toLocale de pe obiectul Date:


const d26 = new Date( Date.UTC(2012, 9, 24, 8, 30) );  // '2012-10-24T08:30:00.000Z'

console.log( d26.toLocaleDateString('en-US') );     // 10/24/2012
console.log( d26.toLocaleDateString('ro-RO') );     // 24.10.2012
console.log( d26.toLocaleDateString('ja-JP') );     // 2012/10/24

console.log( d26.toLocaleString('en-US') );     // 10/24/2012, 11:30:00 AM
console.log( d26.toLocaleString('ro-RO') );     // 24.10.2012, 11:30:00
console.log( d26.toLocaleString('ja-JP') );     // 2012/10/24 11:30:00

console.log( d26.toLocaleTimeString('en-US') );     // 11:30:00 AM
console.log( d26.toLocaleTimeString('ro-RO') );     // 11:30:00
console.log( d26.toLocaleTimeString('ja-JP') );     // 11:30:00

Nu comparati rezultatele formatarilor cu string-uri statice pentru ca pot exista diferente de implementare intre browsere; pe unele poate functiona iar pe altele nu.

De exemplu:


"1/1/2020, 01:00:00" === new Date("2020-01-01T01:00:00Z").toLocaleString("en-US");

// true in Firefox 
// false in IE/Edge/Chrome

Acum sa revenim la DateTimeFormat. Daca avem o multime de formatari de facut, este recomandat sa construim un obiect de tipul Intl.DateTimeFormat si sa-i utilizam metoda format(). Este mai performant.

Atat functiile toLocale de pe obiectul Date cat si constructorul DateTimeFormat pot primi ca al doilea parametru un obiect cu optiuni de formatare:


const d27 = new Date( Date.UTC(2012, 9, 24, 8, 30) );  // '2012-10-24T08:30:00.000Z'

// vezi tabelul de mai sus
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };

const d27en = new Intl.DateTimeFormat('en-US', options);
const d27ro = new Intl.DateTimeFormat('ro-RO', options);
const d27ja = new Intl.DateTimeFormat('ja-JP', options);

console.log(d27en.format(d27));     // Wednesday, October 24, 2012
console.log(d27ro.format(d27));     // miercuri, 24 octombrie 2012
console.log(d27ja.format(d27));     // 2012年10月24日水曜日

Alte exemple:


const d28 = new Date( Date.UTC(2012, 9, 24, 2, 30) );  // '2012-10-24T02:30:00.000Z'

options = {
  year: 'numeric', month: 'numeric', day: 'numeric',
  hour: 'numeric', minute: 'numeric', second: 'numeric',
  hour12: false,
  timeZone: 'America/Los_Angeles' 
};

const d28en_default = new Intl.DateTimeFormat('en-US');
const d28en_custom = new Intl.DateTimeFormat('en-US', options);

console.log(d28en_default.format(d28));    // 10/24/2012 - UTC ora 2:30 (chiar daca nu e afisat timpul)
console.log(d28en_custom.format(d28));     // 10/23/2012, 19:30:00 - aici avem timeZone deci GMT-7

Mai multe informatii despre obiectul Intl.DateTimeFormat aici

Pentru functiile toLocale de pe obiectul Data urmati link-urile: toLocaleString , toLocaleDateString si toLocaleTimeString

Daca prin documentatie intalniti functia d.toLocaleFormat('%A, %B %e, %Y') nu incercati sa o folositi. Face parte din trecutul indepartat al JavaScript. Este chiar inspirata din functia strftime a limbajului C.

comments powered by Disqus