Data i czas w JavaScript – obiekt Date, formatowanie i operacje

JavaScript, jako język działający w różnych środowiskach – od przeglądarek po serwery Node.js – musi oferować solidne narzędzia do pracy z datą i czasem. Obiekt Date reprezentuje konkretny moment jako liczbę milisekund od północy 1 stycznia 1970 roku (UTC), znaną jako epoka Unix.

Mimo fundamentalnej roli w ekosystemie webowym, Date bywa trudny w użyciu z powodu nieintuicyjnych zachowań i pułapek. Ten przewodnik pokazuje, jak tworzyć obiekty Date, pobierać i ustawiać ich komponenty, formatować je, wykonywać obliczenia oraz unikać typowych błędów — ze szczególnym naciskiem na strefy czasowe, wewnętrzną reprezentację czasu i praktyczne zastosowania.

Fundamenty obiektu Date w JavaScript

Jak JavaScript reprezentuje daty wewnętrznie

Obiekt Date opiera się na jednym numerze: milisekundach, które upłynęły od 1 stycznia 1970, 00:00:00 UTC. Wszystkie operacje sprowadzają się do manipulacji tym znacznikiem czasu (timestamp). Data w JavaScript to zawsze data i czas jednocześnie, a zakres możliwych wartości jest ogromny (od 271821 p.n.e. do 275760 n.e.).

Tworząc new Date() bez parametrów, otrzymujesz bieżący moment na podstawie czasu systemowego. Metody takie jak getFullYear(), getMonth() czy getTime() prezentują różne widoki tej samej liczby milisekund.

Aby szybko utrwalić kluczowe fakty o reprezentacji czasu w Date, zwróć uwagę na poniższe punkty:

  • timestamp (ms od epoki) – jedyne źródło prawdy o chwili w czasie dla obiektu Date;
  • data i czas – obiekt zawsze przechowuje pełny moment (data+czas), nie samą datę;
  • strefy czasowe – odczyty mogą być interpretowane jako lokalne lub UTC w zależności od użytych metod;
  • zakres – bardzo szeroki przedział obsługiwanych dat, praktycznie wystarczający dla aplikacji produkcyjnych.

Architektura obiektu Date i jego metody

Instancja Date jest „statyczna”: po utworzeniu jej wartość się nie zmienia. Aby uzyskać aktualny czas, musisz utworzyć nowy obiekt Date lub użyć Date.now(). API oferuje kilkadziesiąt metod, zgrupowanych na pobierające (get) i ustawiające (set), w wersjach lokalnych i UTC.

Dla każdego składnika istnieją odpowiedniki lokalne i UTC, np. getFullYear() (lokalnie) oraz getUTCFullYear() (UTC). Różnice mogą być istotne przy współpracy między systemami i użytkownikami w wielu strefach.

Tworzenie obiektów Date – konstruktory i różne formaty

Podstawowe sposoby inicjalizacji

Istnieją cztery główne sposoby tworzenia nowej daty. Oto skrótowy przegląd:

  1. Bez parametrównew Date() zwraca bieżący moment w czasie;
  2. Z łańcuchem znakównew Date("2022-03-25"), najlepiej w ISO 8601 (np. YYYY-MM-DDTHH:mm:ss.sssZ);
  3. Z komponentównew Date(2018, 11, 24, 10, 33, 30, 0) (uwaga: miesiące 0–11);
  4. Z milisekund od epokinew Date(86400000) daje „1 stycznia 1970 + 24 h”.

Uwaga: liczba przekazanych argumentów do konstruktora wpływa na interpretację (możesz pominąć końcowe parametry, a brakujące przyjmują wartości domyślne, np. godzina 00:00:00).

Krytyczne różnice między formatami łańcuchów

Parsowanie łańcuchów dat historycznie bywało niejednoznaczne i różne silniki mogły zachowywać się odmiennie. Stosuj ISO 8601 zawsze, gdy to możliwe. Pamiętaj też, że łańcuch z samą datą w ISO (np. "2023-07-13") jest interpretowany jako UTC, nie czas lokalny.

Dla szybkiego rozeznania w zachowaniach typowych formatów spójrz na porównanie:

Format Przykład Niezawodność Strefa domyślna Uwagi
ISO data+czas 2023-07-13T10:30:00Z Wysoka UTC zalecany do serializacji i wymiany danych
ISO sama data 2023-07-13 Wysoka UTC może zaskoczyć, bo nie używa lokalnej strefy
MM/DD/YYYY 03/25/2015 Niska Lokalna często jako format amerykański, ale niegwarantowane
YYYY/MM/DD 2015/03/25 Niska Lokalna zachowanie niezdefiniowane, zależne od silnika
DD-MM-YYYY 25-03-2015 Niska Lokalna różne interpretacje, unikaj
Tekstowe 25 Mar 2015 Średnia Lokalna często działa, ale nadal lepiej użyć ISO

Metoda Date.parse() i jej zastosowanie

Metoda Date.parse() zwraca liczbę ms od epoki dla przekazanego łańcucha lub NaN przy błędzie. To szybki sposób walidacji i uzyskania znacznika czasu bez tworzenia instancji Date.

let msec = Date.parse("March 21, 2012");
console.log(msec); // liczba ms od epoki (zależna od interpretacji strefy)

Po uzyskaniu milisekund możesz przekazać je do new Date(msec) i kontynuować pracę na obiekcie.

Pobieranie komponentów daty – metody get

Metody pobierające w lokalnym czasie

getFullYear() zwraca rok (np. 1995) — preferowane zamiast przestarzałego getYear(). getMonth() zwraca miesiąc 0–11 (0 = styczeń). To częste źródło błędów. Pozostałe metody zwracają: getDate() (1–31), getDay() (0–6; 0 = niedziela), getHours() (0–23), getMinutes() (0–59), getSeconds() (0–59), getMilliseconds() (0–999).

Dla szybkiej orientacji w często używanych metodach odczytu skorzystaj z tej ściągi:

  • getFullYear() – czterocyfrowy rok w czasie lokalnym;
  • getMonth() – miesiąc 0–11 (0 = styczeń);
  • getDate() – dzień miesiąca 1–31;
  • getDay() – dzień tygodnia 0–6 (0 = niedziela);
  • getHours() – godzina 0–23;
  • getMinutes() – minuty 0–59;
  • getSeconds() – sekundy 0–59;
  • getMilliseconds() – milisekundy 0–999.

Odpowiadające metody UTC

Każda metoda ma wariant UTC, np. getUTCFullYear(). Są niezbędne w aplikacjach globalnych, gdzie standaryzacja do UTC zapobiega błędom. getTimezoneOffset() zwraca przesunięcie w minutach względem UTC (UTC−8 → 480, UTC+3 → −180).

Praktyczne przykłady pobierania komponentów

Poniżej pokazano, jak pobrać numer dnia tygodnia:

const now = new Date();
const element = document.querySelector(".element");
element.innerText = `Dzisiaj jest dzień tygodnia: ${now.getDay()}`; // dni tygodnia są liczone od 0

Aby wyświetlić nazwę dnia tygodnia, użyj tablicy nazw:

const now = new Date();
const days = ["niedziela", "poniedziałek", "wtorek", "środa", "czwartek", "piątek", "sobota"];
const element = document.querySelector(".element");
element.innerText = `Dzisiaj jest: ${days[now.getDay()]}`;

Aby wyświetlić kompletną datę i czas z zerami wiodącymi, możesz użyć prostej funkcji pomocniczej:

const now = new Date();
function lz(i) {
return `${i}`.padStart(2, "0");
}
const textDate = `${lz(now.getDate())}.${lz(now.getMonth() + 1)}.${now.getFullYear()}`;
const textTime = `${lz(now.getHours())}:${lz(now.getMinutes())}:${lz(now.getSeconds())}`;
console.log(textDate); // np. "10.02.2026"
console.log(textTime); // np. "19:30:45"

Ustawianie komponentów daty – metody set

Podstawowe metody ustawiające

Zmiany można wprowadzać metodami: setFullYear() (opcjonalnie także miesiąc i dzień), setMonth() (0–11), setDate() (1–31), setHours() (0–23), setMinutes() (0–59), setSeconds() (0–59), setMilliseconds() (0–999). Wszystkie modyfikują obiekt in place i zwracają nowy znacznik czasu (ms).

Przykład sekwencji zmian komponentów:

const d = new Date("January 01, 2025");
d.setFullYear(2020); // 1 stycznia 2020
d.setMonth(11); // 1 grudnia 2020
d.setDate(15); // 15 grudnia 2020
d.setHours(22, 10, 20); // 22:10:20

Dla szybkiego przeglądu metod ustawiających użyj listy poniżej:

  • setFullYear(year[, month, date]) – zmienia rok (i opcjonalnie miesiąc oraz dzień);
  • setMonth(month[, date]) – zmienia miesiąc 0–11 (opcjonalnie dzień miesiąca);
  • setDate(date) – zmienia dzień miesiąca 1–31 (obsługuje wartości poza zakresem);
  • setHours(h[, m, s, ms]) – zmienia godzinę (i opcjonalnie minuty, sekundy, ms);
  • setMinutes(m[, s, ms]) – zmienia minuty (i opcjonalnie sekundy, ms);
  • setSeconds(s[, ms]) – zmienia sekundy (i opcjonalnie ms);
  • setMilliseconds(ms) – zmienia milisekundy 0–999;
  • setTime(ms) – ustawia bezpośrednio timestamp w ms.

Dodawanie i odejmowanie czasu

Wygodny sposób dodawania dni to użycie setDate() i automatycznego przenoszenia do kolejnych miesięcy:

const d = new Date("January 01, 2025");
d.setDate(d.getDate() + 50); // dodaje 50 dni
// Wynik: 20 lutego 2025

Odejmowanie działa analogicznie:

const d = new Date("March 15, 2025");
d.setDate(d.getDate() - 10); // odejmuje 10 dni
// Wynik: 5 marca 2025

Dodawanie miesięcy:

const d = new Date("January 15, 2025");
d.setMonth(d.getMonth() + 6); // dodaje 6 miesięcy
// Wynik: 15 lipca 2025

Gdy potrzebujesz operować na milisekundach, użyj setTime():

const d = new Date();
const ONE_WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000;
d.setTime(d.getTime() + ONE_WEEK_IN_MS); // dodaje dokładnie jeden tydzień

Zwracanie wartości i automatyczne dostosowywanie

Metody set zwracają nowy timestamp (ms), a zmieniają istniejący obiekt. Przy wartościach poza zakresem następuje automatyczne dostosowanie (np. 32 czerwca → 2 lipca). Dla nieprawidłowych argumentów (NaN) powstaje Invalid Date.

Oto przykłady automatycznych korekt:

const d1 = new Date("June 15, 2025");
d1.setDate(32); // Wynik: 2 lipca 2025

const d2 = new Date("June 15, 2025");
d2.setDate(0); // Wynik: 31 maja 2025 (0 = ostatni dzień poprzedniego miesiąca)

Formatowanie i konwertowanie dat – różne metody reprezentacji

Wbudowane metody toString

toString() zwraca pełną reprezentację daty wraz ze strefą, np. Tue Aug 19 1975 23:15:30 GMT+0200 (CEST). toDateString() zwraca samą datę, a toTimeString() — sam czas (wraz z oznaczeniem strefy). toUTCString() zawsze formatuje w UTC.

Format ISO 8601 i toISOString()

Metoda toISOString() zwraca łańcuch w ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ) i zawsze w UTC. To preferowana metoda serializacji do JSON i integracji z API.

Przykład użycia toISOString():

const event = new Date("05 October 2011 14:48 UTC");
console.log(event.toISOString()); // "2011-10-05T14:48:00.000Z"

Metody locale – formatowanie wrażliwe na język

toLocaleString(), toLocaleDateString() i toLocaleTimeString() formatują wynik zgodnie z lokalizacją i opcjami, np. styl daty, język, strefa czasowa.

Przykłady formatowania lokalnego:

const d = new Date();
console.log(d.toLocaleDateString()); // Np. w USA: "12/11/2012", w Niemczech: "11.12.2012"

const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
const options = { weekday: "long", year: "numeric", month: "long", day: "numeric" };
console.log(date.toLocaleDateString("de-DE", options)); // "Donnerstag, 20. Dezember 2012"

Porównanie popularnych metod formatowania

Poniższa tabela ułatwia szybki dobór metody do celu wyświetlania:

Metoda Zakres Strefa Przykładowy wynik Typowe użycie
toString() data + czas lokalna Tue Aug 19 1975 23:15:30 GMT+0200 (CEST) debug, szybki podgląd
toDateString() data lokalna Thu Apr 12 2018 wyświetlanie samej daty
toTimeString() czas lokalna 23:15:30 GMT+0200 (CEST) wyświetlanie samego czasu
toUTCString() data + czas UTC Wed Aug 19 1975 15:15:30 GMT+0000 (UTC) standaryzacja, logi
toISOString() data + czas UTC 2011-10-05T14:48:00.000Z serializacja, API, JSON
toLocaleDateString() data lokalna/wybrana 11.12.2012 UI zależne od języka

Intl.DateTimeFormat – zaawansowane formatowanie

Intl.DateTimeFormat daje maksymalną kontrolę nad składnikami i strefą czasową, z zachowaniem reguł lokalizacji.

Przykłady użycia Intl.DateTimeFormat:

const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// Formatowanie dla "en-US"
console.log(new Intl.DateTimeFormat("en-US").format(date)); // "12/20/2020"
// Formatowanie dla "de-DE" z inną strefą czasową
console.log(new Intl.DateTimeFormat("de-DE", {
dateStyle: "full",
timeStyle: "long",
timeZone: "Australia/Sydney",
}).format(date)); // "Sonntag, 20. Dezember 2020 um 14:23:16 GMT+11"

Operacje na datach – porównywanie, odejmowanie i obliczenia

Porównywanie dat przy użyciu operatorów

Operatory relacyjne (<, >, <=, >=) działają „wartościowo”, bo Date konwertuje się do timestampu. Operatory równości (===) porównują referencje obiektów, więc dla równości porównuj getTime().

Przykładowe porównanie relacyjne:

let date1 = new Date();
let date2 = new Date("6/01/2022");
if (date1 > date2) {
console.log("Data 1 jest późniejsza niż data 2");
} else if (date1 < date2) { console.log("Data 1 jest wcześniejsza niż data 2"); } else { console.log("Obie daty są takie same"); }

Metoda getTime() dla porównań równości

Aby sprawdzić, czy dwie daty oznaczają ten sam moment, porównuj wartości getTime().

Przykład porównania równości:

let date1 = new Date("12/01/2021");
let date2 = new Date("12/01/2021");
console.log(date1.getTime() === date2.getTime()); // true

Obliczanie różnic między datami

Odejmij timestampy, a następnie przelicz jednostki (ms → sekundy → minuty → godziny → dni).

Przykład obliczania pozostałego czasu do konkretnej daty:

const dateNow = new Date();
const dateInTheFuture = new Date("2024-12-31");
const msRemaining = dateInTheFuture.getTime() - dateNow.getTime();

Konwersja różnicy na czytelne jednostki:

// Dni
const days = Math.floor(msRemaining / (1000 * 60 * 60 * 24));
console.log(`Pozostało dni: ${days}`);
// Godziny
const hours = Math.floor((msRemaining % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
console.log(`Pozostało godzin: ${hours}`);
// Minuty
const minutes = Math.floor((msRemaining % (1000 * 60 * 60)) / (1000 * 60));
console.log(`Pozostało minut: ${minutes}`);

86400000 to liczba milisekund w 24 godzinach (24 × 60 × 60 × 1000).

Praktyczne przykłady – obliczanie wieku

Przykład uwzględniający, czy urodziny już były w bieżącym roku:

function calculateAge(birthDateString) {
const birthDate = new Date(birthDateString);
const today = new Date();
let age = today.getFullYear() - birthDate.getFullYear();
const monthDifference = today.getMonth() - birthDate.getMonth();
if (monthDifference < 0 || (monthDifference === 0 && today.getDate() < birthDate.getDate())) { age--; } return age; } console.log(calculateAge("1990-05-15"));

Pułapki, problemy i najlepsze praktyki

Problem zerowo indeksowanych miesięcy

Miesiące są indeksowane od zera (styczeń = 0, grudzień = 11). Przy wyświetlaniu dodawaj 1 do getMonth(), a przy tworzeniu z komponentów pamiętaj o indeksacji.

Przykłady użycia miesiąca w odczycie i tworzeniu:

const now = new Date();
const humanReadableMonth = now.getMonth() + 1;
console.log(`Bieżący miesiąc: ${humanReadableMonth}`);
// To tworzy 15 marca (nie kwietnia), ponieważ marzec = 2
const date = new Date(2025, 2, 15);

Problemy ze strefami czasowymi

Daty są domyślnie interpretowane w lokalnej strefie systemu. Najlepsza praktyka: pracuj wewnętrznie w UTC, a do lokalnego czasu konwertuj tylko przy wyświetlaniu.

Przykład bezpiecznej pracy z UTC i prezentacji lokalnej:

const date = new Date("2025-01-15T12:30:00Z"); // Z = UTC
console.log(date.toUTCString()); // zawsze UTC
console.log(date.toLocaleString()); // lokalna prezentacja

Nieprawidłowe daty i automatyczne dostosowanie

JavaScript nie zgłasza błędu dla wielu „nieistniejących” dat, tylko je koryguje. Może to być przydatne lub zdradliwe — zależnie od kontekstu.

Przykład automatycznej korekty 31 lutego:

const date = new Date(2022, 1, 31); // luty nie ma 31 dni
console.log(date); // Wed Mar 03 2022 00:00:00 GMT+0100

Pułapka new Date(null)

new Date(null) zwraca epokę Unix (1970-01-01T00:00:00.000Z), a nie błąd. Natomiast new Date(undefined) daje Invalid Date. Zawsze jawnie sprawdzaj null i undefined przed tworzeniem daty.

Przykłady zachowania konstruktora:

new Date(null); // Thu Jan 01 1970 00:00:00 GMT+0000
new Date(undefined); // Invalid Date

Date.now() vs new Date()

Date.now() jest szybsze i zwraca sam timestamp (przydatne do pomiarów), a new Date() daje obiekt z bogatszym API.

Przykład pomiaru czasu wykonania:

const start = Date.now();
doSomethingForALongTime();
const end = Date.now();
const elapsed = end - start; // różnica w ms

Dla wygodnej nawigacji po typowych pułapkach i rekomendacjach zwróć uwagę na tę listę:

  • miesiące 0–11 – zawsze pamiętaj o indeksacji przy tworzeniu i odczycie;
  • UTC vs lokalny – do przechowywania/serializacji używaj UTC, do prezentacji lokalnych ustawień;
  • parsowanie łańcuchów – preferuj ISO 8601, unikaj niejednoznacznych formatów;
  • automatyczne korekty – JavaScript „przenosi” wartości poza zakresem na kolejne/poprzednie miesiące;
  • null vs undefinednew Date(null) ≠ błąd, new Date(undefined)Invalid Date;
  • wydajność – do znaczników czasu używaj Date.now(), do pracy na komponentach – new Date().

Nowoczesne alternatywy – date-fns i inne biblioteki

Ograniczenia natywnego obiektu Date

Natywne API bywa niewygodne (mutowalne obiekty, brak deklaratywnych operacji na datach). Brakuje ergonomicznych funkcji „dodaj dni/miesiące”, „różnica w słowach” itp.

Wprowadzenie date-fns

date-fns to modularna, lekka i niemutowalna biblioteka z ponad 200 funkcjami. Każda operacja zwraca nową datę, co ogranicza błędy stanu. Posiada szerokie wsparcie lokalizacji i jest znacznie mniejsza niż Moment.js.

Przykładowe użycie date-fns:

import { format, addDays, subDays } from "date-fns";
const today = new Date();
const inFiveDays = addDays(today, 5);
const formatted = format(inFiveDays, "yyyy-MM-dd");
console.log(formatted);

Inne alternatywy – Day.js i Temporal

Day.js to bardzo lekka biblioteka (ok. 2 kB) z API podobnym do Moment.js, ale szybsza i modularna. Temporal to nowe API JavaScript (osobna przestrzeń nazw), które rozwiązuje wiele problemów Date — w trakcie wdrożeń w środowiskach.

Dla szybkiego porównania najpopularniejszych opcji spójrz na tabelę:

Rozwiązanie Rozmiar Mutowalność Obsługa i18n Status
date-fns niski (funkcje per import) niemutowalne tak (wiele lokalizacji) stabilne, aktywnie rozwijane
Day.js bardzo niski (~2 kB) niemutowalne (chainable) tak (pluginy) stabilne, popularne
Moment.js wysoki (~232 kB) mutowalne tak legacy, maintenance mode
Temporal niemutowalne standardowe API nowe API JS (wdrożenia trwają)

Zaawansowane operacje – harmonogram i integracja z API

Harmonogramowanie zdarzeń z setTimeout i setInterval

setTimeout() uruchamia funkcję po określonej liczbie ms, a setInterval() powtarza ją periodycznie.

Przykłady ustawiania opóźnienia i interwału:

// Ustaw alarm na 5 sekund od teraz
setTimeout(() => {
console.log("Minęło 5 sekund");
}, 5000);

// Wyświetl godzinę co sekundę
const timerId = setInterval(() => {
const d = new Date();
document.getElementById("demo").innerHTML = d.toLocaleTimeString();
}, 1000);

// Aby zatrzymać, użyj clearInterval(timerId)

Serializacja dat do JSON

Przy JSON.stringify() obiekty Date konwertują się do ISO 8601 przez toJSON(). Przy deserializacji musisz ręcznie utworzyć Date z łańcucha.

Przykłady serializacji i deserializacji:

const date = new Date(2006, 0, 2, 15, 4, 5);
const jsonString = JSON.stringify(date);
console.log(jsonString); // "2006-01-02T15:04:05.000Z"

const iso = JSON.parse(jsonString); // "2006-01-02T15:04:05.000Z"
const parsed = new Date(iso);
console.log(parsed); // Mon Jan 02 2006 16:04:05 GMT+0100 (w zależności od strefy)

Konwersja milisekund na datę

API często zwracają timestamp w milisekundach. Wystarczy przekazać liczbę do konstruktora, aby uzyskać Date.

Przykład konwersji ms → Date:

let milliseconds = 1578567991011;
let dateFromMs = new Date(milliseconds);
console.log(dateFromMs.toString()); // np. "Thu Jan 09 2020 11:06:31 GMT+0000"

Walidacja i sprawdzanie poprawności dat

Sprawdzanie, czy data jest ważna

Najprostsza walidacja to sprawdzenie, czy getTime() nie zwraca NaN. Alternatywnie użyj Date.parse(), pamiętając o różnicach w parsowaniu nie-ISO.

Przykłady funkcji walidujących:

function isValidDate(dateString) {
const date = new Date(dateString);
return !isNaN(date.getTime());
}
console.log(isValidDate("2022-01-01")); // true
console.log(isValidDate("invalid")); // false

function isValidDateParse(dateString) {
return !isNaN(Date.parse(dateString));
}

Programista i twórca serwisu Creative Coding, absolwent Politechniki Warszawskiej (WEiTI). Od 10+ lat łączy front‑end, grafikę generatywną i narzędzia dla twórców; opublikował 120+ projektów i artykułów, prowadził warsztaty dla 2 000+ uczestników. Pracuje z JavaScriptem, Three.js, P5.js i GLSL, bada wydajność i dokumentuje procesy, tworząc praktyczne przewodniki dla osób łączących kod z obrazem, dźwiękiem i interakcją.
Zostaw komentarz

Komentarze

Brak komentarzy. Dlaczego nie rozpoczniesz dyskusji?

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *