← Kursa Dön
📄 Text · 35 min

Tarih Fonksiyonları: NOW, DATE_FORMAT, DATEDIFF

Giriş — Zamanla Çalışmak

E-ticaret sitesinde tarihlerle sürekli çalışırsın: "Son 30 gündeki siparişler", "Bu ayki satışlar", "Müşterinin hesap yaşı", "Siparişin kargoya verilmesi kaç gün sürdü?"... Tarih fonksiyonları SQL'in en kullanışlı araçlarından biridir.

🎯 Analoji: Tarih fonksiyonlarını bir takvim uygulaması gibi düşün. Bugünün tarihini alabilir, iki tarih arasındaki farkı hesaplayabilir, tarihe gün/ay/yıl ekleyebilir, tarihin sadece yıl veya ay kısmını çıkarabilirsin.


Mevcut Tarih ve Zaman

-- Şu anki tarih ve saat
SELECT NOW();          -- 2024-01-15 14:30:45
SELECT CURRENT_TIMESTAMP();  -- Aynı şey

-- Sadece tarih
SELECT CURDATE();      -- 2024-01-15
SELECT CURRENT_DATE(); -- Aynı şey

-- Sadece saat
SELECT CURTIME();      -- 14:30:45
SELECT CURRENT_TIME(); -- Aynı şey

-- UNIX timestamp (epoch)
SELECT UNIX_TIMESTAMP();        -- 1705322245
SELECT FROM_UNIXTIME(1705322245);  -- 2024-01-15 14:30:45

DATEDIFF ve TIMESTAMPDIFF — Tarih Farkı

DATEDIFF — Gün Farkı

DATEDIFF(tarih1, tarih2)  -- tarih1 - tarih2 = gün farkı

SELECT DATEDIFF('2024-12-31', '2024-01-01');  -- 365
SELECT DATEDIFF('2024-01-01', '2024-12-31');  -- -365 (negatif)

-- Siparişin kaç gün önce verildiği
SELECT order_id, order_date,
       DATEDIFF(NOW(), order_date) AS days_ago
FROM orders
ORDER BY days_ago ASC;

-- Müşterinin hesap yaşı (gün)
SELECT first_name, registration_date,
       DATEDIFF(CURDATE(), registration_date) AS account_age_days
FROM customers;

TIMESTAMPDIFF — Esnek Zaman Farkı

TIMESTAMPDIFF(birim, başlangıç, bitiş)

-- Yıl farkı
SELECT TIMESTAMPDIFF(YEAR, '1990-05-15', CURDATE()) AS age;
-- 33 (veya 34, tarihe göre)

-- Ay farkı
SELECT TIMESTAMPDIFF(MONTH, '2024-01-01', '2024-06-15');  -- 5

-- Saat farkı
SELECT TIMESTAMPDIFF(HOUR, order_date, NOW()) AS hours_since_order
FROM orders;

-- Sipariş işleme süresi (saat)
SELECT order_id,
       TIMESTAMPDIFF(HOUR, order_date, updated_at) AS processing_hours
FROM orders WHERE status = 'shipped';

Birimler: YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND


DATE_ADD ve DATE_SUB — Tarihe Ekleme/Çıkarma

-- 30 gün sonrası
SELECT DATE_ADD(CURDATE(), INTERVAL 30 DAY);

-- 3 ay öncesi
SELECT DATE_SUB(NOW(), INTERVAL 3 MONTH);

-- 1 yıl ve 6 ay sonrası
SELECT DATE_ADD(CURDATE(), INTERVAL 18 MONTH);

-- Alternatif syntax (+ INTERVAL)
SELECT CURDATE() + INTERVAL 30 DAY;
SELECT NOW() - INTERVAL 3 MONTH;

E-ticaret kullanım senaryoları:

-- Son 7 gündeki siparişler
SELECT * FROM orders WHERE order_date >= DATE_SUB(NOW(), INTERVAL 7 DAY);

-- Son 30 gündeki siparişler  
SELECT * FROM orders WHERE order_date >= NOW() - INTERVAL 30 DAY;

-- Bu ayki siparişler
SELECT * FROM orders 
WHERE order_date >= DATE_FORMAT(NOW(), '%Y-%m-01')
  AND order_date < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH);

-- Tahmini teslimat tarihi (5 iş günü sonra ≈ 7 gün)
SELECT order_id, order_date,
       DATE_ADD(order_date, INTERVAL 7 DAY) AS estimated_delivery
FROM orders WHERE status = 'shipped';

-- Üyelik sona erme tarihi (1 yıl)
SELECT first_name,
       registration_date,
       DATE_ADD(registration_date, INTERVAL 1 YEAR) AS membership_expires
FROM customers;

INTERVAL birimleri: YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND, QUARTER


EXTRACT — Tarih Parçalarını Çıkarma

-- Yıl
SELECT EXTRACT(YEAR FROM '2024-06-15');   -- 2024
SELECT YEAR('2024-06-15');                 -- 2024 (kısa yol)

-- Ay
SELECT EXTRACT(MONTH FROM '2024-06-15');  -- 6
SELECT MONTH('2024-06-15');

-- Gün
SELECT EXTRACT(DAY FROM '2024-06-15');    -- 15
SELECT DAY('2024-06-15');

-- Haftanın günü (1=Pazar, 7=Cumartesi — MySQL'de)
SELECT DAYOFWEEK('2024-06-15');           -- 7 (Cumartesi)

-- Günün adı
SELECT DAYNAME('2024-06-15');             -- 'Saturday'

-- Çeyrek
SELECT QUARTER('2024-06-15');             -- 2

-- Haftanın numarası
SELECT WEEK('2024-06-15');                -- 24

Gruplama ve raporlamada EXTRACT kullanımı:

-- Aylık sipariş sayısı
SELECT EXTRACT(YEAR FROM order_date) AS yil,
       EXTRACT(MONTH FROM order_date) AS ay,
       COUNT(*) AS siparis_sayisi,
       SUM(total_amount) AS toplam_tutar
FROM orders
GROUP BY EXTRACT(YEAR FROM order_date), EXTRACT(MONTH FROM order_date)
ORDER BY yil, ay;

-- Hangi günler daha çok sipariş geliyor?
SELECT DAYNAME(order_date) AS gun_adi,
       COUNT(*) AS siparis_sayisi
FROM orders
GROUP BY DAYNAME(order_date)
ORDER BY siparis_sayisi DESC;

DATE_FORMAT — Tarih Formatlama

DATE_FORMAT(tarih, format_stringi)

SELECT DATE_FORMAT(NOW(), '%d/%m/%Y');        -- "15/01/2024"
SELECT DATE_FORMAT(NOW(), '%d %M %Y');        -- "15 January 2024"
SELECT DATE_FORMAT(NOW(), '%H:%i:%s');        -- "14:30:45"
SELECT DATE_FORMAT(NOW(), '%d.%m.%Y %H:%i'); -- "15.01.2024 14:30"

Yaygın format kodları:

KodAçıklamaÖrnek
%Y4 haneli yıl2024
%y2 haneli yıl24
%mAy (01-12)01
%MAy adı (İngilizce)January
%dGün (01-31)15
%DGün + suffix15th
%HSaat (24 saat, 00-23)14
%hSaat (12 saat, 01-12)02
%iDakika (00-59)30
%sSaniye (00-59)45
%WGün adıWednesday
%pAM/PMPM
-- Sipariş raporu formatı
SELECT order_id,
       DATE_FORMAT(order_date, '%d.%m.%Y') AS tarih,
       DATE_FORMAT(order_date, '%H:%i') AS saat,
       total_amount
FROM orders
ORDER BY order_date DESC;

📝 PostgreSQL Notu: PostgreSQL'de DATE_FORMAT yerine TO_CHAR kullanılır: ``sql SELECT TO_CHAR(order_date, 'DD/MM/YYYY HH24:MI') FROM orders; ``


STR_TO_DATE — String'den Tarihe Dönüşüm

-- Farklı formattaki string'i tarihe çevir
SELECT STR_TO_DATE('15/01/2024', '%d/%m/%Y');   -- 2024-01-15
SELECT STR_TO_DATE('Jan 15, 2024', '%b %d, %Y'); -- 2024-01-15

-- Import sırasında tarih dönüşümü
INSERT INTO orders (customer_id, order_date)
VALUES (1, STR_TO_DATE('15.01.2024 14:30', '%d.%m.%Y %H:%i'));

Gerçek Dünya Örneği — E-Ticaret Zaman Bazlı Rapor

-- Kapsamlı satış raporu
SELECT 
    DATE_FORMAT(order_date, '%Y-%m') AS ay,
    COUNT(*) AS siparis_sayisi,
    COUNT(DISTINCT customer_id) AS benzersiz_musteri,
    ROUND(SUM(total_amount), 2) AS toplam_satis,
    ROUND(AVG(total_amount), 2) AS ortalama_siparis,
    -- Ortalama teslimat süresi
    ROUND(AVG(
        CASE WHEN status = 'delivered' 
        THEN DATEDIFF(updated_at, order_date) 
        END
    ), 1) AS ort_teslimat_gunu,
    -- Bu ayki siparişlerin yüzdesi bekleyen/tamamlanan
    ROUND(SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) 
        AS teslim_yuzdesi
FROM orders
WHERE order_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
ORDER BY ay DESC;

Sıkça Yapılan Hatalar

  1. DATETIME sütununda tarih karşılaştırması: WHERE order_date = '2024-01-15' sadece 00:00:00 olan siparişleri getirir. WHERE DATE(order_date) = '2024-01-15' veya BETWEEN ... AND ... kullan.

  2. DATEDIFF parametre sırası: DATEDIFF(büyük_tarih, küçük_tarih) pozitif döner. Ters yazarsan negatif döner.

  3. MONTH()/YEAR() fonksiyonlarını WHERE'de kullanmak: WHERE YEAR(order_date) = 2024 index kullanamaz. WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01' çok daha hızlı.

  4. Timezone farkını görmezden gelmek: NOW() sunucunun timezone'unu kullanır. Farklı timezone'lardaki kullanıcılar için CONVERT_TZ() kullan.


Özet

  • NOW() — tarih+saat, CURDATE() — sadece tarih, CURTIME() — sadece saat

  • DATEDIFF(t1, t2) — gün farkı, TIMESTAMPDIFF(birim, t1, t2) — esnek fark

  • DATE_ADD / DATE_SUB — tarihe ekleme/çıkarma (INTERVAL ile)

  • EXTRACT(YEAR/MONTH/DAY FROM tarih) — tarih parçası çıkarma

  • DATE_FORMAT(tarih, format) — tarih formatlama

  • STR_TO_DATE(string, format) — string'den tarihe dönüşüm

  • WHERE'de tarih fonksiyonu kullanmak index'i bozar — aralık karşılaştırması tercih et