Lär dig hur du formaterar och konverterar tid i JavaScript utan överraskningar: tidsstämplar, ISO-strängar, tidszoner, sommartid, parsning och pålitliga mönster.

Tidsbuggar i JavaScript ser sällan ut som "klockan går fel". De visar sig som små förskjutningar: ett datum som är rätt på din laptop men fel på en kollegas maskin, ett API-svar som ser okej ut tills det renderas i en annan tidszon, eller en rapport som är "en dag fel" runt ett säsongsskifte.
Du märker oftast ett (eller flera) av följande:
+02:00) skiljer sig från vad du förväntade dig.En stor källa till problem är att ordet tid kan syfta på olika begrepp:
Javascripts inbyggda Date försöker täcka alla dessa, men representerar i grunden ett ögonblick i tiden samtidigt som den hela tiden lockar dig till lokal visning, vilket gör att oavsiktliga konverteringar blir lätta.
Guiden är praktiskt inriktad: hur du får förutsägbara konverteringar mellan webbläsare och servrar, hur du väljer säkrare format (som ISO 8601), och hur du hittar klassiska fallgropar (sekunder vs millisekunder, UTC vs lokal och parsningens skillnader). Målet är inte mer teori—utan färre "varför flyttade det?"-överraskningar.
Tidsbuggar i JavaScript börjar ofta med att blanda representationer som ser ut att vara utbytbara, men inte är det.
1) Epokmillisekunder (nummer)
Ett vanligt tal som 1735689600000 är vanligtvis "millisekunder sedan 1970-01-01T00:00:00Z". Det representerar ett ögonblick i tiden utan formatering eller tidszon.
2) Date-objekt (wrapper runt ett ögonblick)
En Date lagrar samma typ av ögonblick som en tidsstämpel. Det förvirrande: när du skriv ut en Date formaterar JavaScript den med miljöns lokala regler om du inte ber om något annat.
3) Formaterad sträng (visning för människa)
Strängar som "2025-01-01", "01/01/2025 10:00" eller "2025-01-01T00:00:00Z" är inte en enda sak. Vissa är entydiga (ISO 8601 med Z), andra beror på lokalinställningar, och vissa innehåller ingen tidszon alls.
Samma ögonblick kan visas olika beroende på tidszon:
const instant = new Date("2025-01-01T00:00:00Z");
instant.toLocaleString("en-US", { timeZone: "UTC" });
// "1/1/2025, 12:00:00 AM"
instant.toLocaleString("en-US", { timeZone: "America/Los_Angeles" });
// "12/31/2024, 4:00:00 PM" (föregående dag)
Välj en intern representation (vanligtvis epokmillisekunder eller UTC ISO 8601) och håll dig till den i hela appen och API:erna. Konvertera till/från Date och formaterade strängar bara i gränssnitten: indata-parsning och UI-visning.
En "tidsstämpel" betyder oftast epoktiden (också kallad Unix-tid): räkningen av tid sedan 1970-01-01 00:00:00 UTC. Fällan: olika system räknar i olika enheter.
JavaScripts Date` är källan till mesta förvirringen eftersom den använder millisekunder. Många API:er, databaser och loggar använder sekunder.
17040672001704067200000Samma ögonblick, men millisekundversionen har tre extra siffror.
Använd explicit multiplikation/division så enheten är tydlig:
// seconds -> Date
const seconds = 1704067200;
const d1 = new Date(seconds * 1000);
// milliseconds -> Date
const ms = 1704067200000;
const d2 = new Date(ms);
// Date -> seconds
const secondsOut = Math.floor(d2.getTime() / 1000);
// Date -> milliseconds
const msOut = d2.getTime();
Date()Det här ser rimligt ut, men är fel när ts är i sekunder:
const ts = 1704067200; // seconds
const d = new Date(ts); // WRONG: treated as milliseconds
Resultatet blir ett datum på 1970, eftersom 1,704,067,200 millisekunder är bara omkring 19 dagar efter epoken.
När du är osäker på vilken enhet du har, lägg in enkla skydd:
function asDateFromUnknownEpoch(x) {
// crude heuristic: seconds are ~1e9-1e10, milliseconds are ~1e12-1e13
if (x < 1e11) return new Date(x * 1000); // assume seconds
return new Date(x); // assume milliseconds
}
const input = Number(valueFromApi);
console.log({ input, digits: String(Math.trunc(input)).length });
console.log('as ISO:', asDateFromUnknownEpoch(input).toISOString());
Om "siffror"-antalet är ~10 är det troligen sekunder. Om det är ~13 är det sannolikt millisekunder. Skriv också ut toISOString() vid felsökning: det är entydigt och hjälper dig att snabbt se enhetsfel.
JavaScripts Date kan vara förvirrande eftersom den lagrar ett enda ögonblick i tiden, men kan presentera det ögonblicket i olika tidszoner.
Internt är en Date i princip "millisekunder sedan Unix-epoken (1970-01-01T00:00:00Z)". Det numret representerar ett ögonblick i UTC. "Skiftet" sker när du ber JavaScript formatera det ögonblicket som lokal tid (baserat på datorns/serverns inställningar) istället för UTC.
Många Date-API:er har både lokala och UTC-varianter. De returnerar olika siffror för samma ögonblick:
const d = new Date('2025-01-01T00:30:00Z');
d.getHours(); // hour in *local* time zone
d.getUTCHours(); // hour in UTC
d.toString(); // local time string
d.toISOString(); // UTC (always ends with Z)
Om din maskin är i New York (UTC-5) kan den UTC-tiden visas som "19:30" föregående dag lokalt. På en server satt till UTC visas den som "00:30". Samma ögonblick, olika visning.
Loggar använder ofta Date#toString() eller interpolerar en Date implicit, vilket använder miljöns lokala tidszon. Det betyder att samma kod kan skriva olika tidsstämplar på din laptop, i CI och i produktion.
Lagra och överför tid som UTC (t.ex. epokmillisekunder eller ISO 8601 med Z). Konvertera till användarens lokal endast vid visning:
toISOString() eller skicka epokmillisekunderIntl.DateTimeFormatOm du bygger en app snabbt (t.ex. med en vibe-coding-workflow i Koder.ai), hjälper det att baka in detta i dina genererade API-kontrakt tidigt: namnge fält tydligt (createdAtMs, createdAtIso) och håll server (Go + PostgreSQL) och klient (React) konsekventa i vad varje fält representerar.
Om du behöver skicka datum/tider mellan en webbläsare, en server och en databas är ISO 8601-strängar det säkraste standardvalet. De är explicita, brett stödda och (viktigast) de bär tidszonsinformation.
Två bra utbytesformat:
2025-03-04T12:30:00Z2025-03-04T12:30:00+02:00Vad betyder "Z"?
Z står för Zulu time, ett annat namn för UTC. Så 2025-03-04T12:30:00Z är "12:30 i UTC".
När spelar offsetar som +02:00 roll?
Offsetar är avgörande när en händelse är knuten till ett lokalt tidssammanhang (möten, bokningar, butikstider). 2025-03-04T12:30:00+02:00 beskriver ett ögonblick som är två timmar före UTC, och det är inte samma ögonblick som 2025-03-04T12:30:00Z.
Strängar som 03/04/2025 är en fälla: är det 4 mars eller 3 april? Olika användare och miljöer tolkar det olika. Föredra 2025-03-04 (ISO-datum) eller en full ISO-datatid.
const iso = "2025-03-04T12:30:00Z";
const d = new Date(iso);
const back = d.toISOString();
console.log(iso); // 2025-03-04T12:30:00Z
console.log(back); // 2025-03-04T12:30:00.000Z
Denna "round-trip"-beteende är precis vad du vill ha för API:er: konsekvent, förutsägbart och tidszonsmedvetet.
Date.parse() känns bekvämt: ge den en sträng, få tillbaka en tidsstämpel. Problemet är att för allt som inte är tydligt ISO 8601 kan parsningen förlita sig på webbläsarens heuristik. Dessa heuristiker har varierat mellan motorer och versioner, vilket innebär att samma indata kan parsas olika (eller inte alls) beroende på var koden körs.
Date.parse() kan varieraJavaScript standardiserar bara parsning pålitligt för ISO 8601–liknande strängar (och även där kan detaljer som tidszon spela roll). För "vänliga" format—som "03/04/2025", "March 4, 2025" eller "2025-3-4"—kan webbläsare tolka:
Om du inte kan förutsäga exakt strängform kan du inte förutsäga resultatet.
YYYY-MM-DDEn vanlig fälla är den rena datumformen "YYYY-MM-DD" (t.ex. "2025-01-15"). Många utvecklare förväntar sig att den tolkas som lokal midnatt. I praktiken behandlar vissa miljöer detta som UTC-midnatt.
Den skillnaden spelar roll: UTC-midnatt konverterad till lokal tid kan bli föregående dag i negativa tidszoner (t.ex. Amerika) eller flytta timmen oväntat. Det är ett enkelt sätt att få "varför är mitt datum en dag fel?"-buggar.
För server/API-indata:
2025-01-15T13:45:00Z eller 2025-01-15T13:45:00+02:00."YYYY-MM-DD") och undvik att konvertera till en Date om du inte också definierar avsedd tidszon.För användarinmatning:
03/04/2025 om inte din UI tvingar betydelsen.Istället för att lita på att Date.parse() "finner rätt", välj något av dessa mönster:
new Date(year, monthIndex, day) för lokala datum).När tidsdata är kritisk räcker inte "det parse:ar på min maskin"—gör parsningen explicit och konsekvent.
Om målet är "visa ett datum/tid som människor förväntar sig" är bästa verktyget i JavaScript Intl.DateTimeFormat. Den använder användarens lokalregler (ordning, separatorer, månadsnamn) och undviker det sköra tillvägagångssättet att manuellt bygga strängar som month + '/' + day.
Manuell formatering hårdkodar ofta US-stil, glömmer ledande nollor eller ger förvirrande 24/12-timmarsresultat. Intl.DateTimeFormat gör det också tydligt vilken tidszon du visar—viktigt när dina data är lagrade i UTC men UI ska spegla användarens lokala tid.
För "bara formatera fint" är dateStyle och timeStyle enklast:
const d = new Date('2025-01-05T16:30:00Z');
// User’s locale + user’s local time zone
console.log(new Intl.DateTimeFormat(undefined, {
dateStyle: 'medium',
timeStyle: 'short'
}).format(d));
// Force a specific time zone (great for event times)
console.log(new Intl.DateTimeFormat('en-GB', {
dateStyle: 'full',
timeStyle: 'short',
timeZone: 'UTC'
}).format(d));
Om du behöver konsekventa timcykler (t.ex. en inställning för 12/24-timmar), använd hour12:
console.log(new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
hour12: true
}).format(d));
Välj en formateringsfunktion per "typ" av tidsstämpel i din UI (meddelandetid, loggpost, eventstart) och gör timeZone-beslutet medvetet:
Det ger konsekvent, lokalvänlig output utan att du måste underhålla ett skört set med egna formatsträngar.
Sommartid (DST) är när en tidszon ändrar sin UTC-offset (vanligen en timme) på specifika datum. Det knepiga är att DST inte bara "ändrar offset"—det förändrar existensen av vissa lokala tider.
När klockan vrids fram händer ett intervall av lokala tider aldrig. Till exempel hoppar klockan ofta från 01:59 till 03:00, så 02:30 lokal tid är "saknad".
När klockan vrids tillbaka händer ett intervall av lokala tider två gånger. Till exempel kan 01:30 inträffa en gång före skiftet och en gång efter, vilket betyder att samma väggklocktid kan motsvara två olika ögonblick.
Dessa är inte ekvivalenta runt DST-gränser:
Om DST börjar i natt kan "i morgon kl 09:00" vara bara 23 timmar bort. Om DST slutar kan det vara 25 timmar.
// Scenario: schedule “same local time tomorrow”
const d = new Date(2025, 2, 8, 9, 0); // Mar 8, 9:00 local
const plus24h = new Date(d.getTime() + 24 * 60 * 60 * 1000);
const nextDaySameLocal = new Date(d);
nextDaySameLocal.setDate(d.getDate() + 1);
// Around DST, plus24h and nextDaySameLocal can differ by 1 hour.
setHours kan överraskaOm du gör något som date.setHours(2, 30, 0, 0) på en dag då klockan "vrids fram", kan JavaScript normalisera det till en annan giltig tid (ofta 03:30), eftersom 02:30 inte existerar i lokal tid.
setDate) snarare än att lägga till millisekunder.Z så att ögonblicket är entydigt.En vanlig källa till buggar är att använda Date för något som inte är ett kalenderögonblick.
En tidsstämpel svarar på "när hände det?" (ett specifikt ögonblick som 2025-12-23T10:00:00Z). En duration svarar på "hur länge?" (som "3 minuter 12 sekunder"). Dessa är olika begrepp, och att blanda dem leder till förvirrande beräkningar och oväntade tidszons-/DST-effekter.
Date är fel verktyg för durationerDate representerar alltid en punkt på tidslinjen relativt en epok. Om du lagrar "90 sekunder" som en Date lagrar du egentligen "1970-01-01 plus 90 sekunder" i en viss tidszon. Formatering kan då plötsligt visa 01:01:30, skifta en timme eller plocka upp ett datum du aldrig avsåg.
För durationer, föredra enkla nummer:
HH:mm:ssHär är en enkel formatterare som fungerar för nedräkningar och mediaslängder:
function formatHMS(totalSeconds) {
const s = Math.max(0, Math.floor(totalSeconds));
const hh = String(Math.floor(s / 3600)).padStart(2, "0");
const mm = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
const ss = String(s % 60).padStart(2, "0");
return `${hh}:${mm}:${ss}`;
}
formatHMS(75); // "00:01:15" (nedräkning)
formatHMS(5423); // "01:30:23" (media duration)
Om du konverterar från minuter, multiplicera först (minutes * 60) och håll värdet numeriskt tills du renderar det.
När du jämför tider i JavaScript är det säkraste att jämföra nummer, inte formaterad text. Ett Date-objekt är i grunden en wrapper runt en numerisk tidsstämpel (epokmillisekunder), så du vill att jämförelser slutar som "nummer vs nummer".
Använd getTime() (eller Date.valueOf(), som returnerar samma nummer) för pålitliga jämförelser:
const a = new Date('2025-01-10T12:00:00Z');
const b = new Date('2025-01-10T12:00:01Z');
if (a.getTime() < b.getTime()) {
// a is earlier
}
// Also works:
if (+a < +b) {
// unary + calls valueOf()
}
Undvik att jämföra formaterade strängar som "1/10/2025, 12:00 PM"—de är lokala och sorterar inte korrekt. Huvudundantaget är ISO 8601-strängar i samma format och tidszon (t.ex. alla ...Z), som går att sortera lexikografiskt.
Sortering efter tid är enkelt om du sorterar efter epokmillisekunder:
items.sort((x, y) => new Date(x.createdAt).getTime() - new Date(y.createdAt).getTime());
Filtrering av objekt inom ett intervall är samma idé:
const start = new Date('2025-01-01T00:00:00Z').getTime();
const end = new Date('2025-02-01T00:00:00Z').getTime();
const inRange = items.filter(i => {
const t = new Date(i.createdAt).getTime();
return t >= start && t < end;
});
"Dagens början" beror på om du menar lokal tid eller UTC:
// Local start/end of day
const d = new Date(2025, 0, 10); // Jan 10 in local time
const localStart = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
const localEnd = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
// UTC start/end of day
const utcStart = new Date(Date.UTC(2025, 0, 10, 0, 0, 0, 0));
const utcEnd = new Date(Date.UTC(2025, 0, 10, 23, 59, 59, 999));
Välj en definition tidigt och håll dig till den i dina jämförelser och intervall-logik.
Tidsbuggar känns slumpmässiga tills du fastställer vad du har (tidsstämpel? sträng? Date?) och var förskjutningen introduceras (parsning, tidszonskonvertering, formatering).
Börja med att logga samma värde på tre olika sätt. Det visar snabbt om problemet är sekunder vs millisekunder, lokal vs UTC, eller strängparsning.
console.log('raw input:', input);
const d = new Date(input);
console.log('toISOString (UTC):', d.toISOString());
console.log('toString (local):', d.toString());
console.log('timezone offset (min):', d.getTimezoneOffset());
Vad du ska leta efter:
toISOString() är helt fel (t.ex. år 1970 eller långt i framtiden), misstänk sekunder vs millisekunder.toISOString() ser rätt men toString() är "förskjuten", ser du en lokal tidszonsvisnings-fråga.getTimezoneOffset() ändras beroende på datum, korsar du sommartid.Många "det funkar på min maskin"-rapporter beror på olika miljöinställningar.
console.log(Intl.DateTimeFormat().resolvedOptions());
console.log('TZ:', process.env.TZ);
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);
Om din server kör i UTC men din laptop i lokal zon, kommer formaterad output skilja sig om du inte anger timeZone explicit.
Skapa enhetstester runt DST-gränser och "edge"-tider:
23:30 → 00:30-överkorsningarOm du itererar snabbt, överväg att göra dessa tester till en del av din scaffolding. Till exempel, när du genererar en React + Go-app i Koder.ai kan du lägga in en liten "tidskontrakts"-testsvit tidigt (API-exempel + parsning/formatering-assertioner) så regressionsfel fångas innan deployment.
"2025-03-02 10:00".locale och (när det behövs) timeZone.Pålitlig tidshantering i JavaScript handlar mest om att välja en "sanningens källa" och vara konsekvent från lagring till visning.
Lagra och beräkna i UTC. Behandla användarens lokala tid som en presentationsdetalj.
Skicka datum mellan system som ISO 8601-strängar med en explicit offset (helst Z). Om du måste skicka numeriska epoker, dokumentera enheten och håll den konsekvent (millisekunder är standard i JS).
Formatera för människor med Intl.DateTimeFormat (eller toLocaleString), och ange en explicit timeZone när du behöver deterministisk output (t.ex. alltid visa tider i UTC eller i en viss affärsregion).
Z (t.ex. 2025-12-23T10:15:00Z). Om du använder epoker, inkludera ett fältnamn som createdAtMs för att göra enheter uppenbara.Överväg ett dedikerat datum-tidsbibliotek om du behöver återkommande händelser, komplexa tidszonsregler, DST-säker aritmetik ("samma lokala tid i morgon") eller mycket parsning från inkonsekventa indata. Värdet ligger i klarare API:er och färre kantfall.
Om du vill fördjupa dig, bläddra fler tidsrelaterade guider på /blog. Om du utvärderar verktyg eller supportalternativ, se /pricing.