Der endgültige Leitfaden zur DateTime-Manipulation
Veröffentlicht: 2022-03-11Als Softwareentwickler können Sie vor der Datumsmanipulation nicht davonlaufen. Fast jede App, die ein Entwickler erstellt, verfügt über eine Komponente, bei der Datum und Uhrzeit vom Benutzer abgerufen, in einer Datenbank gespeichert und dem Benutzer angezeigt werden müssen.
Fragen Sie einen beliebigen Programmierer nach seiner Erfahrung im Umgang mit Daten und Zeitzonen und er wird Ihnen wahrscheinlich einige Kriegsgeschichten erzählen. Der Umgang mit Datums- und Zeitfeldern ist sicherlich kein Hexenwerk, kann aber oft mühsam und fehleranfällig sein.
Es gibt Hunderte von Artikeln zu diesem Thema, aber die meisten sind entweder zu akademisch und konzentrieren sich auf die wesentlichen Details, oder sie sind zu lückenhaft und bieten kurze Codeschnipsel ohne viele begleitende Erklärungen. Dieser ausführliche Leitfaden zur DateTime-Manipulation soll Ihnen dabei helfen, die Programmierkonzepte und Best Practices zu verstehen, die für Zeit und Datum relevant sind, ohne dass Sie eine Flut von Informationen zu diesem Thema durchsuchen müssen.
In diesem Artikel werde ich Ihnen helfen, klar über Datums- und Zeitfelder nachzudenken, und einige bewährte Verfahren vorschlagen, mit denen Sie die Datums-/Zeithölle vermeiden können. Hier werden einige der Schlüsselkonzepte untersucht, die für die korrekte Bearbeitung von Datums- und Uhrzeitwerten erforderlich sind, Formate, die zum Speichern von DateTime-Werten und deren Übertragung über APIs geeignet sind, und mehr.
Für den Anfang ist die richtige Antwort für Produktionscode fast immer, eine richtige Bibliothek zu verwenden, anstatt eine eigene zu erstellen. Die in diesem Artikel diskutierten potenziellen Schwierigkeiten bei der DateTime-Berechnung sind nur die Spitze des Eisbergs, aber es ist dennoch hilfreich, sie zu kennen, mit oder ohne Bibliothek.
DateTime-Bibliotheken helfen, wenn Sie sie richtig verstehen
Datumsbibliotheken helfen in vielerlei Hinsicht, Ihr Leben einfacher zu machen. Sie vereinfachen die Datumsanalyse, arithmetische und logische Operationen und die Datumsformatierung erheblich. Sie können eine zuverlässige Datumsbibliothek sowohl für das Frontend als auch für das Backend finden, um den größten Teil der schweren Arbeit für Sie zu erledigen.
Wir verwenden jedoch häufig Datumsbibliotheken, ohne darüber nachzudenken, wie Datum/Uhrzeit tatsächlich funktioniert. Datum/Uhrzeit ist ein komplexes Konzept. Die Fehler, die aufgrund des falschen Verständnisses auftreten, können selbst mit Hilfe von Datumsbibliotheken äußerst schwer zu verstehen und zu beheben sein. Als Programmierer müssen Sie die Grundlagen verstehen und in der Lage sein, die Probleme zu verstehen, die Datumsbibliotheken lösen, um das Beste aus ihnen herauszuholen.
Auch Datums-/Zeitbibliotheken können Sie nur so weit bringen. Alle Datumsbibliotheken funktionieren, indem sie Ihnen Zugriff auf bequeme Datenstrukturen geben, um eine DateTime darzustellen. Wenn Sie Daten über eine REST-API senden und empfangen, müssen Sie das Datum eventuell in eine Zeichenfolge konvertieren und umgekehrt, da JSON keine native Datenstruktur zur Darstellung von DateTime hat. Die Konzepte, die ich hier skizziert habe, helfen Ihnen, einige der häufigsten Probleme zu vermeiden, die bei diesen Datum-zu-String- und String-zu-Datum-Transformationen auftreten können.
Hinweis: Obwohl ich in diesem Artikel JavaScript als Programmiersprache verwendet habe, handelt es sich um allgemeine Konzepte, die weitgehend auf praktisch alle Programmiersprachen und ihre Datumsbibliotheken zutreffen. Auch wenn Sie also noch nie eine Zeile JavaScript geschrieben haben, können Sie gerne weiterlesen, da ich in dem Artikel kaum Vorkenntnisse in JavaScript voraussetze.
Standardisierung der Zeit
Eine DateTime ist ein ganz bestimmter Zeitpunkt. Denken wir darüber nach. Während ich diesen Artikel schreibe, zeigt die Uhr auf meinem Laptop den 21. Juli, 13:29 Uhr. Das nennen wir „Ortszeit“, die Zeit, die ich auf Wanduhren um mich herum und auf meiner Armbanduhr sehe.
Geben oder nehmen Sie sich ein paar Minuten Zeit, wenn ich meine Freundin bitte, mich um 15:00 Uhr in einem nahe gelegenen Café zu treffen, kann ich damit rechnen, sie ungefähr zu dieser Zeit dort zu sehen. Ebenso würde es keine Verwirrung geben, wenn ich stattdessen beispielsweise sagen würde: „Lass uns in eineinhalb Stunden treffen.“ Wir sprechen routinemäßig auf diese Weise mit Menschen, die in derselben Stadt oder Zeitzone leben.
Stellen wir uns ein anderes Szenario vor: Ich möchte einem Freund aus Uppsala, Schweden, sagen, dass ich ihn um 17:00 Uhr sprechen möchte. Ich schicke ihm eine Nachricht: „Hey Anton, lass uns um 17 Uhr reden.“ Ich bekomme sofort die Antwort: „Ihre Zeit oder meine Zeit?“
Anton erzählt mir, dass er in der mitteleuropäischen Zeitzone UTC+01:00 lebt. Ich lebe in UTC+05:45. Das heißt, wenn es an meinem Wohnort 17 Uhr ist, ist es 17 Uhr - 05:45 = 11:15 Uhr UTC, was in Uppsala 11:15 Uhr UTC + 01:00 = 12:15 Uhr bedeutet, perfekt für beide von uns.
Beachten Sie auch den Unterschied zwischen Zeitzone (Mitteleuropäische Zeit) und Zeitzonenverschiebung (UTC+05:45). Länder können auch aus politischen Gründen beschließen, ihre Zeitzonen-Offsets für die Sommerzeit zu ändern. Fast jedes Jahr ändern sich die Regeln in mindestens einem Land, was bedeutet, dass jeder Code mit diesen eingebauten Regeln auf dem neuesten Stand gehalten werden muss – es lohnt sich zu überlegen, wovon Ihre Codebasis für jede Ebene Ihrer App abhängt.
Das ist ein weiterer guter Grund, warum wir empfehlen, dass sich in den meisten Fällen nur das Frontend mit Zeitzonen befasst. Wenn dies nicht der Fall ist, was passiert, wenn die von Ihrer Datenbank-Engine verwendeten Regeln nicht mit denen Ihres Front- oder Backends übereinstimmen?
Dieses Problem, zwei verschiedene Versionen der Zeit zu verwalten, relativ zum Benutzer und relativ zu einem allgemein akzeptierten Standard, ist schwierig, umso mehr in der Welt der Programmierung, wo Präzision entscheidend ist und sogar eine Sekunde einen großen Unterschied machen kann. Der erste Schritt zur Lösung dieser Probleme besteht darin, DateTime in UTC zu speichern.
Standardisierung des Formats
Das Standardisieren der Zeit ist wunderbar, weil ich nur die UTC-Zeit speichern muss und solange ich die Zeitzone des Benutzers kenne, kann ich immer in seine Zeit umrechnen. Wenn ich umgekehrt die Ortszeit und die Zeitzone eines Benutzers kenne, kann ich diese in UTC umwandeln.
Datum und Uhrzeit können jedoch in vielen verschiedenen Formaten angegeben werden. Als Datum können Sie „30. Juli“ oder „30. Juli“ oder „30.7.“ (oder 30.7., je nach Wohnort) schreiben. Für die Zeit könnten Sie „9:30 PM“ oder „2130“ schreiben.
Wissenschaftler auf der ganzen Welt haben sich zusammengeschlossen, um dieses Problem anzugehen, und haben sich für ein Format zur Zeitbeschreibung entschieden, das Programmierern sehr gefällt, weil es kurz und präzise ist. Wir nennen es gerne „ISO-Datumsformat“, das eine vereinfachte Version des erweiterten ISO-8601-Formats ist und wie folgt aussieht:
Für 00:00 oder UTC verwenden wir stattdessen „Z“, was Zulu-Zeit bedeutet, ein anderer Name für UTC.
Datumsmanipulation und Arithmetik in JavaScript
Bevor wir mit Best Practices beginnen, lernen wir die Datumsmanipulation mit JavaScript kennen, um die Syntax und allgemeine Konzepte zu verstehen. Obwohl wir JavaScript verwenden, können Sie diese Informationen einfach an Ihre bevorzugte Programmiersprache anpassen.
Wir werden Datumsarithmetik verwenden, um allgemeine datumsbezogene Probleme zu lösen, auf die die meisten Entwickler stoßen.
Mein Ziel ist es, Ihnen das Erstellen eines Datumsobjekts aus einer Zeichenfolge und das Extrahieren von Komponenten aus einer Zeichenfolge bequem zu machen. Dabei kann Ihnen eine Datumsbibliothek helfen, aber es ist immer besser zu verstehen, wie es hinter den Kulissen gemacht wird.
Sobald wir uns mit Datum/Uhrzeit die Hände schmutzig gemacht haben, ist es einfacher, über die Probleme nachzudenken, mit denen wir konfrontiert sind, die besten Praktiken zu extrahieren und weiterzumachen. Wenn Sie zu den Best Practices springen möchten, können Sie dies gerne tun, aber ich würde Ihnen dringend empfehlen, den Abschnitt zur Datumsarithmetik unten zumindest zu überfliegen.
Das JavaScript-Datumsobjekt
Programmiersprachen enthalten nützliche Konstrukte, die uns das Leben erleichtern. Das JavaScript Date
Objekt ist so ein Ding. Es bietet bequeme Methoden, um das aktuelle Datum und die aktuelle Uhrzeit zu erhalten, ein Datum in einer Variablen zu speichern, Datumsarithmetik durchzuführen und das Datum basierend auf dem Gebietsschema des Benutzers zu formatieren.
Aufgrund von Unterschieden zwischen Browserimplementierungen und falscher Handhabung der Sommerzeit (DST) wird die Abhängigkeit vom Date-Objekt für unternehmenskritische Anwendungen nicht empfohlen und Sie sollten wahrscheinlich eine DateTime-Bibliothek wie Luxon, date-fns oder dayjs verwenden. (Was auch immer Sie verwenden, vermeiden Sie das einst beliebte Moment.js – oft einfach moment
genannt, wie es im Code erscheint – da es jetzt veraltet ist.)
Aber für Bildungszwecke werden wir die Methoden verwenden, die das Date()-Objekt bereitstellt, um zu lernen, wie JavaScript mit DateTime umgeht.
Aktuelles Datum erhalten
const currentDate = new Date();
Wenn Sie nichts an den Date-Konstruktor übergeben, enthält das zurückgegebene date-Objekt das aktuelle Datum und die aktuelle Uhrzeit.
Sie können es dann so formatieren, dass nur der Datumsteil wie folgt extrahiert wird:
const currentDate = new Date(); const currentDayOfMonth = currentDate.getDate(); const currentMonth = currentDate.getMonth(); // Be careful! January is 0, not 1 const currentYear = currentDate.getFullYear(); const dateString = currentDayOfMonth + "-" + (currentMonth + 1) + "-" + currentYear; // "27-11-2020"
Hinweis: Die Falle „Januar ist 0“ ist weit verbreitet, aber nicht universell. Es lohnt sich, die Dokumentation jeder Sprache (oder jedes Konfigurationsformats: zB ist cron insbesondere 1-basiert) doppelt zu überprüfen, bevor Sie damit beginnen, es zu verwenden.
Abrufen des aktuellen Zeitstempels
Wenn Sie stattdessen den aktuellen Zeitstempel abrufen möchten, können Sie ein neues Date-Objekt erstellen und die Methode getTime() verwenden.
const currentDate = new Date(); const timestamp = currentDate.getTime();
In JavaScript ist ein Zeitstempel die Anzahl der Millisekunden, die seit dem 1. Januar 1970 vergangen sind.
Wenn Sie <IE8 nicht unterstützen möchten, können Sie Date.now()
verwenden, um den Zeitstempel direkt zu erhalten, ohne ein neues Date-Objekt erstellen zu müssen.
Analysieren eines Datums
Das Konvertieren eines Strings in ein JavaScript-Datumsobjekt erfolgt auf unterschiedliche Weise.
Der Konstruktor des Date-Objekts akzeptiert eine Vielzahl von Datumsformaten:
const date1 = new Date("Wed, 27 July 2016 13:30:00"); const date2 = new Date("Wed, 27 July 2016 07:45:00 UTC"); const date3 = new Date("27 July 2016 13:30:00 UTC+05:45");
Beachten Sie, dass Sie den Wochentag nicht angeben müssen, da JS den Wochentag für jedes Datum bestimmen kann.
Sie können Jahr, Monat, Tag, Stunden, Minuten und Sekunden auch als separate Argumente übergeben:
const date = new Date(2016, 6, 27, 13, 30, 0);
Natürlich können Sie immer das ISO-Datumsformat verwenden:
const date = new Date("2016-07-27T07:45:00Z");
Sie können jedoch Probleme bekommen, wenn Sie die Zeitzone nicht explizit angeben!
const date1 = new Date("25 July 2016"); const date2 = new Date("July 25, 2016");
Beide geben Ihnen den 25. Juli 2016 00:00:00 Ortszeit an.
Wenn Sie das ISO-Format verwenden, wird die Zeitzone automatisch als UTC akzeptiert, selbst wenn Sie nur das Datum und nicht die Uhrzeit und die Zeitzone angeben.
Dies bedeutet, dass:
new Date("25 July 2016").getTime() !== new Date("2016-07-25").getTime() new Date("2016-07-25").getTime() === new Date("2016-07-25T00:00:00Z").getTime()
Formatieren eines Datums
Glücklicherweise verfügt modernes JavaScript über einige bequeme Internationalisierungsfunktionen, die in den standardmäßigen Intl
-Namensraum integriert sind und die Datumsformatierung zu einem unkomplizierten Vorgang machen.
Dazu benötigen wir zwei Objekte: ein Date
und ein Intl.DateTimeFormat
, die mit unseren Ausgabeeinstellungen initialisiert werden. Angenommen, wir möchten das amerikanische Format (M/D/YYYY) verwenden, würde dies folgendermaßen aussehen:
const firstValentineOfTheDecade = new Date(2020, 1, 14); // 1 for February const enUSFormatter = new Intl.DateTimeFormat('en-US'); console.log(enUSFormatter.format(firstValentineOfTheDecade)); // 2/14/2020
Wenn wir stattdessen das niederländische Format (D/M/YYYY) wollten, würden wir einfach einen anderen Kulturcode an den DateTimeFormat
Konstruktor übergeben:
const nlBEFormatter = new Intl.DateTimeFormat('nl-BE'); console.log(nlBEFormatter.format(firstValentineOfTheDecade)); // 14/2/2020
Oder eine längere Form des amerikanischen Formats mit ausgeschriebenem Monatsnamen:
const longEnUSFormatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', }); console.log(longEnUSFormatter.format(firstValentineOfTheDecade)); // February 14, 2020
Wenn wir nun ein korrektes Ordinalformat für den Tag des Monats wünschen – also „14.“ statt nur „14“ –, muss leider etwas umgangen werden, da zum jetzigen Zeitpunkt die einzigen gültigen Werte für day
sind "numeric"
oder "2-digit"
. Indem wir Flavio Copes' Version des Codes von Mathias Bynens ausleihen, um einen anderen Teil von Intl
dafür zu nutzen, können wir die Ausgabe des Tages des Monats über formatToParts()
:
const pluralRules = new Intl.PluralRules('en-US', { type: 'ordinal' }) const suffixes = { 'one': 'st', 'two': 'nd', 'few': 'rd', 'other': 'th' } const convertToOrdinal = (number) => `${number}${suffixes[pluralRules.select(number)]}` // At this point: // convertToOrdinal("1") === "1st" // convertToOrdinal("2") === "2nd" // etc. const extractValueAndCustomizeDayOfMonth = (part) => { if (part.type === "day") { return convertToOrdinal(part.value); } return part.value; }; console.log( longEnUSFormatter.formatToParts(firstValentineOfTheDecade) .map(extractValueAndCustomizeDayOfMonth) .join("") ); // February 14th, 2020
Leider wird formatToParts
zum jetzigen Zeitpunkt überhaupt nicht vom Internet Explorer (IE) unterstützt, aber alle anderen Desktop-, Mobil- und Back-End-Technologien (z. B. Node.js) werden unterstützt. Für diejenigen, die den IE unterstützen und unbedingt Ordnungszahlen benötigen, bietet die folgende Randnotiz (oder besser eine richtige Datumsbibliothek) eine Antwort.
Wenn Sie ältere Browser wie IE vor Version 11 unterstützen müssen, ist die Datumsformatierung in JavaScript schwieriger, da es keine Standardfunktionen zur Datumsformatierung wie strftime
in Python oder PHP gab.
In PHP zum Beispiel liefert die Funktion strftime("Today is %b %d %Y %X", mktime(5,10,0,12,30,99))
Today is Dec 30 1999 05:10:00
.
Sie können eine andere Buchstabenkombination mit vorangestelltem %
verwenden, um das Datum in verschiedenen Formaten zu erhalten. (Vorsicht, nicht jede Sprache weist jedem Buchstaben die gleiche Bedeutung zu – insbesondere können „M“ und „m“ für Minuten und Monate vertauscht werden.)
Wenn Sie sich sicher sind, welches Format Sie verwenden möchten, extrahieren Sie am besten einzelne Bits mit den oben behandelten JavaScript-Funktionen und erstellen selbst einen String.
var currentDate = new Date (); var date = currentDate.getDate(); var month = currentDate.getMonth(); var year = currentDate.getFullYear();
Wir können das Datum im Format MM/TT/JJJJ erhalten
var monthDateYear = (month+ 1 ) + "/" + date + "/" + year;
Das Problem bei dieser Lösung besteht darin, dass Datumsangaben eine inkonsistente Länge erhalten können, da einige Monate und Tage des Monats einstellig und andere zweistellig sind. Dies kann beispielsweise problematisch sein, wenn Sie das Datum in einer Tabellenspalte anzeigen, da die Datumsangaben nicht übereinstimmen.
Wir können dies beheben, indem wir eine „Pad“-Funktion verwenden, die eine führende 0 hinzufügt.
function pad ( n ) { return n< 10 ? '0' +n : n; }
Jetzt erhalten wir das korrekte Datum im Format MM/TT/JJJJ mit:
var mmddyyyy = pad(month + 1 ) + "/" + pad(date) + "/" + year;
Wenn wir stattdessen DD-MM-YYYY wollen, ist der Prozess ähnlich:
var ddmmyyyy = pad(date) + "-" + pad(month + 1 ) + "-" + year;
Lassen Sie uns den Einsatz erhöhen und versuchen, das Datum im Format „Monat, Datum, Jahr“ zu drucken. Wir benötigen eine Zuordnung von Monatsindizes zu Namen:
var monthNames = [ "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" ]; var dateWithFullMonthName = monthNames[month] + " " + pad(date) + ", " + year;
Einige Leute möchten das Datum als 1. Januar 2013 anzeigen. Kein Problem, alles, was wir brauchen, ist eine ordinal
der Hilfsfunktion, die 1. für 1, 12. für 12 und 103. für 103 usw. zurückgibt, und der Rest ist einfach:
var ordinalDate = ordinal(date) + " " + monthNames[month] + ", " + year;
Es ist einfach, den Wochentag aus dem Datumsobjekt zu bestimmen, also fügen wir das hinzu:
var daysOfWeek = [ "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" ]; ordinalDateWithDayOfWeek = daysOfWeek[currentDate.getDay()] + ", " + ordinalDate;
Der wichtigere Punkt hier ist, sobald Sie die Zahlen aus dem Datum extrahiert haben, bezieht sich die Formatierung hauptsächlich auf Zeichenfolgen.
Ändern des Datumsformats
Sobald Sie wissen, wie man ein Datum analysiert und formatiert, müssen Sie beim Ändern eines Datums von einem Format in ein anderes nur noch beide kombinieren.
Wenn Sie beispielsweise ein Datum im Format 21. Juli 2013 haben und das Format in 21.07.2013 ändern möchten, können Sie dies wie folgt erreichen:
const myDate = new Date("Jul 21, 2013"); const dayOfMonth = myDate.getDate(); const month = myDate.getMonth(); const year = myDate.getFullYear(); function pad(n) { return n<10 ? '0'+n : n } const ddmmyyyy = pad(dayOfMonth) + "-" + pad(month + 1) + "-" + year; // "21-07-2013"
Verwenden der Lokalisierungsfunktionen von JavaScript Date Object
Die oben besprochenen Datumsformatierungsmethoden sollten in den meisten Anwendungen funktionieren, aber wenn Sie die Formatierung des Datums wirklich lokalisieren möchten, schlage ich vor, dass Sie die Methode toLocaleDateString()
des Date
-Objekts verwenden:
const today = new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', });
… ergibt so etwas wie den 26 Jul 2016
.

Wenn Sie das Gebietsschema in „en-US“ ändern, wird stattdessen „26. Juli 2016“ angezeigt. Beachten Sie, wie sich die Formatierung geändert hat, die Anzeigeoptionen jedoch unverändert geblieben sind – eine sehr nützliche Funktion. Wie im vorherigen Abschnitt gezeigt, funktioniert die neuere Intl.DateTimeFormat
-basierte Technik sehr ähnlich, ermöglicht Ihnen jedoch die Wiederverwendung eines Formatierungsobjekts, sodass Sie Optionen nur einmal festlegen müssen.
Bei toLocaleDateString()
ist es eine gute Angewohnheit, die Formatierungsoptionen immer zu übergeben, auch wenn die Ausgabe auf Ihrem Computer gut aussieht. Dies kann die Benutzeroberfläche davor schützen, in unerwarteten Gebietsschemas mit wirklich langen Monatsnamen zu brechen oder wegen kurzer Monatsnamen unangenehm auszusehen.
Wenn ich stattdessen den vollen Monat „Juli“ haben möchte, ändere ich nur den Monatsparameter in den Optionen auf „lang“. JavaScript erledigt alles für mich. Für en-US erhalte ich jetzt den 26. Juli 2016.
Hinweis: Wenn Sie möchten, dass der Browser automatisch das Gebietsschema des Benutzers verwendet, können Sie „undefined“ als ersten Parameter übergeben.
Wenn Sie die numerische Version des Datums anzeigen und sich nicht mit MM/TT/JJJJ vs. TT/MM/JJJJ für verschiedene Gebietsschemas herumschlagen möchten, schlage ich die folgende einfache Lösung vor:
const today = new Date().toLocaleDateString(undefined, { day: 'numeric', month: 'numeric', year: 'numeric', });
Auf meinem Computer gibt dies 7/26/2016
. Wenn Sie sicherstellen möchten, dass Monat und Datum zweistellig sind, ändern Sie einfach die Optionen:
const today = new Date().toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', });
Dies gibt 07/26/2016
. Genau das, was wir wollten!
Sie können auch einige andere verwandte Funktionen verwenden, um die Art und Weise zu lokalisieren, wie sowohl Uhrzeit als auch Datum angezeigt werden:
Code | Ausgabe | Beschreibung |
---|---|---|
| "4:21:38 Uhr" | Zeigen Sie die lokalisierte Version nur der Zeit an |
| "04:21:38 Uhr" | Zeigen Sie die lokalisierte Zeit basierend auf den bereitgestellten Optionen an |
| "22.7.2016, 4:21:38 Uhr" | Datum und Uhrzeit für das Gebietsschema des Benutzers anzeigen |
| "22.07.2016, 04:21 Uhr" | Zeigen Sie lokalisiertes Datum und Uhrzeit basierend auf den bereitgestellten Optionen an |
Berechnung relativer Daten und Zeiten
Hier ist ein Beispiel für das Hinzufügen von 20 Tagen zu einem JavaScript-Datum (dh das Ermitteln des Datums 20 Tage nach einem bekannten Datum):
const myDate = new Date("July 20, 2016 15:00:00"); const nextDayOfMonth = myDate.getDate() + 20; myDate.setDate(nextDayOfMonth); const newDate = myDate.toLocaleString();
Das ursprüngliche Datumsobjekt stellt jetzt ein Datum dar, das 20 Tage nach dem 20. Juli liegt, und newDate
enthält eine lokalisierte Zeichenfolge, die dieses Datum darstellt. In meinem Browser enthält newDate
„8/9/2016, 3:00:00 PM“.
Um relative Zeitstempel mit einer genaueren Differenz als ganze Tage zu berechnen, können Sie Date.getTime()
und Date.setTime()
verwenden, um mit Ganzzahlen zu arbeiten, die die Anzahl der Millisekunden seit einer bestimmten Epoche darstellen – nämlich dem 1. Januar 1970. Für Beispiel, wenn Sie wissen möchten, wann es gerade 17 Stunden später ist:
const msSinceEpoch = (new Date()).getTime(); const seventeenHoursLater = new Date(msSinceEpoch + 17 * 60 * 60 * 1000);
Daten vergleichen
Wie bei allem anderen, was mit Datum zu tun hat, hat das Vergleichen von Datumsangaben seine eigenen Fallstricke.
Zuerst müssen wir Datumsobjekte erstellen. Glücklicherweise funktionieren <, >, <= und >= alle. Der Vergleich zwischen dem 19. Juli 2014 und dem 18. Juli 2014 ist also so einfach wie:
const date1 = new Date("July 19, 2014"); const date2 = new Date("July 28, 2014"); if(date1 > date2) { console.log("First date is more recent"); } else { console.log("Second date is more recent"); }
Die Überprüfung auf Gleichheit ist schwieriger, da zwei Datumsobjekte, die dasselbe Datum darstellen, immer noch zwei verschiedene Datumsobjekte sind und nicht gleich sein werden. Das Vergleichen von Datumszeichenfolgen ist eine schlechte Idee, da beispielsweise „20. Juli 2014“ und „20. Juli 2014“ dasselbe Datum darstellen, aber unterschiedliche Zeichenfolgendarstellungen haben. Der folgende Ausschnitt veranschaulicht den ersten Punkt:
const date1 = new Date("June 10, 2003"); const date2 = new Date(date1); const equalOrNot = date1 == date2 ? "equal" : "not equal"; console.log(equalOrNot);
Dies wird not equal
ausgeben.
Dieser spezielle Fall kann behoben werden, indem die ganzzahligen Äquivalente der Daten (ihre Zeitstempel) wie folgt verglichen werden:
date1.getTime() == date2.getTime()
Ich habe dieses Beispiel an vielen Stellen gesehen, aber es gefällt mir nicht, weil Sie normalerweise kein Datumsobjekt aus einem anderen Datumsobjekt erstellen. Daher denke ich, dass das Beispiel nur aus akademischer Sicht wichtig ist. Außerdem müssen sich beide Date-Objekte auf dieselbe Sekunde beziehen, während Sie vielleicht nur wissen möchten, ob sie sich auf denselben Tag oder dieselbe Stunde oder Minute beziehen.
Schauen wir uns ein praktischeres Beispiel an. Sie versuchen zu vergleichen, ob der vom Benutzer eingegebene Geburtstag mit dem Glücksdatum übereinstimmt, das Sie von einer API erhalten.
const userEnteredString = "12/20/1989"; // MM/DD/YYYY format const dateStringFromAPI = "1989-12-20T00:00:00Z"; const dateFromUserEnteredString = new Date(userEnteredString) const dateFromAPIString = new Date(dateStringFromAPI); if (dateFromUserEnteredString.getTime() == dateFromAPIString.getTime()) { transferOneMillionDollarsToUserAccount(); } else { doNothing(); }
Beide stellten das gleiche Datum dar, aber leider wird Ihr Benutzer die Million Dollar nicht erhalten.
Hier ist das Problem: JavaScript geht immer davon aus, dass die Zeitzone diejenige ist, die der Browser bereitstellt, sofern nicht ausdrücklich anders angegeben.
Das bedeutet für mich, dass new Date ("12/20/1989")
ein Datum 1989-12-20T00:00:00+5:45 oder 1989-12-19T18:15:00Z erstellt, das nicht dasselbe ist wie 1989-12-20T00:00:00Z in Bezug auf den Zeitstempel.
Es ist nicht möglich, nur die Zeitzone eines vorhandenen Datumsobjekts zu ändern, daher ist unser Ziel jetzt, ein neues Datumsobjekt zu erstellen, aber mit UTC anstelle der lokalen Zeitzone.
Wir ignorieren die Zeitzone des Benutzers und verwenden UTC beim Erstellen des Datumsobjekts. Es gibt zwei Möglichkeiten, dies zu tun:
- Erstellen Sie eine ISO-formatierte Datumszeichenfolge aus dem Benutzereingabedatum und verwenden Sie sie, um ein Date-Objekt zu erstellen. Verwenden eines gültigen ISO-Datumsformats zum Erstellen eines Datumsobjekts, während die Absicht von UTC im Vergleich zu lokal sehr deutlich wird.
const userEnteredDate = "12/20/1989"; const parts = userEnteredDate.split("/"); const userEnteredDateISO = parts[2] + "-" + parts[0] + "-" + parts[1]; const userEnteredDateObj = new Date(userEnteredDateISO + "T00:00:00Z"); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDateObj.getTime() == dateFromAPI.getTime(); // true
Dies funktioniert auch, wenn Sie die Zeit nicht angeben, da dies standardmäßig Mitternacht ist (dh 00:00:00Z):
const userEnteredDate = new Date("1989-12-20"); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDate.getTime() == dateFromAPI.getTime(); // true
Denken Sie daran: Wenn dem Datumskonstruktor ein String im korrekten ISO-Datumsformat JJJJ-MM-TT übergeben wird, nimmt er automatisch UTC an.
- JavaScript bietet eine nette Date.UTC()-Funktion, die Sie verwenden können, um den UTC-Zeitstempel eines Datums zu erhalten. Wir extrahieren die Komponenten aus dem Datum und übergeben sie an die Funktion.
const userEnteredDate = new Date("12/20/1989"); const userEnteredDateTimeStamp = Date.UTC(userEnteredDate.getFullYear(), userEnteredDate.getMonth(), userEnteredDate.getDate(), 0, 0, 0); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDateTimeStamp == dateFromAPI.getTime(); // true ...
Finde den Unterschied zwischen zwei Daten
Ein häufiges Szenario, auf das Sie stoßen werden, besteht darin, den Unterschied zwischen zwei Daten zu finden.
Wir diskutieren zwei Anwendungsfälle:
Ermitteln der Anzahl der Tage zwischen zwei Daten
Konvertieren Sie beide Daten in UTC-Zeitstempel, finden Sie die Differenz in Millisekunden und finden Sie die entsprechenden Tage.
const dateFromAPI = "2016-02-10T00:00:00Z"; const now = new Date(); const datefromAPITimeStamp = (new Date(dateFromAPI)).getTime(); const nowTimeStamp = now.getTime(); const microSecondsDiff = Math.abs(datefromAPITimeStamp - nowTimeStamp); // Math.round is used instead of Math.floor to account for certain DST cases // Number of milliseconds per day = // 24 hrs/day * 60 minutes/hour * 60 seconds/minute * 1000 ms/second const daysDiff = Math.round(microSecondsDiff / (1000 * 60 * 60 * 24)); console.log(daysDiff);
Ermitteln des Alters des Benutzers anhand seines Geburtsdatums
const birthDateFromAPI = "12/10/1989";
Hinweis: Wir haben ein nicht standardmäßiges Format. Lesen Sie das API-Dokument, um festzustellen, ob dies der 12. Oktober oder der 10. Dezember bedeutet. Wechseln Sie entsprechend zum ISO-Format.
const parts = birthDateFromAPI.split("/"); const birthDateISO = parts[2] + "-" + parts[0] + "-" + parts[1]; const birthDate = new Date(birthDateISO); const today = new Date(); let age = today.getFullYear() - birthDate.getFullYear(); if(today.getMonth() < birthDate.getMonth()) { age--; } if(today.getMonth() == birthDate.getMonth() && today.getDate() < birthDate.getDate()) { age--; }
Ich weiß, dass es kürzere Möglichkeiten gibt, diesen Code zu schreiben, aber ich schreibe ihn wegen der schieren Klarheit der Logik gerne so.
Vorschläge zur Vermeidung von Date Hell
Jetzt, da wir uns mit der Datumsarithmetik auskennen, sind wir in der Lage, die zu befolgenden Best Practices und die Gründe dafür zu verstehen.
Abrufen von DateTime vom Benutzer
Wenn Sie das Datum und die Uhrzeit vom Benutzer erhalten, suchen Sie höchstwahrscheinlich nach seiner lokalen DateTime. Wir haben im Abschnitt über die Datumsarithmetik gesehen, dass der Date
-Konstruktor das Datum auf verschiedene Arten akzeptieren kann.
Um Verwirrung zu vermeiden, schlage ich immer vor, ein Datum im new Date(year, month, day, hours, minutes, seconds, milliseconds)
zu erstellen, auch wenn Sie das Datum bereits in einem gültigen parsbaren Format haben. Wenn alle Programmierer in Ihrem Team diese einfache Regel befolgen, wird es extrem einfach, den Code auf lange Sicht zu warten, da er so explizit ist, wie Sie es mit dem Date
-Konstruktor sein können.
Der coole Teil ist, dass Sie die Variationen verwenden können, die es Ihnen ermöglichen, jeden der letzten vier Parameter wegzulassen, wenn sie Null sind; dh new Date(2012, 10, 12)
ist dasselbe wie new Date(2012, 10, 12, 0, 0, 0, 0)
da die nicht angegebenen Parameter standardmäßig auf Null gesetzt werden.
Wenn Sie beispielsweise eine Datums- und Zeitauswahl verwenden, die Ihnen das Datum 2012-10-12 und die Uhrzeit 12:30 anzeigt, können Sie die Teile extrahieren und wie folgt ein neues Datumsobjekt erstellen:
const dateFromPicker = "2012-10-12"; const timeFromPicker = "12:30"; const dateParts = dateFromPicker.split("-"); const timeParts = timeFromPicker.split(":"); const localDate = new Date(dateParts[0], dateParts[1]-1, dateParts[2], timeParts[0], timeParts[1]);
Versuchen Sie zu vermeiden, ein Datum aus einer Zeichenfolge zu erstellen, es sei denn, es handelt sich um ein ISO-Datumsformat. Verwenden Sie stattdessen die Methode Date(Jahr, Monat, Datum, Stunden, Minuten, Sekunden, Mikrosekunden).
Nur das Datum erhalten
Wenn Sie nur das Datum erhalten, z. B. das Geburtsdatum eines Benutzers, ist es am besten, das Format in ein gültiges ISO-Datumsformat zu konvertieren, um alle Zeitzoneninformationen zu eliminieren, die dazu führen können, dass sich das Datum nach vorne oder hinten verschiebt, wenn es in UTC konvertiert wird. Zum Beispiel:
const dateFromPicker = "12/20/2012"; const dateParts = dateFromPicker.split("/"); const ISODate = dateParts[2] + "-" + dateParts[0] + "-" + dateParts[1]; const birthDate = new Date(ISODate).toISOString();
Falls Sie es vergessen haben, wenn Sie ein Date
mit der Eingabe im gültigen ISO-Datumsformat (JJJJ-MM-TT) erstellen, wird es standardmäßig auf UTC anstatt auf die Zeitzone des Browsers eingestellt.
Speichern des Datums
Speichern Sie DateTime immer in UTC. Senden Sie immer einen ISO-Datumsstring oder einen Zeitstempel an das Backend.
Generationen von Computerprogrammierern haben nach bitteren Erfahrungen mit dem Versuch, dem Benutzer die korrekte Ortszeit anzuzeigen, diese einfache Wahrheit erkannt. Das Speichern der Ortszeit im Backend ist eine schlechte Idee, es ist besser, den Browser die Konvertierung in die Ortszeit im Frontend übernehmen zu lassen.
Außerdem sollte klar sein, dass Sie niemals einen DateTime-String wie „20. Juli 1989, 12:10 Uhr“ an das Backend senden sollten. Selbst wenn Sie die Zeitzone mitsenden, erhöhen Sie den Aufwand für andere Programmierer, Ihre Absichten zu verstehen und das Datum korrekt zu analysieren und zu speichern.
Verwenden Sie die toISOString()
oder toJSON()
des Date-Objekts, um die lokale DateTime in UTC zu konvertieren.
const dateFromUI = "12-13-2012"; const timeFromUI = "10:20"; const dateParts = dateFromUI.split("-"); const timeParts = timeFromUI.split(":"); const date = new Date(dateParts[2], dateParts[0]-1, dateParts[1], timeParts[0], timeParts[1]); const dateISO = date.toISOString(); $.post("http://example.com/", {date: dateISO}, ...)
Anzeige von Datum und Uhrzeit
- Rufen Sie den Zeitstempel oder das ISO-formatierte Datum von einer REST-API ab.
- Erstellen Sie ein
Date
-Objekt. - Verwenden Sie die
toLocaleString()
odertoLocaleDateString()
undtoLocaleTimeString()
oder eine Datumsbibliothek, um die Ortszeit anzuzeigen.
const dateFromAPI = "2016-01-02T12:30:00Z"; const localDate = new Date(dateFromAPI); const localDateString = localDate.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric', }); const localTimeString = localDate.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit', });
Wann sollten Sie auch die Ortszeit speichern?
„Manchmal ist es wichtig, die Zeitzone zu kennen, in der sich ein Ereignis ereignet hat, und durch die Umstellung auf eine einzelne Zeitzone werden diese Informationen unwiderruflich gelöscht.
„Wenn Sie eine Marketingaktion durchführen und wissen möchten, welche Kunden um die Mittagszeit herum Bestellungen aufgegeben haben, ist eine Bestellung, die scheinbar um 12:00 Uhr GMT aufgegeben wurde, nicht sehr hilfreich, wenn sie tatsächlich beim Frühstück in New York aufgegeben wurde.“
Wenn Sie auf eine solche Situation stoßen, wäre es klüger, auch die Ortszeit zu speichern. Wie üblich möchten wir das Datum im ISO-Format erstellen, müssen aber zuerst den Zeitzonen-Offset finden.
Die Funktion getTimeZoneOffset()
des Date-Objekts gibt uns die Anzahl der Minuten an, die, wenn sie zu einer bestimmten Ortszeit addiert werden, die entsprechende UTC-Zeit ergeben. Ich schlage vor, es in das Format (+-)hh:mm zu konvertieren, da es deutlicher macht, dass es sich um einen Zeitzonenversatz handelt.
const now = new Date(); const tz = now.gettime zoneOffset();
Für meine Zeitzone +05:45 bekomme ich -345, das ist nicht nur das entgegengesetzte Vorzeichen, sondern eine Zahl wie -345 könnte für einen Back-End-Entwickler völlig verwirrend sein. Also wandeln wir dies in +05:45 um.
const sign = tz > 0 ? "-" : "+"; const hours = pad(Math.floor(Math.abs(tz)/60)); const minutes = pad(Math.abs(tz)%60); const tzOffset = sign + hours + ":" + minutes;
Jetzt holen wir uns die restlichen Werte und erstellen einen gültigen ISO-String, der die lokale DateTime darstellt.
const localDateTime = now.getFullYear() + "-" + pad(now.getMonth()+1) + "-" + pad(now.getDate()) + "T" + pad(now.getHours()) + ":" + pad(now.getMinutes()) + ":" + pad(now.getSeconds());
Wenn Sie möchten, können Sie die UTC- und lokalen Daten in ein Objekt einschließen.
const eventDate = { utc: now.toISOString(), local: localDateTime, tzOffset: tzOffset, }
Wenn Sie nun im Backend herausfinden möchten, ob das Ereignis vor Mittag Ortszeit aufgetreten ist, können Sie das Datum analysieren und einfach die Funktion getHours()
verwenden.
const localDateString = eventDate.local; const localDate = new Date(localDateString); if(localDate.getHours() < 12) { console.log("Event happened before noon local time"); }
Wir haben den tzOffset
hier nicht verwendet, aber wir speichern ihn trotzdem, da wir ihn in Zukunft möglicherweise zu Debugging-Zwecken benötigen. Sie könnten eigentlich nur den Zeitzonenoffset und die UTC-Zeit senden. Aber ich speichere auch gerne die Ortszeit, weil Sie das Datum schließlich in einer Datenbank speichern müssen und die getrennte Speicherung der Ortszeit es Ihnen ermöglicht, direkt auf der Grundlage eines Felds abzufragen, anstatt Berechnungen durchführen zu müssen, um das Ortsdatum zu erhalten.
Manchmal möchten Sie, selbst wenn die lokale Zeitzone gespeichert ist, Datumsangaben in einer bestimmten Zeitzone anzeigen. Beispielsweise können Zeiten für Ereignisse in der Zeitzone des aktuellen Benutzers sinnvoller sein, wenn sie virtuell sind, oder in der Zeitzone, in der sie physisch stattfinden, wenn dies nicht der Fall ist. Es lohnt sich in jedem Fall, sich vorher etablierte Lösungen zur Formatierung mit expliziten Zeitzonennamen anzuschauen.
Server- und Datenbankkonfiguration
Konfigurieren Sie Ihre Server und Datenbanken immer so, dass sie die UTC-Zeitzone verwenden. (Beachten Sie, dass UTC und GMT nicht dasselbe sind – GMT kann beispielsweise im Sommer eine Umstellung auf BST bedeuten, während UTC dies niemals tun wird.)
Wir haben bereits gesehen, wie mühsam Zeitzonenumrechnungen sein können, besonders wenn sie unbeabsichtigt sind. Immer UTC DateTime zu senden und Ihre Server so zu konfigurieren, dass sie in der UTC-Zeitzone sind, kann Ihnen das Leben erleichtern. Ihr Back-End-Code wird viel einfacher und sauberer, da er keine Zeitzonenkonvertierungen durchführen muss. DateTime data coming in from servers across the world can be compared and sorted effortlessly.
Code in the back end should be able to assume the time zone of the server to be UTC (but should still have a check in place to be sure). A simple configuration check saves having to think about and code for conversions every time new DateTime code is written.
It's Time for Better Date Handling
Date manipulation is a hard problem. The concepts behind the practical examples in this article apply beyond JavaScript, and are just the beginning when it comes to properly handling DateTime data and calculations. Plus, every helper library will come with its own set of nuances—which is even true of the eventual official standard support{target=”_blank”} for these types of operations.
The bottom line is: Use ISO on the back end, and leave the front end to format things properly for the user. Professional programmers will be aware of some of the nuances, and will (all the more decidedly) use well-supported DateTime libraries on both the back end and the front end. Built-in functions on the database side are another story, but hopefully this article gives enough background to make wiser decisions in that context, too.