DATEADD w SQL Serverze bez pułapek końca miesiąca

Przemysław Kwiatkowski 17 czerwca 2026
Ręka pisze markerem na ekranie słowo SQL, obok ikony symbolizujące funkcje, np. DATEADD w SQL.

Spis treści

Dodanie 7 dni do daty, wyznaczenie terminu wygaśnięcia abonamentu albo pobranie rekordów z ostatniego miesiąca brzmi prosto, dopóki nie pojawią się końce miesięcy, godziny i różnice między silnikami baz danych. Funkcja DATEADD rozwiązuje te problemy w SQL Serverze, a poniżej pokazuję jej składnię, praktyczne zastosowania, ograniczenia oraz odpowiedniki w PostgreSQL i MySQL.

Najważniejsze informacje o dodawaniu czasu w SQL

  • DATEADD dodaje lub odejmuje wskazaną liczbę jednostek od daty.
  • Podstawowa składnia SQL Servera to DATEADD(datepart, number, date).
  • Liczba ujemna pozwala łatwo cofnąć datę, na przykład o 30 dni.
  • Dodanie miesiąca do 31 stycznia może zakończyć się datą 28 lub 29 lutego.
  • W PostgreSQL używa się zwykle operatora + i wartości INTERVAL, a w MySQL funkcji DATE_ADD.

Jak działa DATEADD w SQL Serverze

DATEADD przyjmuje trzy argumenty. Pierwszy określa jednostkę czasu, drugi mówi, ile jednostek trzeba dodać, a trzeci jest datą wejściową. Funkcja nie zmienia istniejącej wartości, tylko zwraca nową datę, którą można wyświetlić, zapisać albo wykorzystać w warunku.

DATEADD(datepart, number, date)

Najprostszy przykład dodaje 7 dni do konkretnej daty:

SELECT DATEADD(day, 7, '2026-04-10') AS DataPoTygodniu;

Wynikiem będzie 17 kwietnia 2026 roku. W aplikacjach produkcyjnych zamiast niejednoznacznych zapisów dat preferuję format YYYY-MM-DD, ponieważ ogranicza ryzyko pomyłek zależnych od ustawień językowych serwera.

Jednostka Przykład Zastosowanie
year DATEADD(year, 1, Data) Przesunięcie o rok
quarter DATEADD(quarter, 1, Data) Przesunięcie o kwartał
month DATEADD(month, 1, Data) Dodanie miesiąca
week DATEADD(week, 2, Data) Dodanie tygodni
day DATEADD(day, 30, Data) Dodanie dni
hour DATEADD(hour, 6, Data) Dodanie godzin
minute DATEADD(minute, 15, Data) Dodanie minut
second DATEADD(second, 45, Data) Dodanie sekund

Argument number nie musi być stałą. Może pochodzić z kolumny, zmiennej albo innej funkcji. Dzięki temu można na przykład obliczyć termin ważności na podstawie liczby dni zapisanej przy produkcie.

SELECT
    Id,
    DataZakupu,
    OkresGwarancjiDni,
    DATEADD(day, OkresGwarancjiDni, DataZakupu) AS KoniecGwarancji
FROM Zamowienia;

Warto pamiętać, że nazwa jednostki, czyli datepart, nie działa tak samo jak zwykła wartość. Nie można bezpośrednio przekazać nazwy kolumny jako jednostki czasu. Gdy jednostka ma być wybierana dynamicznie, potrzebna jest logika CASE albo ostrożnie przygotowany SQL dynamiczny.

Praktyczne przykłady dla dni, miesięcy i godzin

Daty względne względem bieżącego czasu

Do wyznaczania dat względem chwili wykonania zapytania można użyć SYSDATETIME lub GETDATE. Pierwsza funkcja oferuje większą precyzję, dlatego zwykle wybieram ją w nowych rozwiązaniach.

SELECT
    SYSDATETIME() AS Teraz,
    DATEADD(day, 7, SYSDATETIME()) AS ZaTydzien,
    DATEADD(day, -30, SYSDATETIME()) AS MiesiacTemu;

Liczba ujemna oznacza cofnięcie daty. Ten zapis jest czytelniejszy niż ręczne kombinowanie z odejmowaniem i dobrze pasuje do raportów, retencji danych oraz zadań uruchamianych cyklicznie.

Terminy płatności i wygaśnięcia

Typowy przypadek to wyliczenie terminu na podstawie daty wystawienia dokumentu. Gdy termin płatności wynosi 14 dni, zapytanie może wyglądać tak:

SELECT
    NumerFaktury,
    DataWystawienia,
    DATEADD(day, 14, DataWystawienia) AS TerminPlatnosci
FROM Faktury;

Podobnie można obliczyć wygaśnięcie tokenu, dostęp próbny albo datę kolejnego odnowienia. Kluczowe jest to, aby jednostka odpowiadała regule biznesowej. „30 dni” nie zawsze oznacza „miesiąc”, zwłaszcza gdy proces ma znaczenie księgowe lub prawne.

Filtrowanie danych z ostatnich dni

Do pobrania rekordów utworzonych w ciągu ostatnich 7 dni można użyć warunku:

SELECT *
FROM Logi
WHERE Utworzono >= DATEADD(day, -7, SYSDATETIME());

Przy filtrowaniu jednego dnia lepiej użyć przedziału zamkniętego z lewej i otwartego z prawej. Taki zapis działa poprawnie także wtedy, gdy kolumna przechowuje godziny, minuty i milisekundy.

DECLARE @Dzisiaj date = CAST(SYSDATETIME() AS date);

SELECT *
FROM Zamowienia
WHERE Utworzono >= @Dzisiaj
  AND Utworzono < DATEADD(day, 1, @Dzisiaj);

To podejście jest praktyczniejsze niż konwertowanie kolumny do typu date w klauzuli WHERE. Nie opakowuję kolumny funkcją bez potrzeby, ponieważ może to utrudnić użycie indeksu.

Co dzieje się na końcu miesiąca

Najwięcej niespodzianek pojawia się przy dodawaniu miesięcy. Miesiące mają różną długość, więc nie każda data ma dokładny odpowiednik w kolejnym miesiącu.

SELECT DATEADD(month, 1, '2024-01-31') AS Wynik;

Wynikiem będzie 29 lutego 2024 roku, ponieważ rok 2024 jest przestępny. SQL Server wybiera ostatni dostępny dzień miesiąca docelowego, zamiast tworzyć niepoprawną datę 31 lutego.

Podobnie dodanie miesiąca do 31 marca może dać 30 kwietnia. To zachowanie jest rozsądne technicznie, ale nie zawsze odpowiada regułom biznesowym. Jeśli aplikacja ma naliczać odnowienie dokładnie w ostatnim dniu miesiąca, trzeba tę zasadę opisać osobno, zamiast zakładać, że każde przesunięcie o miesiąc będzie symetryczne.

Rok, miesiąc i dzień nie są tym samym

Przesunięcie o rok i przesunięcie o 12 miesięcy najczęściej daje ten sam wynik, ale przy datach granicznych nadal trzeba sprawdzić zachowanie. W systemach abonamentowych szczególnego testowania wymagają daty takie jak 29 lutego, 30 kwietnia i 31 stycznia.

Nie należy też mylić miesiąca kalendarzowego z okresem trwającym 30 dni. Jeśli regulamin mówi o 30 dniach, użyj DATEADD(day, 30, Data). Jeśli mówi o kolejnym miesiącu, użyj DATEADD(month, 1, Data) i przetestuj końce miesięcy.

Przeczytaj również: MERGE INTO w SQL - jak bezpiecznie synchronizować dane

Zakres dat i typ wyniku

DATEADD może zgłosić błąd, gdy wynik wyjdzie poza zakres typu danych. Ryzyko pojawia się przy dużych wartościach albo przy dodawaniu lat do dat bliskich granicy obsługiwanego zakresu.

Typ wyniku zależy od typu daty przekazanej do funkcji. Dlatego w kodzie wykorzystującym date, datetime, datetime2 lub datetimeoffset trzeba sprawdzić precyzję i sposób przechowywania strefy czasowej. Nie zakładałbym automatycznie, że wynik zawsze będzie zwykłym typem date.

DATEADD i DATEDIFF w zapytaniach raportowych

DATEADD służy do przesuwania daty, a DATEDIFF do obliczania różnicy między dwiema datami. Te funkcje często występują razem, choć rozwiązują różne problemy.

SELECT
    DataRozpoczecia,
    DataZakonczenia,
    DATEDIFF(day, DataRozpoczecia, DataZakonczenia) AS LiczbaDni
FROM Procesy;

Trzeba uważać na sposób działania DATEDIFF. Funkcja liczy przekroczone granice jednostek, a nie zawsze pełne okresy. Różnica między 23:59 a 00:01 może zostać policzona jako jeden dzień, mimo że upłynęły tylko dwie minuty.

Przy raportach okresowych dobrze sprawdza się budowanie początku i końca zakresu:

DECLARE @Od datetime2 = '2026-01-01';
DECLARE @Do datetime2 = DATEADD(month, 1, @Od);

SELECT *
FROM Sprzedaz
WHERE DataSprzedazy >= @Od
  AND DataSprzedazy < @Do;

Zakres >= początek i < koniec eliminuje problem sztucznego ustawiania godziny 23:59:59.999. Jest też bezpieczniejszy dla kolumn o większej precyzji.

Różnice między SQL Serverem, PostgreSQL i MySQL

Nazwa funkcji może sugerować standard SQL, ale DATEADD jest przede wszystkim rozwiązaniem kojarzonym z SQL Serverem. Po przeniesieniu zapytania do innego silnika składnia zwykle wymaga zmiany.

Silnik Przykład dodania 7 dni Najważniejsza różnica
SQL Server DATEADD(day, 7, Data) Jednostka jest pierwszym argumentem funkcji.
PostgreSQL Data + INTERVAL '7 days' Najczęściej używa się operatora plus i INTERVAL.
MySQL DATE_ADD(Data, INTERVAL 7 DAY) Jednostka występuje wewnątrz wyrażenia INTERVAL.

Przykład dla PostgreSQL wygląda tak:

SELECT DataUtworzenia + INTERVAL '7 days' AS ZaTydzien
FROM Zamowienia;

W MySQL zapis będzie inny:

SELECT DATE_ADD(DataUtworzenia, INTERVAL 7 DAY) AS ZaTydzien
FROM Zamowienia;

Przy migracji aplikacji nie wystarczy mechaniczna zamiana nazwy funkcji. Trzeba sprawdzić również typ wyniku, obsługę końca miesiąca i strefy czasowe. Właśnie te szczegóły najczęściej powodują różnice widoczne dopiero na danych produkcyjnych.

Typowe błędy przy używaniu DATEADD

  • Pomylenie dnia z miesiącem. 30 dni i jeden miesiąc nie zawsze oznaczają ten sam okres.
  • Niepoprawny format daty. Daty zapisane jako tekst mogą być interpretowane zależnie od ustawień sesji.
  • Funkcja na kolumnie w WHERE. Konwersja lub DATEADD użyte bezpośrednio na kolumnie może pogorszyć działanie indeksu.
  • Ignorowanie czasu. Kolumna datetime obejmuje również godzinę, więc filtr po samej dacie może pominąć część rekordów.
  • Brak obsługi NULL. Jeśli data wejściowa jest NULL, wynik również będzie NULL.
  • Nieuwzględnienie strefy czasowej. Dodanie godziny do datetime nie rozwiązuje problemu zmiany czasu letniego.

W aplikacjach działających w wielu krajach szczególnie ostrożnie podchodzę do operacji na czasie lokalnym. Dla terminów technicznych, logów i zdarzeń integracyjnych zwykle bezpieczniej przechowywać czas w UTC, a dopiero podczas prezentacji przeliczać go na strefę użytkownika.

Przed wdrożeniem testuję co najmniej zwykłą datę, koniec miesiąca, rok przestępny, wartości ujemne, NULL oraz datę z godziną. Kilka takich przypadków testowych potrafi wychwycić więcej błędów niż długie sprawdzanie typowych dni.

Jak podejść do DATEADD w projekcie

Najpierw ustal, czy reguła biznesowa mówi o dniach, godzinach czy miesiącach. Potem wybierz typ daty odpowiedni dla wymaganej precyzji i zapisz zakresy w sposób przyjazny indeksom.

Jeżeli pracujesz wyłącznie z SQL Serverem, DATEADD jest prostym i czytelnym wyborem. Przy kodzie przenośnym między bazami lepiej od razu oddzielić zapytania zależne od silnika albo użyć warstwy dostępu do danych, która świadomie tłumaczy operacje na datach.

Najważniejsza praktyczna zasada jest prosta: nie traktuj daty jak zwykłej liczby. Testuj miesiące o różnej długości, godziny i strefy czasowe, a wtedy funkcja będzie przewidywalnym elementem zapytań zamiast źródłem trudnych do odtworzenia błędów.

FAQ - Najczęstsze pytania

Składnia funkcji to DATEADD(datepart, number, date). Dodanie 7 dni wykonuje zapis DATEADD(day, 7, Data), a liczba ujemna, na przykład -30, cofa datę o 30 dni. Argument number może pochodzić z kolumny, zmiennej lub innej funkcji.

SQL Server wybiera ostatni dostępny dzień miesiąca docelowego. Dodanie miesiąca do 31 stycznia 2024 roku zwróci 29 lutego, a przesunięcie 31 marca o miesiąc da 30 kwietnia. Miesiąc kalendarzowy nie jest tym samym co 30 dni, dlatego regułę trzeba dopasować do wymagań biznesowych.

Najlepiej użyć zakresu zamkniętego z lewej i otwartego z prawej: Utworzono >= @Dzisiaj AND Utworzono < DATEADD(day, 1, @Dzisiaj). Takie podejście uwzględnia godziny, minuty i milisekundy, a także nie wymaga opakowywania kolumny funkcją w klauzuli WHERE, co może ułatwić użycie indeksu.

W PostgreSQL zwykle dodaje się interwał, na przykład Data + INTERVAL '7 days'. W MySQL stosuje się DATE_ADD(Data, INTERVAL 7 DAY). Przy migracji trzeba dodatkowo sprawdzić typ wyniku, końce miesięcy oraz obsługę stref czasowych.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

sql server
postgresql
mysql
dateadd
strefy czasowe
Autor Przemysław Kwiatkowski
Przemysław Kwiatkowski
Jestem Przemysław i od 15 lat zajmuję się programowaniem .NET, chmurą Azure oraz sztuczną inteligencją. Moja przygoda z tymi technologiami zaczęła się od fascynacji możliwościami, jakie dają, a z czasem przerodziła się w pasję do tworzenia rozwiązań, które realnie wpływają na pracę i życie ludzi. Na kursdotnet.pl staram się dzielić się swoją wiedzą w sposób przystępny, tłumacząc złożone zagadnienia i pomagając zrozumieć, jak te dynamicznie rozwijające się obszary IT mogą być wykorzystane w praktyce. Dokładam wszelkich starań, aby prezentowane przeze mnie materiały były rzetelne, aktualne i oparte na sprawdzonych źródłach, a także aby uporządkować wiedzę w sposób ułatwiający jej przyswojenie.

Udostępnij artykuł

Napisz komentarz