Extindeți TMS WEB Core cu JS Libraries cu Andrew: Luxon

TMS Software Componente Delphi
Ultimele postări au acoperit FlatPickr , o bibliotecă JS populară și foarte capabilă de datepicker. Ne-am uitat la multe dintre opțiunile sale și am analizat două moduri complet diferite de a-l adăuga la proiectele noastre TMS WEB Core. În ceea ce nu ne-am aprofundat prea mult este întreaga zonă a formatelor de dată și oră, fusuri orare, internaționalizare și diferențele dintre gestionarea datei în Delphi și JavaScript. Ei bine, am făcut puțin, dar suficient pentru a trece prin câteva exemple. De data aceasta, vom intra în mai multe detalii și vom introduce, de asemenea, cea mai recentă bibliotecă JS în repertoriul nostru – Luxon – care se descrie ca „un înveliș puternic, modern și prietenos pentru datele și orele JavaScript”. Nu sunt deloc sigur că cumpăr bitul „prietenos”, dar este într-adevăr puternic și modern, așa că să vedem unde se potrivește.

Motivația.

Dezvoltatorii Delphi de lungă durată sunt probabil să fie destul de familiarizați cu TDateTime și cu diferitele sale avantaje și deficiențe. Și chiar dacă ești relativ nou în Delphi și nu ai avut prea multă oportunitate de a interacționa cu TDateTime, nu există prea multe lucruri care să te împiedice și, în general, nu este probabil să atragă prea multă atenție de la sine . Acesta este un lucru bun, desigur. Când aveți de-a face cu JavaScript, totuși, frumosul și confortabilul TDateTime este înlocuit cu un format de dată JavaScript destul de sinistru, care nu este niciunul dintre aceste lucruri. Sigur, este destul de ușor să începeți cu o dată JavaScript, dar este într-adevăr o fiară complet diferită. Deplasarea între cele două poate fi o provocare și chiar și doar încercarea de a face cea mai mică parte de formatare poate fi uneori mult mai multe probleme decât ar putea părea posibil. Deci, în această postare, vom trece peste o grămadă de astfel de lucruri. Scoateți totul dintr-o singură lovitură, smulgeți aparatul, ca să spunem așa, ca să putem trece cu sinceritate și încredere la alte subiecte, dar să avem asta în buzunarul din spate atunci când avem nevoie. Și crede-mă, vom avea nevoie de el!

Epocă epică.

Să ne scufundăm mai întâi degetul în capătul superficial și să trecem rapid peste ce este TDateTime. Delphi folosește clasa TDateTime pentru a codifica, în mod natural, o dată și o oră. Face acest lucru într-un mod foarte simplu – prin utilizarea unui număr în virgulă mobilă (în special, un dublu), în care întreaga parte a numărului reprezintă numărul de zile de la 1899-12-30, iar partea fracțională a numărului reprezintă timpul ca o fracțiune de zi. Amiaza ar fi reprezentată ca .5, 18:00 ar fi reprezentată ca .75 și așa mai departe. Alegerea lui 1899-12-30 este oarecum arbitrară, dar am dat peste acest link care a descris-o astfel:
Se pare că motivul pentru Delphi începând cu 30 decembrie 1899 este acela de a-l face cât mai compatibil cu Excel și, în același timp, nu adoptă incorectitudinea Excel cu privire la date. Din punct de vedere istoric, Excel a jucat al doilea lăutar în fața lui Lotus 1-2-3. Lotus (care poate să fi primit această eroare de la Visicalc) a considerat în mod incorect 1900 ca fiind un an bisect, prin urmare, o valoare de 60 vă oferă 29 februarie 1900 în Excel, dar este interpretat ca 28 februarie 1900 în Delphi din cauza Delphi care a început cu o zi înainte. De la 01 martie 1901, cele două sisteme de date dau același rezultat pentru un număr dat.

Grozav, un alt motiv pentru a detesta Excel. De parcă nu erau deja destui! Detaliile reale din jurul acestui lucru nu vor conta atât de mult, cu excepția cazului în care, desigur, se întâmplă să lucrați cu o mulțime de date în acea perioadă (sau mai devreme), unde aceasta ar putea fi o problemă. Și nici pentru cealaltă direcție nu trebuie să ne îngrijorăm prea mult. Ca o dublă, data maximă care poate fi reprezentată cu TDateTime este cel puțin 9999-12-31. De fapt, poate reprezenta date după aceea, dar lucrurile devin un pic zgomotoase când anul este mai mare de patru cifre.

Unix (și Linux) stochează aceste informații ca numere întregi, numărul de secunde de la 1970-01-01 00:00:00. Folosind numere întregi semnate pe 32 de biți, aceasta înseamnă că valoarea maximă care poate fi stocată se va depăși în 2038. În mod curios, data minimă este în 1901. Din fericire, se pare că sistemele pe 64 de biți le-au actualizat cu înțelepciune pentru a folosi numere întregi pe 64 de biți, deci asta nu ar trebui să fie o problemă. Sunt sigur că până în 2038 veți fi greu să găsiți un prăjitor de pâine fără un procesor pe 64 de biți. În JavaScript, se adoptă o abordare similară, dar este numărul de milisecunde din 1970-01-01 00:00:00 și cred că a folosit întotdeauna numere întregi pe 64 de biți (sau, mai degrabă, ceva aproximativ echivalent), așa că nimic să-ți faci griji acolo. De remarcat, totuși, este faptul că JavaScript presupune ca valoarea să fie în mod specific numărul de milisecunde începând cu 1970-01-01 00:00:00 UTC. Ultimele trei litere vor deveni importante mai târziu.

Utilizarea de bază a Delphi.

Utilizarea TDateTime în Delphi nu este deosebit de dificilă și probabil că este ceva cu care ești deja familiarizat, așa că nu vom petrece prea mult timp cu asta. Din fericire, toate acestea funcționează bine și în TMS WEB Core. Iată o grămadă de exemple ale modurilor în care folosesc cel mai des TDateTime. Acestea sunt tipurile de lucruri pe care vom dori să le putem face și în JavaScript. Și în timp ce unele dintre acestea ar putea părea o grămadă mare de dureri de cap, în practică, acesta nu este cazul. Majoritatea datepicker-urilor de timp sunt folosite pentru a alege datele reale dintr-un calendar sau sunt folosite alte elemente ale interfeței de utilizare, pentru a face ca o mare parte din aceasta să nu fie o problemă. Și, de obicei, nu le-ați folosi pe toate acestea în același timp!

 ...
utilizări
...
System.DateUtils,
...
procedura TForm1.WebButton2Click(Expeditor: TObject);

procedura TestTDateTime(aDateTime: TDateTime);
ÎNCEPE
console.log('TDateTime Value: '+FloatToStrF(ADateTime,ffNumber,5,3));
console.log('TDateTime = 0.0: '+DateTimeToStr(0.0));

console.log('Format DateTime preferat: ' +FormatDateTime('aaaa-mm-zz hh:nn:ss', aDateTime));
console.log('Al doilea format DateTime preferat: ' +FormatDateTime('aaaa-mmm-zz hh:nn:ss', aDateTime));
console.log('Al treilea format DateTime preferat: ' +FormatDateTime('aaaa-mmm-zz (zz) hh:nn:ss', aDateTime));

console.log('Short DateTime Format: ' +FormatDateTime('ddddd t', aDateTime));
console.log('Long DateTime Format: '+FormatDateTime('ddddd tt', aDateTime));

console.log('Short Date Format Settings: ' +FormatSettings.ShortDateFormat);
console.log('Setări de format de dată lungă: ' +FormatSettings.LongDateFormat);
console.log('Separator de date: ' +FormatSettings.DateSeparator);
console.log('Short Time Format Settings: ' +FormatSettings.ShortTimeFormat);
console.log('Setări de format pe termen lung: ' +FormatSettings.LongTimeFormat);
console.log('Separator de timp: ' +FormatSettings.TimeSeparator);

console.log('Prima zi a lunii: ' +FormatDateTime('aaaa-mmm-zz', StartOfTheMonth(ADateTime)));
console.log('Ultima zi a lunii: ' +FormatDateTime('aaaa-mmm-zz', EndOfTheMonth(ADateTime)));
console.log('Prima zi a lunii anterioare: ' +FormatDateTime('aaaa-mmm-zz', StartOfTheMonth(IncMonth(ADateTime,-1))));
console.log('Ultima zi a lunii anterioare: ' +FormatDateTime('aaaa-mmm-zz', EndOfTheMonth(IncMonth(ADateTime,-1))));
console.log('Prima zi a lunii următoare: ' +FormatDateTime('aaaa-mmm-zz', StartOfTheMonth(IncMonth(ADateTime,+1))));
console.log('Ultima zi a lunii următoare: ' +FormatDateTime('aaaa-mmm-zz', EndOfTheMonth(IncMonth(ADateTime,+1))));

console.log('Ziua anului: ' +IntToStr(DayOfTheYear(aDateTime)));
console.log('Ziua săptămânii: ' +FormatDateTime('dddd', ADateTime));

// Săptămâna începe luni
console.log('Numărul ISO al zilei săptămânii: ' +IntToStr(DayoftheWeek(ADateTime)));
console.log('Numărul săptămânii ISO: ' +IntToStr(WeekOfTheYear(aDateTime)));
console.log('ISO Prima zi a săptămânii: ' +FormatDateTime('aaaa-mmm-zz (zz)', StartOfTheWeek(aDateTime)));
console.log('ISO Ultima zi a săptămânii: ' +FormatDateTime('aaaa-mmm-zz (ddd)', EndOfTheWeek(aDateTime)));
console.log('ISO Prima zi a săptămânii precedente: ' +FormatDateTime('aaaa-mmm-zz (zz)', StartOfTheWeek(aDateTime-7)));
console.log('Ultima zi ISO a săptămânii anterioare: ' +FormatDateTime('aaaa-mmm-zz (zz)', EndOfTheWeek(aDateTime-7)));
console.log('ISO Prima zi a săptămânii viitoare: ' +FormatDateTime('aaaa-mmm-zz (zz)', StartOfTheWeek(aDateTime+7)));
console.log('ISO Ultima zi a săptămânii viitoare: ' +FormatDateTime('aaaa-mmm-zz (zz)', EndOfTheWeek(aDateTime+7)));

// Săptămâna începe duminică
console.log('Numărul zilei săptămânii non-ISO: ' +IntToStr(DayofWeek(ADateTime)));
// Dacă duminica, folosiți luni în schimb pentru că WeekOfTheYear nu știe despre săptămânile de duminică
console.log('Numărul săptămânii non-ISO: ' +IntToStr(WeekOfTheYear(aDateTime+Trunc(DayOftheWeek(ADateTime)/7))));
console.log('Prima zi a săptămânii non-ISO: ' +FormatDateTime('aaaa-mmm-zz (zz)',aDateTime + 1 - DayOfWeek(aDateTime)));
console.log('non-ISO Ultima zi a săptămânii: ' +FormatDateTime('aaaa-mmm-zz (zz)',aDateTime + 7 - DayOfWeek(aDateTime)));
console.log('non-ISO Prima zi a săptămânii precedente: ' +FormatDateTime('aaaa-mmm-zz (zzj)',aDateTime - 6 - DayOfWeek(aDateTime)));
console.log('Ultima zi non-ISO a săptămânii precedente: ' +FormatDateTime('aaaa-mmm-zz (zz)',aDateTime - DayOfWeek(aDateTime)));
console.log('non-ISO Prima zi a săptămânii viitoare: ' +FormatDateTime('aaaa-mmm-zz (zzj)',aDateTime + 8 - DayOfWeek(aDateTime)));
console.log('non-ISO Ultima zi a săptămânii viitoare: ' +FormatDateTime('aaaa-mmm-zz (zzj)',aDateTime +14 - DayOfWeek(aDateTime)));

console.log('Se calculează durata unei zile: ' +IntToStr(DaysBetween(Today,EncodeDate(1971,05,25))));
console.log('Calculul unei durate de timp: ' +IntToStr(MillisecondsBetween(Now,Now-1))+'ms');
Sfârşit;

ÎNCEPE
TestTDateTime(EncodeDateTime(2021, 12, 26, 12, 13, 14, 0)); // O duminică
TestTDateTime(EncodeDateTime(2022, 1, 2, 12, 13, 14, 0)); // Tot o duminică
TestTDateTime(EncodeDateTime(2022, 1, 9, 12, 13, 14, 0)); // Da, încă o duminică
Sfârşit;

Adunarea rezultatelor celor trei date și punerea lor într-un tabel ne aduce următoarele. Facem aici o distincție între săptămânile care încep luni (standardul ISO) și săptămânile care încep duminică (de obicei, Canada și SUA, dar și altele). Vom aborda acest lucru în doar un moment, dar principalul lucru de reținut este că acest lucru se schimbă acolo unde cade duminica în ceea ce privește numărul real al săptămânii. Dacă nu folosiți numere de săptămână, aceasta nu este o problemă. Dacă o faci, aceasta este, ei bine, aceasta nu este deloc o problemă.

26-dec-2021 (duminică)

02-ian-2022 (duminică)

09-ian-2022 (duminică)

Valoarea TDateTime

44.556,509

44.563,509

44.570,509

TDateTime = 0,0

1899-12-30

1899-12-30

1899-12-30

Formatul DateTime preferat

26-12-2021 12:13:14

2022-01-02 12:13:14

2022-01-09 12:13:14

Al doilea format de date și oră preferat

26-Dec-2021 12:13:14

2022-ian-02 12:13:14

09-ian-2022 12:13:14

Al treilea format preferat de date și oră

26-dec-2021 (duminică) 12:13:14

02-ian-2022 (duminică) 12:13:14

09-ian-2022 (duminică) 12:13:14

Format scurt DateTime

26-12-2021 12:13

2022-01-02 12:13

2022-01-09 12:13

Long DateTime Format

26-12-2021 12:13:14

2022-01-02 12:13:14

2022-01-09 12:13:14

Setări de format de dată scurtă

aaaa-mm-zz

aaaa-mm-zz

aaaa-mm-zz

Setări de format de dată lungă

zz, aaaa-mm-zz

zz, aaaa-mm-zz

zz, aaaa-mm-zz

Setări separator de date

Setări de format de scurtă durată

hh:nn

hh:nn

hh:nn

Setări de format de lungă durată

hh:nn:ss

hh:nn:ss

hh:nn:ss

Setări Separator de timp

:

:

:

Prima zi a lunii

2021-dec-01

01-ian-2022

01-ian-2022

Ultima zi a lunii

2021-dec-31

31-ian-2022

31-ian-2022

Prima zi a lunii anterioare

2021-Nov-01

2021-dec-01

2021-dec-01

Ultima zi a lunii anterioare

2021-Nov-30

2021-dec-31

2021-dec-31

Prima zi a lunii viitoare

01-ian-2022

01-feb-2022

01-feb-2022

Ultima zi a lunii viitoare

31-ian-2022

28-feb-2022

28-feb-2022

Ziua Anului

360

2

9

Zi a săptămânii

duminică

duminică

duminică

Numărul ISO al zilei săptămânii

7

7

7

Numărul săptămânii ISO

51

52

1

ISO prima zi a săptămânii

20-dec-2021 (luni)

27-dec-2021 (luni)

03-ian-2022 (luni)

ISO Ultima zi a săptămânii

26-dec-2021 (duminică)

02-ian-2022 (duminică)

09-ian-2022 (duminică)

ISO prima zi a săptămânii precedente

13-dec-2021 (luni)

20-dec-2021 (luni)

27-dec-2021 (luni)

ISO Ultima zi a săptămânii precedente

19-dec-2021 (duminică)

26-dec-2021 (duminică)

02-ian-2022 (duminică)

ISO prima zi a săptămânii viitoare

27-dec-2021 (luni)

03-ian-2022 (luni)

10-ian-2022 (luni)

ISO Ultima zi a săptămânii viitoare

02-ian-2022 (duminică)

09-ian-2022 (duminică)

16-ian-2022 (duminică)

Numărul zilei săptămânii non-ISO

1

1

1

Numărul săptămânii non-ISO

52

1

2

prima zi a acestei săptămâni non-ISO

26-dec-2021 (duminică)

02-ian-2022 (duminică)

09-ian-2022 (duminică)

non-ISO Ultima zi a acestei săptămâni

01-ian-2022 (sâmbătă)

08-ian-2022 (sâmbătă)

15-ian-2022 (sâmbătă)

non-ISO prima zi a săptămânii precedente

19-dec-2021 (duminică)

26-dec-2021 (duminică)

02-ian-2022 (duminică)

non-ISO Ultima zi a săptămânii precedente

01-ian-2022 (sâmbătă)

01-ian-2022 (sâmbătă)

08-ian-2022 (sâmbătă)

non-ISO prima zi a săptămânii viitoare

02-ian-2022 (duminică)

09-ian-2022 (duminică)

16-ian-2022 (duminică)

non-ISO Ultima zi a săptămânii viitoare

08-ian-2022 (sâmbătă)

15-ian-2022 (sâmbătă)

22-ian-2022 (sâmbătă)

Calcularea duratei unei zile

18626

18626

18626

non-ISO Ultima zi a săptămânii viitoare

86400000ms

86400000ms

86400000 ms

Există, desigur, mult mai multe funcții disponibile, iar preferatele tale pot fi foarte bine diferite de ale mele, desigur. Dar chiar și din această listă de bază de apeluri de funcții, apar destul de multe subiecte de interes pe care probabil ar trebui să le acoperim înainte de a continua. Dar o declinare generală aici. Există diverse convenții utilizate în întreaga lume când vine vorba de utilizarea și afișarea datelor și orelor. Ceea ce este obișnuit acolo unde locuiesc, poate foarte bine să fie diferit în altă parte a lumii. Și oamenii au opinii, uneori păreri foarte puternice, inclusiv eu. Și nu mă pot gândi la nimic din partea de sus a minții despre care doi dezvoltatori din diferite regiuni ale lumii ar putea să nu fie de acord mai puternic decât ceva legat de formatele de date. Principalul punct general pe care încerc să îl subliniez aici este că există opțiuni și, în calitate de dezvoltatori TMS WEB Core (sau orice dezvoltatori, într-adevăr), avem o mulțime de instrumente la dispoziție pentru a face din acest tip de lucru o plăcere pentru utilizatorii noștri. sau clienți, indiferent care ar fi convențiile lor preferate, mai degrabă decât o pedeapsă. Deci iată!

  1. Indiferent de platforma de dezvoltare sau limbajul de programare, se consideră, în general, o bună practică să folosești întotdeauna funcțiile disponibile oferite de mediul tău atunci când ai de-a face cu valorile datetime. În parte, acest lucru este pentru a vă salva de durerile de cap care vin odată cu nevoia de a vă gândi la anii bisecți sau la câte zile sunt într-o lună. Acest lucru funcționează și cu presupunerea că system_function(a_valid_date) -> another_valid_date. Numeroase erori se pot strecura atunci când începi să te joci cu lucruri. Totuși, acest lucru nu este întotdeauna ușor și cu cât te îndepărtezi de „convenție”, cu atât este mai probabil să întâmpinați probleme. Chiar și în exemplele mele de mai sus, există mai multe lăutari decât mi-aș dori cu acele calcule pentru prima/ultima zi a săptămânii. Din fericire, încă nu am întâlnit pe nimeni care să fi argumentat împotriva săptămânilor care au șapte zile, așa că este destul de sigur. Unul dintre puținele cazuri în care nu există numeroase opinii.
  2. Apropo de asta, ISO8601 pare la început a fi un standard strict în multe privințe, dar la o inspecție mai atentă, nu este deloc atât de strict. Cu excepția, în mod curios, pentru începutul săptămânii, despre care afirmă fără echivoc că este luni. Cu toate acestea, în partea mea de lume (Canada), sincer nu-mi amintesc să fi văzut vreodată un calendar tipărit care nu avea duminica în stânga și sâmbăta în dreapta. Ceea ce înseamnă, indirect, că săptămâna începe duminică. Și oriunde am lucrat și toți cei cu care am lucrat, probabil că ar avea aceeași părere. Cu toate acestea, într-o altă locație, poate într-o altă țară sau continent, se poate întâmpla exact opusul. Și deși nu am întâlnit niciodată această situație personal, am citit că în unele locuri prima zi a săptămânii poate fi sâmbătă. Mai precis, unele grupuri etnice (indiferent de locația fizică) pot prefera acest lucru. Asa de…. ??! Aceasta poate fi o problemă reală. Delphi în sine are chiar diferite variații ale funcțiilor sale. Un set de variații oferă posibilitatea de a trece valorile TDateTime în loc de părțile constitutive (funcțiile “the”). O altă variație se referă la faptul că duminica = 1 sau luni = 1. Tricky, tricky. Indiferent de situația cu care te confrunți, trebuie să fii atent și să fii sigur că folosești aceleași funcții (și convenții) în mod consecvent, chiar dacă doar pentru propria ta minte.
  3. În mod similar, numerele săptămânii pot fi uneori un motiv de confuzie. Cu un număr mare de ani în urmă, îmi amintesc că am vizitat un birou la începutul lunii ianuarie, când angajații verificau cele mai recente produse agricole de la doi vânzători separati de agricultură de peste miliarde de dolari, ambii din aceeași țară din nordul Europei care se mândrește mai degrabă cu priceperea sa agricolă. . Swag-ul în acest caz includea calendare de perete foarte mari, care arată întregul an dintr-o privire. Cu părți egale de groază și umor, s-a dovedit că calendarele de la diferiți vânzători nu aveau aceleași numere de săptămână alocate. Cred că, colectiv, ca planetă, am ajuns de atunci să fim de acord că Săptămâna #1 este considerată prima săptămână care are o zi de joi. ISO8601 prevalează. Deși aceasta este mai mult o realizare „recentă” la care au ajuns oamenii și nu este neapărat implementată în același mod peste tot. Totuși, cu asta mergem aici. În mod convenabil, acest lucru nu este într-adevăr în conflict cu punctul meu (2) de mai sus. Va trebui doar să ne certăm despre săptămâna în care se încadrează duminica.
  4. TDateTime are precizie în milisecunde, dacă sunteți interesat de acel nivel de detaliu. Cu toate acestea, unele sisteme (baze de date, de exemplu) pot avea mai multă precizie. Marcajele de timp DB2 înregistrează microsecunde (șase cifre în loc de trei), la fel ca și altele. Deci, acesta este ceva de care trebuie să fiți atenți când mutați datele, mai ales dacă comparați datele în căutarea potrivirilor exacte. Și mai ales când baza de date în sine generează marcaje temporale, pe care le vom trata mai detaliat în curând. Uneori, acest lucru se întâmplă și în culise. Acesta este motivul pentru care, dacă utilizați componente convenționale Delphi DB pentru a accesa o bază de date, marcajele de timp sunt trunchiate după secunde. Pentru a vă asigura că componentele și baza de date folosesc întotdeauna aceeași precizie.
  5. Un exemplu de ISO8601 care poate fi mai puțin rigid este că datele pot fi exprimate fie ca AAAA-LL-ZZ sau AAAAMMZZ – ceea ce se referă la îmbunătățit vs. de bază. Nimic ca un standard bun pentru a nu lua parte! Așadar, atunci când se pretinde că ceva este compatibil cu ISO8601, trebuie să nu ai prea multă încredere doar în asta, deoarece poate însemna lucruri diferite pentru diferiți oameni. Și da, mă uit la tine, TXDataWebDataset.SetJSONData. Sau FireDAC, la fel de vinovat, presupun! Ca și în orice altceva, astfel de lucruri trebuie testate și trebuie să fii atent nu numai la variațiile de formate, ci și la faptul că atunci când lucrurile funcționează, nu cauzează alte probleme în aval. O parte a unei serii de apeluri de funcții poate tolera cu bucurie mici variații într-un format, în timp ce alta va fi oprită la rece.
  6. Utilizatorii au preferințe. Clienții au preferințe. Dezvoltatorii au preferințe. Rareori acestea sunt aliniate. Și, uneori, factorul decisiv nu este legat de standarde sau codare sau de oricare dintre aceste preferințe, ci mai degrabă de temutele „reguli de afaceri” când cineva a decis, probabil cu decenii în urmă, că „Asta este calea” pentru acea organizație. Din acest motiv, am luat obiceiul de a oferi o opțiune de configurare generală în proiectele mele, în care diferite perioade (săptămâni, luni, ani fiscali, trimestre, sezoane, perioade de salarizare, vacanțe statistice etc.) pot fi arbitrar. definit și apoi implementat în mod consecvent în cadrul unei organizații, cel puțin în limitele proiectelor mele. Cel puțin atunci când o încurcă (ceea ce și ei fac în mod constant , aproape în fiecare an!), este încurcată în mod constant pentru toată lumea. Nu sunteți sigur dacă acesta a fost un beneficiu net sau nu? Ideea este, totuși, că, cu puțin (OK, MULT) noroc, utilizatorii tăi sau clienții tăi vor fi în concordanță intern cu acest gen de lucruri, cel puțin în cadrul propriei organizații.
  7. Apoi există conceptul de „internaționalizare”. Să-i spunem așa deocamdată, oricum. Uneori se numește „localizare”. Deloc confuz. Ideea că, oriunde te-ai afla în lume, regiunea ta (de obicei țara ta) are câteva idei general agreate despre care ar trebui să fie formatul datei, care ar trebui să fie prima zi a săptămânii, cum se numesc zilele de săptămâna și lunile anului sunt (cu variații pentru limbile oficiale din regiune), nume mai scurte echivalente pentru zilele săptămânii și lunile anului, cum ar trebui exprimate datele „medie” și „lungi” și așa mai departe . Adesea, veți vedea setări software în aplicații în care puteți selecta o altă regiune, dacă se întâmplă să găsiți că setările implicite preferate ale propriei regiuni nu sunt pe placul dvs. Bănuiesc că Danemarca este probabil o selecție regională mai populară decât ar fi altfel, tocmai din acest motiv. O școală de gândire (foarte bună!) este că, în calitate de dezvoltator, ar trebui să utilizați acest sistem exclusiv, oferind valori implicite bune tuturor utilizatorilor la nivel global, cu opțiunea ca aceștia să folosească setări diferite dacă doresc. Nobil chiar, poate. Dar poate deplasat.
  8. Și, în sfârșit, complet contrar cu (7) de mai sus, este că utilizatorii dintr-o anumită regiune ar putea să nu dorească să folosească setările implicite pentru regiunea lor sau pentru orice regiune. Chiar dacă se întâmplă să trăiască în Danemarca! Și acest sistem cu siguranță nu este lipsit de defecte proprii. De exemplu, folosesc în mod regulat un sistem desktop Fedora cu FireFox, Thunderbird (BetterBird, de fapt) și Google Chrome. Se pare că toți (un fel de) se bazează pe locația Fedora pentru a-și face treaba. Fedora însăși face o treabă foarte slabă în ceea ce privește oferirea de orice fel de flexibilitate. Și astfel totul în aval de acolo se înrăutățește progresiv. Chiar și Chrome și Firefox s-ar putea să nu fie de acord cu anumite opțiuni care utilizează același sistem, ceea ce poate fi enervant uneori.

Deci, pentru a fi clar, există standarde precum ISO8601 care oferă îndrumări solide cu privire la multe lucruri, dar există și excepții de rutină de care trebuie să fii conștient și adesea reguli care intră în joc care nu au nicio bază în realitate. Vestea bună este că instrumentele aflate la îndemână sunt capabile să se ocupe cu ușurință de aproape orice, atâta timp cât cineva poate găsi o definiție clară. Și ține-te de el. Nu atât de comun pe cât ai putea crede!

Ca un ultim exemplu al acestei prostii, Biblioteca FlatPickr JS pe care tocmai am acoperit-o nu enumeră în opțiunile sale nimic de-a face cu când începe săptămâna. Probabil preia setările din informațiile de sistem local. Sau cel puțin asta sper să facă. Totuși, ce se întâmplă dacă în stânga afișează calendarul începând cu duminica, dar chiar ai prefera să înceapă întotdeauna cu luni? Sau poate că se întâmplă invers? Se pare că există o modalitate de a face asta, cu doar puțin mai mult lăutari. Soluția poate fi găsită în documentația FlatPickr, sub Localizare . Alte funcționalități asociate pot fi găsite acolo.
 procedura TForm1.WebFormCreate(Expeditor: TObject);
ÎNCEPE
asm

var fp1 = flatpickr('#WebEdit1', {
appendTo: WebHTMLDiv1,
inline: adevărat,
WeekNumbers: adevărat,
local: {
firstDayOfWeek:0, // Duminică
}
});

var fp2 = flatpickr('#WebEdit2', {
appendTo: WebHTMLDiv2,
inline: adevărat,
WeekNumbers: adevărat,
local: {
firstDayOfWeek:1, // luni
}
});

Sfârşit;
Sfârşit; 
TMS Software Componente Delphi
Voi actualiza pachetul JSExtend în scurt timp pentru a avea acest lucru ca opțiune și în IDE. Dar iată o reprezentare vizuală mai bună a motivului pentru care am ales cele trei întâlniri pe care le-am făcut pentru exemplu. Ne dorim într-adevăr ca toate acestea să fie fără echivoc și fără ambiguități, dar uneori poate fi o luptă. Și nu am terminat cu internaționalizarea/localizarea, dar o vom lăsa deoparte pentru moment și vom trata o altă problemă problematică.

Fusuri orare, UTC, Ora locală și Compensații.

Acest subiect este puțin mai simplu și în același timp puțin mai complex. Partea mai simplă, crezi sau nu, este că toată lumea este de acord (mai mult sau mai puțin, ha!) cu privire la care sunt fusurile orare și unde sunt limitele fizice, când au loc schimbările de timp și așa mai departe. Și chiar dacă limitele sunt uneori vagi, utilizatorul probabil știe în ce fus orar se află și sistemul său (browser, sistem de operare etc.) are probabil ceva în el care ne va ajuta să identificăm fusul orar fără niciun fel de ambiguitate. În general, vă setați fusul orar (sau, mai probabil, doar confirmați fusul orar) atunci când vă configurați pentru prima dată computerul, telefonul sau browserul sau orice altceva utilizați, și asta este tot – un lucru odată la care nu trebuie să vă gândiți cu adevărat despre mai mult, din fericire. De obicei, telefoanele nici nu trebuie să ceară, deoarece pot obține asta de la operator.

A trecut mult timp! Dar cred că suntem acolo acum. Cu toate acestea, datele de fus orar se schimbă, așa că acesta este unul dintre acele lucruri care apar și necesită actualizări periodice ale software-ului. Ideea că poți să creezi o aplicație și să pleci și să nu o mai actualizezi niciodată este mai mult decât puțin demodată. Modificările fusului orar pot necesita actualizări ale sistemului de operare de bază, sau poate bibliotecile pe care le utilizați în proiectul dvs., browserul sau chiar propriul cod (să sperăm că nu!), sau chiar toate cele de mai sus. În scopurile noastre, vom presupune că folosim cea mai recentă versiune a tuturor lucrurilor și că putem da vina pe furnizorul browserului dacă apare vreodată o problemă. Glumeam. Mai ales.

Partea mai complexă este că Delphi presupune că nu știm nimic despre fusurile orare. Și JavaScript presupune că știm totul despre fusurile orare. Așa că există un mic decalaj de informații de depășit atunci când vă deplasați înainte și înapoi între cele două. Ei bine, TDateTime presupune că nu știm nimic despre fusurile orare. Dar care sunt elementele despre care trebuie să știm cu adevărat? Poate că cea mai critică informație este decalajul nostru local față de UTC. Dacă știm asta, putem face imediat câteva lucruri esențiale, cum ar fi convertirea înainte și înapoi între ora locală și UTC. În mod curios, sursa TMS WEB Core folosește acest lucru pentru a determina decalajul orar local față de UTC:

 Funcția GetLocalTimeOffset : Integer;
ÎNCEPE
Rezultat:=TJSDate.New.getTimezoneOffset();
Sfârşit;

Ceea ce înseamnă în esență că primește compensarea de la JavaScript. Acest lucru are sens, deoarece doar browserul știe ce se întâmplă într-o aplicație TMS WEB Core care rulează, așa că acesta este singurul loc pentru a o obține. Browserul însuși îl va obține din orice mediu în care rulează, așa că ar trebui să funcționeze bine. Delphi VCL este puțin mai cuprinzător, cu suport pentru lucruri precum TTimeZone, care nu funcționează (încă) în TMS WEB Core, dar deoarece nu ne putem baza pe Windows (am putea vreodată?), trebuie să obținem aceste informații de la noua sursă definitivă – browserul. De fapt, putem obține puțin mai multe informații de la browser, dar lucrurile încep să devină puțin șocante în ceea ce privește dacă un browser acceptă aceste apeluri, dacă diferite platforme acceptă aceste apeluri, dacă acel telefon din 2014 acceptă aceste apeluri și așa mai departe . Este posibil să nu funcționeze întotdeauna. Dar merită încercat. Iată ce avem. Nu este un început rău.

 procedura TForm1.WebButton1Click(Expeditor: TObject);
var
tz_name: șir;
tz_short: șir;
ÎNCEPE

asm
tz_name = Intl.DateTimeFormat().resolvedOptions().timeZone;
tz_short = new Date().toLocaleString('ro', {timeZoneName:'short'}).split(' ').pop();
Sfârşit;

console.log('Ora locală: '+FormatDateTime('aaaa-mmm-zz (zz) hh:nn:ss', Acum));
console.log('Nume fus orar local: '+tz_name);
console.log('Scurt fus orar local: '+tz_short);
console.log('UTC Offset: '+IntToStr(TJSDate.new.gettimezoneoffset())+' minute');
console.log('UTC: '+FormatDateTime('aaaa-mmm-zz (zz) hh:nn:ss', LocalTimeToUniversal(Acum)));
console.log('UTC la Ora locală: '+FormatDateTime('aaaa-mmm-zz (zz) hh:nn:ss', UniversalTimeToLocal(EncodeDateTime(2022,5,22,6,0,0,0))) );
Sfârşit;
// ieșire console.log:
Ora locală: 21 mai 2022 (sâmbătă) 22:40:14
Nume fus orar local: America/Vancouver
Fus orar local scurt: PDT
UTC Offset: 420 minute
UTC: 22-mai-2022 (duminică) 05:40:14
UTC la ora locală: 21 mai 2022 (sâmbătă) 23:00:00

Din păcate, cam asta este tot ceea ce putem determina cu ușurință. Conversia între fusurile orare nu este chiar o opțiune până acum, de exemplu.

Bara laterală pe partea serverului.

Când lucrați cu partea client a unei aplicații de tip client/server, adesea clientului chiar nu îi pasă de fusurile orare. Nu este că nu sunt importante, ci mai degrabă că, de cele mai multe ori, oamenii fie lucrează în propriile fusuri orare, deci toate compensațiile sunt întotdeauna aceleași, fie dacă lucrează cu date din fusuri orare diferite, este tot ora locală. asta este important – nu este necesară nicio conversie.

De exemplu, am petrecut o cantitate considerabilă de timp lucrând la sisteme de timp și de prezență pentru operatorii comerciali de sere (în mare parte cultivatorii de tomate și ardei). Cu aceste sisteme, lucrătorii „perforează” la un computer chioșc client pe tot parcursul zilei lor de lucru, care apoi înregistrează datele și orele și activitățile într-o bază de date de server local (serverul înregistrează ora, nu are încredere în client). Supraveghetorii folosesc apoi aceste date pentru a monitoriza performanța lucrătorilor, fiind conștienți de faptul că timpul lor trebuie să se încadreze în orele de schimb alocate și să îndeplinească diferite criterii bazate pe durata activităților care sunt executate, tipul de fabrici la care lucrează și așa mai departe. . Lucrătorii dintr-o altă locație (în alt fus orar) care lucrează pentru aceeași companie ar putea foarte bine să lovească un alt computer chioșc client conectat la o altă bază de date locală. Administratorul de resurse umane (care lucrează într-un alt fus orar) ar putea reuni toate aceste date pentru a procesa statul de plată săptămânal al organizației. Persoanei respective nu îi pasă neapărat prea mult unde a fost făcută munca și nu are nevoie să convertească orele locale din locațiile îndepărtate într-un anumit fus orar. Administratorul vede doar că lucrătorii intră la, să zicem, 06:00 și ies la 14:00, astfel încât să fie plătiți pentru acea perioadă de timp (ajustat pentru pauze plătite și neplătite, bonusuri de performanță și așa mai departe). Nu este deloc nevoie să vă preocupați de fusurile orare.

Pe de altă parte, dacă aveți un server care înregistrează tot felul de activități într-o bază de date locală de la utilizatori din mai multe fusuri orare, situația ar putea fi destul de diferită. Un server XData poate, de exemplu, să înregistreze conexiuni și tichete de solicitare de servicii către o bază de date locală. Acea bază de date ar putea fi configurată pentru a înregistra marcaje de timp folosind UTC sau ar putea fi configurată să utilizeze ora locală a serverului bazei de date (care ar putea fi chiar diferită de fusul orar al serverului XData). Când un utilizator trimite un bilet de solicitare de serviciu utilizând un punct final al serverului XData, serverul bazei de date poate înregistra ora în propriul fus orar. Când utilizatorul dorește să verifice acel bilet, totuși, ora returnată utilizatorului trebuie convertită înapoi în fusul orar al acestuia. Din perspectiva lor, fusul orar este irelevant, dar pe server, orele înregistrate pentru toate biletele vor avea marcaje temporale înregistrate în fusul orar al serverului bazei de date care reflectă ordinea reală în care au fost create biletele, indiferent de fusul orar al persoanelor care au creat biletul.

Cum putem face acest lucru fără a fi nevoie să stocăm fusul orar al utilizatorului în baza de date? O abordare este de a face conversia numai atunci când utilizatorul face o cerere ulterioară. Când este invocată o operațiune de serviciu care necesită aceste informații, cum ar fi o solicitare de „informații bilet” sau un raport la nivel de utilizator de un fel, fusul orar al clientului poate fi trimis la XData, fie ca parametru de punct final, fie chiar inclus ca element. într-un JWT. Apoi, când se execută baza de date SQL, trecem aceste informații pentru a face conversia. Rezultatul interogării are apoi date în fusul orar corectat.

În DB2, o astfel de conversie ar putea arăta astfel. Fusul orar al serverului și fusul orar al utilizatorului sunt transmise ca parametri de interogare (de exemplu: America/Vancouver).

 Selectați
fus orar(TICKETTIMESTAMP, :SERVERTZ, :LOCALTZ) TICKETLOCALTIMESTAMP
din
BILETE
Unde
USER='franc'

În MySQL (sau MariaDB) poate fi folosită aceeași abordare, dar cu un apel de funcție diferit.

 Selectați
convert_tz(TICKETTIMESTAMP, :SERVERTZ, :LOCALTZ) TICKETLOCALTIMESTAMP
din
BILETE
Unde
USER='franc'

Rețineți că, în ambele baze de date, probabil că trebuie făcută unele lucrări de pregătire inițiale doar pentru a vă asigura că bazele de date au informații actuale despre fusul orar. Și, deși nu sunt sigur că fiecare bază de date are același nivel de suport pentru fusul orar, mi-aș imagina că dacă acestea două o fac, mulți alții o fac și ele.

Ce se întâmplă dacă trebuie să faci ceva similar pe partea de server, dar în afara unei baze de date? Să presupunem, de exemplu, că doriți să includeți marcajul de timp actual pe un raport generat pe server, dar prezentat utilizatorului în fusul orar local. O modalitate este să folosiți foarte impresionantul TZDB.pas , care conține întreaga (actuală!) Baza de date IANA de fus orar. Pur și simplu descărcați-l și adăugați-l la proiectul dvs., apoi la clauza dvs. de utilizări și ați pornit. Pagina GitHub are o mulțime de informații despre toate lucrurile pe care le poate face și este destul de extinsă. Iată ce ați putea face pentru marcarea temporală a unui raport.
 funcția MyService.GetReport(Parameters:String):TStream;
var
ClientTimeZone: TBundledTimeZone;
ServerTime:TDateTime;
ClientTime:TDateTime;
GlobalTime:TDateTime;
TimeZone: șir;
ÎNCEPE
...
// Valoarea TimeZone este preluată din JWT
...
ClientTimeZone :=TBundledTimeZone.GetTimeZone(TimeZone);
ServerTime := Acum;
GlobalTime := TTimeZone.Local.ToUniversalTime(ServerTime);
ClientTime := ClientTimeZone.ToLocalTime(GlobalTime);
Report.FooterLeft.Caption := FormatDateTime('aaaa-mmm-zz (zz) hh:nn:ss', ClientTime);
...
Sfârşit;

Aceeași abordare ar putea fi utilizată pentru a transmite informații locale sau alți parametri legați de formatarea datei și orei. Care ar fi, de asemenea, candidați buni de stocat în JWT. Nu este probabil ca cineva să schimbe frecvent limbile sau formatul de dată preferat în timpul sesiunii. Sau dacă ar face-o, ar fi o justificare rezonabilă să generezi un nou JWT oricum.

Luxon intră în chat.

În regulă. Bara laterală peste. Revenind la client acum, ne-am ocupat de o mulțime de lucruri din partea Delphi, așa că haideți să cercetăm puțin mai departe partea JavaScript. Ceea ce aș dori să încep este echivalentul JavaScript al exemplelor pe care le-am oferit pentru Delphi. Sigur că ar avea sens să încep de acolo, nu-i așa? Dar, din păcate, ajungem la a treia intrare în care vrem să formatăm data de ieșire și să ne blocăm imediat. Se pare că nu există o modalitate reală simplă în JavaScript nativ de a face același lucru pe care îl face și funcția Delphi FormatDateTime. Dar așteaptă doar o secundă! Știu la ce te gândești. TMS WEB Core a convertit FormatDateTime în JavaScript și a funcționat foarte bine, nu? Sigur, exact asta am crezut. Așa că m-am dus și am verificat ce au făcut. Cel mai impresionant! Chiar te încurajez să mergi și să te uiți singur. …Core SourceSystem.SysUtils.pas. Deci da. Aș dori să adopt o abordare diferită aici.

Timp de mulți ani, de departe populara bibliotecă JS pentru a se ocupa de toate aceste lucruri a fost moment.js. Dar recent (2021), creatorii acelui proiect au anunțat că a ajuns la sfârșitul vieții și că nu va mai fi dezvoltat activ. Ei au dat două motive principale. Unul era că a crescut substanțial, adăugând potențial mai mult de 200 KB proiectelor și într-un mod care nu era deosebit de adaptat tehnicilor moderne de optimizare JavaScript, cum ar fi „tree-shaking”. Cealaltă a fost această idee despre imuabilitate – folosirea bibliotecii însemna utilizarea anumitor stiluri de codare, altfel riscai efecte secundare neintenționate care nu erau întotdeauna evidente. Nu sunt foarte clar cu privire la acest ultim punct sau cum să-l definesc în termeni de echivalent Delphi. Aceasta pare mai degrabă o problemă de limbaj JavaScript, dar au simțit că a fost suficient de problematică încât să o renunțe. Una dintre alternativele pe care le recomandă ei, care este aproximativ egală cu proiectul lor, este Luxon . Principalele sale afirmații sunt că este mai mic decât moment.js și că obiectele sale sunt imuabile. În calitate de moștenitor al moment.js, se pare că a atras o mulțime de urmăritori. Și o parte din motivul pentru care am ales să o prezint aici este că este și biblioteca care acceptă acest tip de funcționalitate în Tabulator , care urmează să fie prezentată în curând în seria noastră de bloguri.

Pentru a începe cu Luxon, avem cerința obișnuită de a adăuga un link către fișierul Project.html, fie manual, fie prin funcția Gestionare biblioteci JavaScript. Aș sugera că aceasta este o bibliotecă foarte utilizată și este puțin probabil să fie actualizată cu modificări de ultimă oră, așa că destul de sigur, relativ vorbind, pentru a utiliza un CDN. Ca aceasta:

Nu există obiecte vizuale sau lucruri legate de IDE care să fie prezentate aici. Este doar o bibliotecă Helper JS care are o grămadă de cod pe care le putem folosi. Am putea face toate acestea fără Luxon, dar ar fi destul de multă muncă. Cu Luxon la treabă, ne putem ocupa apoi de conversia exemplului nostru din Delphi în JavaScript. Jetoanele de formatare sunt în mod natural diferite, dar puteți găsi lista lor de jetoane aici . Și, deși există multe lucruri de plăcut acolo, există atât de multe opțiuni când vine vorba de ceea ce ar putea fi considerate formate „localizate”, încât să le facă aproape lipsite de sens. Și cumva, în sistemul meu, lucruri precum AM/PM și L/D/AAAA încep să apară, în ciuda eforturilor mele de a le evita.
 procedura TForm1.WebButton4Click(Expeditor: TObject);
ÎNCEPE
asm
function testLuxon(aDate) {
console.log('Valoarea datei: '+aData);

console.log('Format DateTime preferat: ' +aDate.toFormat('aaaa-LL-zz HH:mm:ss'));
console.log('Al doilea format DateTime preferat: ' +aDate.toFormat('aaaa-LLL-zz HH:mm:ss'));
console.log('Al treilea format de date și oră preferat: ' +aDate.toFormat('aaaa-LLL-zz (ccc) HH:mm:ss'));

console.log('Cel mai scurt format DateTime: '+aDate.toFormat('f'));
console.log('Format mai scurt DateTime: '+aDate.toFormat('ff'));
console.log('Shortish DateTime Format: '+aDate.toFormat('fff'));
console.log('Short DateTime Format: '+aDate.toFormat('ffff'));
console.log('Long DateTime Format: '+aDate.toFormat('F'));
console.log('Longish DateTime Format: '+aDate.toFormat('FF'));
console.log('Format mai lung DateTime: '+aDate.toFormat('FFF'));
console.log('Cel mai lung format DateTime: '+aDate.toFormat('FFFF'));

console.log('Prima zi a lunii: '+aDate.startOf('lună').toFormat('aaaa-LLL-zz'));
console.log('Ultima zi a lunii: '+aDate.endOf('lună').toFormat('aaaa-LLL-zz'));
console.log('Prima zi a lunii anterioare: '+aData.plus({luni: -1}).startOf('lună').toFormat('aaaa-LLL-zz'));
console.log('Ultima zi a lunii anterioare: '+aDate.plus({luni: -1}).endOf('lună').toFormat('aaaa-LLL-zz'));
console.log('Prima zi a lunii următoare: '+aData.plus({luni: 1}).startOf('lună').toFormat('aaaa-LLL-zz'));
console.log('Ultima zi a lunii următoare: '+aDate.plus({luni: 1}).endOf('lună').toFormat('aaaa-LLL-zz'));

console.log('Ziua săptămânii: '+aDate.toFormat('cccc'));
console.log('Ziua anului: '+aData.toFormat('o'));

// Începutul săptămânii = luni
console.log('Număr ISO Ziua Săptămânii: ' +aDate.zi de săptămână);
console.log('Numărul săptămânii ISO: ' +aDate.weekNumber);
console.log('ISO Prima zi a săptămânii: '+aDate.startOf('week').toFormat('yyyy-LLL-dd (ccc)'));
console.log('ISO Ultima zi a săptămânii: '+aDate.endOf('week').toFormat('yyyy-LLL-dd (ccc)'));
console.log('ISO Prima zi a săptămânii anterioare: '+aDate.plus({săptămâni: -1}).startOf('săptămână').toFormat('yyyy-LLL-dd (ccc)'));
console.log('ISO Ultima zi a săptămânii anterioare: ' +aDate.plus({săptămâni: -1}).endOf('săptămână').toFormat('yyyy-LLL-dd (ccc)'));
console.log('ISO Prima zi a săptămânii viitoare: ' +aDate.plus({săptămâni: 1}).startOf('săptămână').toFormat('yyyy-LLL-dd (ccc)'));
console.log('ISO Ultima zi a săptămânii viitoare: ' +aDate.plus({săptămâni: 1}).endOf('week').toFormat('yyyy-LLL-dd (ccc)'));

// Începutul săptămânii = duminică
console.log('Ziua săptămânii non-ISO: '+aDate.toFormat('cccc'));
// Luni=1,Sun=7 >>> Sun=1, Sat=7
console.log('Numărul zilei săptămânii non-ISO: ' +((aDate.zi de săptămână % 7) + 1));
// Dacă este duminică, obțineți weekNumber pentru luni
console.log('Numărul săptămânii non-ISO: ' +aDate.plus({zile: Math.trunc(aDate.zi de săptămână / 7)}).weekNumber);
console.log('Ziua anului non-ISO: '+aDate.toFormat('o'));
console.log('non-ISO Prima zi a săptămânii: ' +aDate.plus({zile: + 1 - ((aDate.zile săptămânii % 7) + 1)}).toFormat('yyyy-LLL-dd (ccc) '));
console.log('non-ISO Ultima zi a săptămânii: ' +aDate.plus({zile: + 7 - ((aDate.zile săptămânii % 7) + 1)}).toFormat('yyyy-LLL-dd (ccc) '));
console.log('non-ISO Prima zi a săptămânii anterioare: ' +aDate.plus({zile: - 6 - ((aDate.zile săptămânii % 7) + 1)}).toFormat('yyyy-LLL-dd (ccc) )'));
console.log('non-ISO Ultima zi a săptămânii anterioare: ' +aDate.plus({zile: - ((aDate.zile săptămânii % 7) + 1)}).toFormat('yyyy-LLL-dd (ccc)' ));
console.log('non-ISO prima zi a săptămânii următoare: ' +aDate.plus({zile: + 8 - ((aDate.zile săptămânii % 7) + 1)}).toFormat('yyyy-LLL-dd (ccc) )'));
console.log('non-ISO Ultima zi a săptămânii viitoare: ' +aDate.plus({zile: +14 - ((aDate.zi de săptămână % 7) + 1)}).toFormat('yyyy-LLL-dd (ccc) )'));

console.log('Se calculează durata unei zile: '+Math.trunc(luxon.DateTime.now().diff(luxon.DateTime.local(1971,5,25,0,0,0,0), 'zile' ).zile));
console.log('Calculul unei durate de timp: '+luxon.DateTime.now().diff(luxon.DateTime.now().plus({zile: -1}))+'ms');
}

testLuxon(luxon.DateTime.local(2021, 12, 26, 12, 13, 14, 0));
testLuxon(luxon.DateTime.local(2022, 1, 2, 12, 13, 14, 0));
testLuxon(luxon.DateTime.local(2022, 1, 9, 12, 13, 14, 0));

Sfârşit;
Sfârşit;

Rezultatul final este că obținem același tabel ca înainte, cu excepția diferitelor formate de dată aproape de început. Rețineți că, dacă rulați acest lucru în propriul browser, formatele ar putea arăta foarte diferit de acesta, dar sperăm că restul tabelului rămâne același. Acesta este un fel de idee, până la urmă.

26-dec-2021 (duminică)

02-ian-2022 (duminică)

09-ian-2022 (duminică)

Valoarea datei

1640549594000

1641154394000

1641759194000

Formatul DateTime preferat

26-12-2021 12:13:14

2022-01-02 12:13:14

2022-01-09 12:13:14

Al doilea format de date și oră preferat

26-Dec-2021 12:13:14

2022-ian-02 12:13:14

09-ian-2022 12:13:14

Al treilea format preferat de date și oră

26-dec-2021 (duminică) 12:13:14

02-ian-2022 (duminică) 12:13:14

09-ian-2022 (duminică) 12:13:14

Cel mai scurt format DateTime

26.12.2021, ora 12:13

02.01.2022, ora 12:13

09.01.2022, ora 12:13

Format mai scurt DateTime

26 decembrie 2021, 12:13

2 ianuarie 2022, 12:13

2022-01-09 12:13:14

Format scurt DateTime

26 decembrie 2021, 12:13 PST

2 ianuarie 2022, 12:13 PST

9 ianuarie 2022, 12:13 PST

Format scurt DateTime

Duminică, 26 decembrie 2021, ora 12:13, ora standard a Pacificului

Duminică, 2 ianuarie 2022, ora 12:13, ora standard a Pacificului

Duminică, 9 ianuarie 2022, ora 12:13, ora standard a Pacificului

Long DateTime Format

26.12.2021, 12:13:14

02.01.2022, 12:13:14

09.01.2022, 12:13:14

Format DateTime lung

26 decembrie 2021, 12:13:14

2 ianuarie 2022, 12:13:14

9 ianuarie 2022, 12:13:14

Format DateTime mai lung

26 decembrie 2021, 12:13:14 PST

2 ianuarie 2022, 12:13:14 PST

9 ianuarie 2022, 12:13:14 PST

Cel mai lung format DateTime

Duminică, 26 decembrie 2021, 12:13:14 Ora standard a Pacificului

Duminică, 2 ianuarie 2022, ora 12:13:14, ora standard a Pacificului

Duminică, 9 ianuarie 2022, ora 12:13:14, ora standard a Pacificului

Prima zi a lunii

2021-dec-01

01-ian-2022

01-ian-2022

Ultima zi a lunii

2021-dec-31

31-ian-2022

31-ian-2022

Prima zi a lunii anterioare

2021-Nov-01

2021-dec-01

2021-dec-01

Ultima zi a lunii anterioare

2021-Nov-30

2021-dec-31

2021-dec-31

Prima zi a lunii viitoare

01-ian-2022

2022-feb-01

2022-feb-01

Ultima zi a lunii viitoare

31-ian-2022

28-feb-2022

28-feb-2022

Ziua Anului

360

2

9

Zi a săptămânii

duminică

duminică

duminică

Numărul ISO al zilei săptămânii

7

7

7

Numărul săptămânii ISO

51

52

1

ISO prima zi a săptămânii

20-dec-2021 (luni)

27-dec-2021 (luni)

03-ian-2022 (luni)

ISO Ultima zi a săptămânii

26-dec-2021 (duminică)

02-ian-2022 (duminică)

09-ian-2022 (duminică)

ISO prima zi a săptămânii precedente

13-dec-2021 (luni)

20-dec-2021 (luni)

27-dec-2021 (luni)

ISO Ultima zi a săptămânii precedente

19-dec-2021 (duminică)

26-dec-2021 (duminică)

02-ian-2022 (duminică)

ISO prima zi a săptămânii viitoare

27-dec-2021 (luni)

03-ian-2022 (luni)

10-ian-2022 (luni)

ISO Ultima zi a săptămânii viitoare

02-ian-2022 (duminică)

09-ian-2022 (duminică)

16-ian-2022 (duminică)

Numărul zilei săptămânii non-ISO

1

1

1

Numărul săptămânii non-ISO

52

1

2

prima zi a acestei săptămâni non-ISO

26-dec-2021 (duminică)

02-ian-2022 (duminică)

09-ian-2022 (duminică)

non-ISO Ultima zi a acestei săptămâni

01-ian-2022 (sâmbătă)

08-ian-2022 (sâmbătă)

15-ian-2022 (sâmbătă)

non-ISO prima zi a săptămânii precedente

19-dec-2021 (duminică)

26-dec-2021 (duminică)

02-ian-2022 (duminică)

non-ISO Ultima zi a săptămânii precedente

01-ian-2022 (sâmbătă)

01-ian-2022 (sâmbătă)

08-ian-2022 (sâmbătă)

non-ISO prima zi a săptămânii viitoare

02-ian-2022 (duminică)

09-ian-2022 (duminică)

16-ian-2022 (duminică)

non-ISO Ultima zi a săptămânii viitoare

08-ian-2022 (sâmbătă)

15-ian-2022 (sâmbătă)

22-ian-2022 (sâmbătă)

Calcularea duratei unei zile

18626

18626

18626

non-ISO Ultima zi a săptămânii viitoare

86400000ms

86400000ms

86400000 ms

În regulă. Deci acum putem face în JavaScript ceea ce am putea face deja în Delphi. Acest lucru este important, cu siguranță, astfel încât să ne putem verifica munca și să ne asigurăm că lucrurile rămân consecvente. Cu toate acestea, nu suntem aici doar pentru a converti Delphi în JavaScript. Nici să-mi amintesc că sunt bătrân! Mai degrabă, scopul tuturor acestor lucruri este că dorim să putem valorifica ceea ce Luxon poate oferi pentru a îmbunătăți și a integra în continuare proiectele noastre Delphi și TMS WEB Core.

Grozav. Deci, ce altceva poate face Luxon?

Destul de multe, de fapt. În primul rând, conversia între fusurile orare nu este acum deloc dificilă.

 asm
var vancouver = luxon.DateTime.local(2022, 5, 22, 12, 13, 14, 0);
console.log('America/Vancouver: '+vancouver.toFormat('yyyy-LLL-zz (ccc) HH:mm:ss')+' ( fus orar = '+vancouver.zoneName+' )');
console.log('America/New_York: '+vancouver.setZone('America/New_York').toFormat('yyyy-LLL-zz (ccc) HH:mm:ss'));
console.log('Europa/Londra: '+vancouver.setZone('Europa/Londra').toFormat('aaaa-LLL-zz (ccc) HH:mm:ss'));
console.log('Europa/Paris: '+vancouver.setZone('Europa/Paris').toFormat('aaaa-LLL-zz (ccc) HH:mm:ss'));
console.log('Europa/Kiev: '+vancouver.setZone('Europa/Kiev').toFormat('aaaa-LLL-zz (ccc) HH:mm:ss'));
console.log('Asia/Singapore: '+vancouver.setZone('Asia/Singapore').toFormat('aaaa-LLL-zz (ccc) HH:mm:ss'));
Sfârşit;
// ieșire console.log:
America/Vancouver: 22-mai-2022 (duminică) 12:13:14 ( fus orar = America/Vancouver )
America/New_York: 22-mai-2022 (duminică) 15:13:14
Europa/Londra: 22 mai 2022 (duminică) 20:13:14
Europa/Paris: 22-mai-2022 (duminică) 21:13:14
Europa/Kiev: 22 mai 2022 (duminică) 22:13:14
Asia/Singapor: 23 mai 2022 (luni) 03:13:14

Pot fi folosite și diferite variante ale modului în care este exprimat fusul orar, dacă variația continent/oraș nu este pe placul dvs.

 asm
console.log('America/Vancouver: '+vancouver.toFormat('yyyy-LLL-zz (ccc) HH:mm:ss')+' ( fus orar = '+vancouver.zoneName+' )');
console.log('UTC: '+vancouver.setZone('utc').toFormat('aaaa-LLL-zz (ccc) HH:mm:ss'));
console.log('UTC+1: '+vancouver.setZone('utc+1').toFormat('aaaa-LLL-zz (ccc) HH:mm:ss'));
console.log('CET (Europa Centrală): '+vancouver.setZone('cet').toFormat('yyyy-LLL-zz (ccc) HH:mm:ss'));
Sfârşit;
// ieșire console.log:
America/Vancouver: 22-mai-2022 (duminică) 12:13:14 ( fus orar = America/Vancouver )
UTC: 22-mai-2022 (duminică) 19:13:14
UTC+1: 22 mai 2022 (duminică) 20:13:14
CET (Europa Centrală): 22 mai 2022 (duminică) 21:13:14

Am văzut deja o mulțime de exemple de ajustare a unei date plus/minus un interval, cum ar fi „zile”, „săptămâni”, „luni” și așa mai departe. Când se calculează diferența dintre două date, există opțiunea de a returna și o valoare care are astfel de intervale, făcând posibilă afișarea unor durate mai prietenoase.

 asm
var start = luxon.DateTime.local(2022, 5, 21, 0, 13, 14);
var finish = luxon.DateTime.local(2022, 5, 22, 12, 13, 14); // 36 de ore mai târziu

console.log('start a fost '+Math.trunc(finish.diff(start, 'minute').minutes)+' minut(e) în urmă');
console.log('start a fost '+Math.trunc(finish.diff(start, 'hours').hours)+' ora(e) acum');
console.log('start a fost '+Math.trunc(finish.diff(start, 'zile').zile)+' zi(ile) in urma');
console.log('start a fost '+JSON.stringify(finish.diff(start, ['zile','ore','minute']).toObject()));
console.log('start a fost '+finish.diff(start, ['zile','ore']).toHuman()+'acum');
Sfârşit;
începutul a fost acum 2160 minut(e).
începutul a fost acum 36 ore
începutul a fost acum 1 zi(e).
începutul a fost {"days":1,"hours":12,"minutes":0}
începutul a fost acum 1 zi, 12 ore

Nu este greu de imaginat toate felurile în care acestea ar putea fi folosite pentru a prezenta date mai citite de om.

Deplasarea în jur.

Aceste tipuri de funcții pot fi, de asemenea, bine încapsulate. Am câteva idei despre crearea unor funcții comune care ar putea intra în biblioteca noastră JSExtend, prin ceva de genul Luxon.pas. Iată câțiva concurenți. Ce funcții crezi că ar putea fi utile?

 funcția TForm1.ConvertTimezone(aDateTime: TDateTime; TZ: String): TDateTime;
var
ayear, amonth, aday, ahour, aminute, asecond, amilisecond: word;
ÎNCEPE
DecodeDateTime(aDateTime, ayear, month, aday, ahour, aminute, asecond, amilisecundă);
asm
var lDateTime = new luxon.DateTime.local(ayear, month, aday, ahour, aminute, asecond, amillisecond).setZone(TZ);
ayear = lDateTime.year;
luna = lDateTime.month;
aday = lDateTime.day;
ahour = lDateTime.hour;
aminute = lDateTime.minute;
asecond = lDateTime.second;
amilisecundă = lDateTime.milisecundă;
Sfârşit;
Rezultat := EncodeDateTime(un an, o lună, o zi, o oră, un minut, o secundă, amilisecundă);
Sfârşit;
procedura TForm1.WebButton1Click(Expeditor: TObject);
var
olddate, newdate: TDateTime;
ÎNCEPE
data veche := EncodeDateTime(2022, 5, 22, 12, 13, 14, 0);
data noua := ConvertTimeZone(data veche, 'Europa/Paris');
console.log(FormatDateTime('aaaa-mmm-zz hh:nn:ss',data veche));
console.log(FormatDateTime('aaaa-mmm-zz hh:nn:ss',newdate));
Sfârşit;
// ieșire console.log:
22-mai-2022 12:13:14
22-mai-2022 21:13:14

funcția TForm1.HumanDifference(nowDateTime: TDateTime; thenDateTime: TDateTime): String;
var
ayear, amonth, aday, ahour, aminute, asecond, amilisecond: word;
byear, blună, zi, bhour, bminut, bsecundă, bmilisecundă: cuvânt;
ÎNCEPE
DecodeDateTime(nowDateTime, ayear, month, aday, ahour, aminute, asecond, amilisecundă);
DecodeDateTime(thenDateTime, byear, bmonth, bday, bhour, bminute, bsecond, bmilisecundă);
asm
var aDateTime = new luxon.DateTime.local(ayear, month, aday, ahour, aminute, asecond, amilisecond);
var bDateTime = new luxon.DateTime.local(byear, bmonth, bday, bhour, bminute, bsecond, bmilisecundă);
Rezultat = aDateTime.diff(bDateTime, ['days','hours']).toHuman()+'acum';
Sfârşit;
Sfârşit;
procedura TForm1.WebButton1Click(Expeditor: TObject);
var
olddate, newdate: TDateTime;
ÎNCEPE
data veche := EncodeDateTime(2022, 5, 21, 06, 13, 14, 0);
data noua := EncodeDateTime(2022, 5, 22, 12, 13, 14, 0); // 30 de ore mai târziu
console.log(HumanDifference(data noua, data veche));
Sfârşit;
// ieșire console.log:
acum 1 zi, 6 ore

Ramas fara timp!

Cred că aproximativ acoperă cel puțin elementele de bază ale Luxon și utilizarea lui pentru a vă ajuta să vă îmbunătățiți proiectele TMS WEB Core. Sper că ați găsit acest lucru util și că puteți profita cu ușurință de Luxon în propriile proiecte, cu orice sisteme de localizare/internaționalizare/numerotare care vi se întâmplă!

Andrew Simard.