SQLite'ta tarih ve saat için ayrı bir veri tipi yoktur; değerleri ISO-8601 metni, Jülyen gün sayısı (REAL) veya Unix zaman damgası (INTEGER) olarak saklar ve yedi yerleşik fonksiyonla işlersiniz: date(), time(), datetime(), julianday(), unixepoch(), strftime() ve timediff(). İlk altısı bir zaman değeri ve ardından sırayla uygulanan değiştiriciler (modifier) alır; gün eklemek, ayın başına gitmek veya saat dilimi düzeltmek bu değiştiricilerle yapılır.
Bu yazıda her fonksiyonun döndürdüğü tipi, kabul edilen giriş biçimlerini, değiştiricilerin sırasının neden önemli olduğunu, ay eklerken oluşan belirsizliği ve saat dilimi tuzaklarını örneklerle göreceksiniz. Sonda, sipariş tablosu üzerinde aylık rapor ve tarih aralığı sorgusu yazacağız. Saklama sınıflarının genel mantığı veri tipleri ve type affinity yazısında anlatılıyor.
Yedi fonksiyon ve döndürdükleri değerler

date() YYYY-MM-DD, time() HH:MM:SS, datetime() ise YYYY-MM-DD HH:MM:SS biçiminde metin döndürür. julianday() MÖ 4714 yılı 24 Kasım öğlen saatinden bu yana geçen günü kesirli bir REAL sayı olarak, unixepoch() ise 1970-01-01 00:00:00 UTC'den bu yana geçen saniyeyi INTEGER olarak verir. strftime() ilk argümanındaki biçim metnine göre metin üretir; timediff() iki zaman değeri arasındaki farkı okunabilir bir metin olarak döndürür.
SELECT date('2026-11-07 14:30:15'), time('2026-11-07 14:30:15'), datetime('2026-11-07T14:30:15');
-- çıktı: 2026-11-07|14:30:15|2026-11-07 14:30:15
SELECT julianday('2026-11-07 12:00:00'), unixepoch('2026-11-07 14:30:15');
-- çıktı: 2461352.0|1794061815
SELECT typeof(date('2026-11-07')), typeof(julianday('2026-11-07')), typeof(unixepoch('2026-11-07'));
-- çıktı: text|real|integerBelge diğer fonksiyonların strftime() ile ifade edilebileceğini gösterir: date(...) strftime('%F', ...), datetime(...) ise strftime('%F %T', ...) ile aynıdır. Ayrı fonksiyonların varlık nedeni kolaylık ve verimliliktir: julianday() ve unixepoch() doğrudan sayı döndürdüğü için metin dönüşümünün maliyetine ve hassasiyet kaybına girmez. unixepoch() 3.38.0, timediff() ise 3.43.0 sürümünde eklendi; daha eski bir SQLite'ta bu iki fonksiyon bulunmaz.
Zaman değeri verilmezse 'now' varsayılır. 'now' her zaman UTC'dir ve aynı ifadenin tek bir çalıştırması içinde kaç kez çağrılırsa çağrılsın aynı değeri döndürür. Bu nedenle aşağıdaki sorguların çıktısı çalıştırdığınız ana göre değişir:
SELECT date('now'), datetime('now'), unixepoch();
SELECT datetime('now', 'localtime');
SELECT unixepoch('subsec');Kabul edilen zaman değeri biçimleri
Belge on iki biçim listeler: tarih (YYYY-MM-DD), tarih ve saat (saniyeli veya saniyesiz, kesirli saniyeli, arada boşluk veya T harfiyle), yalnızca saat, now ve sayısal biçim. Yalnızca saat içeren değerlerde tarih 2000-01-01 kabul edilir. Sayısal bir değer varsayılan olarak Jülyen gün sayısı olarak yorumlanır; Unix zaman damgası olduğunu bildirmek için hemen ardından 'unixepoch' değiştiricisini yazarsınız. 'auto' değiştiricisi sayının büyüklüğüne bakarak ikisinden birini seçer.
SELECT datetime(1794061815, 'unixepoch'), datetime(1794061815, 'auto'), datetime(2461352.0);
-- çıktı: 2026-11-07 14:30:15|2026-11-07 14:30:15|2026-11-07 12:00:00
SELECT datetime('2026-11-07 14:30:00+03:00'), datetime('2026-11-07T11:30:00Z');
-- çıktı: 2026-11-07 11:30:00|2026-11-07 11:30:00
SELECT time('14:30'), date('14:30');
-- çıktı: 14:30:00|2000-01-01Saat içeren biçimlerin sonuna +HH:MM veya -HH:MM biçiminde bir saat dilimi ya da Z eklenebilir. Fonksiyonlar içeride UTC kullandığı için Z hiçbir şey değiştirmez, sıfırdan farklı bir dilim ise UTC'ye çevrilmek üzere çıkarılır: 14:30+03:00 UTC olarak 11:30 olur. Belge 'auto' için bir uyarı da yapar: 1970'in ilk 63 gününe ait Unix zaman damgaları Jülyen gün sayısı aralığına düştüğü için yanlış yorumlanır. Veriniz bu döneme ait değer içerebiliyorsa 'unixepoch' yazın.
Değiştiriciler: eklemek, yuvarlamak, başa ve sona gitmek
Değiştiriciler soldan sağa sırayla uygulanır ve her biri solundaki sonucu dönüştürür. NNN days, hours, minutes, seconds, months ve years miktar ekler; sondaki "s" isteğe bağlıdır ve NNN kesirli ya da negatif olabilir. start of month, start of year ve start of day tarihi o dönemin başına geri çeker. +YYYY-MM-DD HH:MM:SS biçimindeki zaman kaydırma değiştiricileri ise yılı, ayı, günü ve saati tek seferde kaydırır.
SELECT date('2026-11-07', '+1 day'), date('2026-11-07', '-7 days'),
datetime('2026-11-07 14:30', '+90 minutes');
-- çıktı: 2026-11-08|2026-10-31|2026-11-07 16:00:00
-- Ayın ilk ve son günü
SELECT date('2026-11-07', 'start of month'), date('2026-11-07', 'start of month', '+1 month', '-1 day');
-- çıktı: 2026-11-01|2026-11-30
-- Değiştiricilerin sırası önemlidir
SELECT date('2026-01-31', '+1 month', 'start of month'), date('2026-01-31', 'start of month', '+1 month');
-- çıktı: 2026-03-01|2026-02-01Son satır sıranın etkisini gösteriyor. 31 Ocak'a bir ay eklemek önce "31 Şubat" gibi var olmayan bir tarih üretir; bu tarih 3 Mart'a taşar, ardından ayın başına gidilince 1 Mart elde edilir. Önce ayın başına gidip sonra bir ay eklemek ise beklenen 1 Şubat sonucunu verir. Ayın son gününü bulmanın klasik yolu da buradan çıkar: ayın başına git, bir ay ekle, bir gün çıkar.
Ay ve yıl eklerken belirsizlik: ceiling ve floor
Ayların ve yılların uzunluğu değiştiği için "29 Şubat 2024'ten bir yıl sonra" gibi soruların tek doğru cevabı yoktur. Belge bu belirsizliği çözmek için 3.46.0 sürümünde eklenen ceiling ve floor değiştiricilerini tanımlar. Kaydırmadan hemen sonra ceiling gelirse belirsiz tarih sonraki aya taşınır; floor ise önceki ayın son gününe çekilir. Varsayılan davranış ceiling olduğu için abonelik veya fatura dönemi hesaplarken bilinçli bir seçim yapmanız gerekir.
SELECT date('2026-01-31', '+1 month'), date('2026-01-31', '+1 month', 'floor');
-- çıktı: 2026-03-03|2026-02-28
SELECT date('2024-02-29', '+1 year'), date('2024-02-29', '+1 year', 'floor');
-- çıktı: 2025-03-01|2025-02-28Haftanın günü ve strftime() biçimleri

weekday N değiştiricisi tarihi, haftanın günü N olan ilk tarihe ilerletir; tarih zaten o gündeyse değiştirmez. Numaralandırma pazardan başlar: pazar 0, pazartesi 1, cumartesi 6. strftime() tarafında ise iki farklı numaralandırma vardır: %w pazarı 0 sayar, %u pazartesiyi 1 sayar. Türkiye'de hafta pazartesi başladığı için raporlarda %u genellikle daha doğal bir seçimdir.
-- 2026-11-07 bir cumartesi: %w (pazar=0) ve %u (pazartesi=1)
SELECT strftime('%w %u', '2026-11-07'), date('2026-11-07', 'weekday 1'), date('2026-11-07', 'weekday 6');
-- çıktı: 6 6|2026-11-09|2026-11-07
SELECT strftime('%d.%m.%Y %H:%M', '2026-11-07 14:30:15'), strftime('%j', '2026-11-07'),
strftime('%W', '2026-11-07'), strftime('%V', '2026-11-07');
-- çıktı: 07.11.2026 14:30|311|44|45
SELECT strftime('%Y-%m', '2026-11-07'), strftime('%e/%m', '2026-11-07'), strftime('%I:%M %p', '2026-11-07 14:30');
-- çıktı: 2026-11| 7/11|02:30 PM
SELECT quote(strftime('%Q', '2026-11-07'));
-- çıktı: NULLSık kullanılan biçimler %Y (yıl), %m (ay), %d (gün), %H, %M, %S (saat, dakika, saniye), %j (yılın günü), %s (Unix saniyesi) ve %f (kesirli saniye) biçimleridir. Hafta numarası için üç seçenek var: %W haftayı pazartesiden, %U pazardan başlatır, %V ise ISO 8601 hafta numarasını verir. Örnekte aynı gün %W ile 44, %V ile 45. hafta çıktı; raporlarınızda hangisini kullandığınızı belirtin. Belge tanımsız veya desteklenmeyen bir biçim görüldüğünde sonucun NULL olacağını söyler; %Q örneği bunu gösterir.
| Biçim | Anlamı | Eklendiği sürüm |
|---|---|---|
| %F, %T, %R | ISO tarih, saat ve saat:dakika | 3.44.0 |
| %e, %k, %l | Baştaki sıfır olmadan gün ve saat | 3.44.0 |
| %I, %p, %P | 12 saatlik saat ve AM/PM | 3.44.0 |
| %u | Pazartesi 1 olacak şekilde haftanın günü | 3.44.0 |
| %G, %g, %V | ISO 8601 yılı ve hafta numarası | 3.46.0 |
| %U | Pazardan başlayan hafta numarası | 3.46.0 |
| %J | Kesirli Jülyen gün sayısı | SQLite'a özgü |
Ay ve gün adları için bir biçim yoktur; Türkçe ay adı gerekiyorsa strftime('%m', ...) sonucunu bir CASE ifadesiyle veya küçük bir eşleme tablosuyla çevirirsiniz. %e gibi yeni biçimlerin eklendiği sürümler sürüm geçmişinde yer alır; eski bir kütüphane bu biçimler için NULL döndürür.
Süre hesaplamak: subsec, timediff() ve farklar
subsec (uzun adıyla subsecond) değiştiricisi 3.42.0 sürümünde eklendi ve datetime(), time(), unixepoch() çıktısını milisaniye çözünürlüğüne çıkarır. unixepoch() ile kullanıldığında sonuç REAL olur. İki an arasındaki farkı ölçmenin kesin yolu, belgenin de önerdiği gibi iki julianday() veya iki unixepoch() değerini çıkarmaktır.
SELECT time('2026-11-07 14:30:15.123456', 'subsec'), unixepoch('2026-11-07 14:30:15.250', 'subsec');
-- çıktı: 14:30:15.123|1794061815.25
SELECT timediff('2026-11-07', '2026-01-01'), timediff('2026-01-01', '2026-11-07');
-- çıktı: +0000-10-06 00:00:00.000|-0000-10-06 00:00:00.000
SELECT julianday('2026-11-07') - julianday('2026-01-01'),
(unixepoch('2026-11-07 18:00') - unixepoch('2026-11-07 09:30')) / 60;
-- çıktı: 310.0|510
SELECT datetime('2026-01-01', timediff('2026-11-07 10:00', '2026-01-01'));
-- çıktı: 2026-11-07 10:00:00timediff(A, B), B'ye eklendiğinde A'yı veren süreyi (+|-)YYYY-MM-DD HH:MM:SS.SSS biçiminde döndürür ve bu metin diğer fonksiyonlarda değiştirici olarak kullanılabilir; son satır bu değişmezliği gösterir. Belge, insan için okunaklı bir süre gerektiğinde timediff(), gün veya saniye cinsinden kesin fark gerektiğinde ise çıkarma işlemini önerir. Ayların uzunluğu değiştiğinden 28 günlük ve 31 günlük iki aralık aynı "bir ay" sonucunu verebilir. Bölme yaparken dikkat edin: iki tamsayının bölümü tamsayıdır, kesirli dakika istiyorsanız 60.0 ile bölün; ayrıntı için matematik fonksiyonları yazısına bakın.
Hatalar NULL döner: sessiz başarısızlıklar
Tarih fonksiyonları hata mesajı üretmez. Belgeye göre tanınmayan, hatalı yazılmış veya aralık dışı bir değiştirici tüm fonksiyonun NULL döndürmesine yol açar; aynısı desteklenmeyen giriş biçimleri için de geçerlidir. Bu davranış özellikle INSERT ... SELECT veya UPDATE içinde tehlikelidir, çünkü hatalı bir değiştirici fark edilmeden binlerce satıra NULL yazabilir.
SELECT quote(date('07.11.2026')), quote(date('2026-11-7')), quote(date('2026-11-07', '+1 dya')),
quote(date('2026-11-07', 'weekday 7'));
-- çıktı: NULL|NULL|NULL|NULL
SELECT '2026-11-07' < '2026-11-10', '2026-9-07' < '2026-11-10';
-- çıktı: 1|0
SELECT date('2026-02-30');
-- çıktı: 2026-03-02Örnekteki ikinci satır, metin olarak saklanan tarihlerin neden sıfırla doldurulmuş olması gerektiğini gösterir: karşılaştırma metin olarak yapılır ve "2026-9-07" metni "2026-11-10" metninden büyük sayılır. Son satır ise çalıştırdığımız 3.50.6 sürümünde var olmayan 30 Şubat tarihinin hata vermeden 2 Mart'a taşındığını gösteriyor. Tarih doğrulaması gerekiyorsa değeri date() sonucuyla karşılaştıran bir CHECK kısıtı kullanabilirsiniz. Burada = yerine IS kullanmak önemlidir: date() NULL döndürdüğünde = karşılaştırması da NULL olur ve CHECK kısıtı NULL sonucu ihlal saymaz.
CREATE TABLE t1(tarih TEXT CHECK (tarih = date(tarih)));
INSERT INTO t1 VALUES ('2026-02-30');
-- hata: CHECK constraint failed: tarih = date(tarih)
INSERT INTO t1 VALUES ('07.11.2026');
SELECT count(*) FROM t1;
-- çıktı: 1
CREATE TABLE t2(tarih TEXT CHECK (tarih IS date(tarih)));
INSERT INTO t2 VALUES ('2026-02-30');
-- hata: CHECK constraint failed: tarih IS date(tarih)
INSERT INTO t2 VALUES ('07.11.2026');
-- hata: CHECK constraint failed: tarih IS date(tarih)
INSERT INTO t2 VALUES ('2026-11-07');
SELECT count(*) FROM t2;
-- çıktı: 1İlk kural taşan tarihi yakaladı ama Türkçe biçimli metni içeri aldı; IS kullanan ikinci kural ikisini de reddetti. Kısıtların ayrıntısı CREATE TABLE ve kısıtlamalar yazısında, IS işlecinin NULL ile ilişkisi ise NULL davranışı yazısında anlatılıyor.
Yerel saat ve saat dilimleri

localtime değiştiricisi solundaki değerin UTC olduğunu varsayar ve onu sunucunun yerel saatine çevirir; utc ise tersini yapar. Solundaki değer beklenen dilimde değilse sonuç belgeye göre tanımsızdır. Yerel saat hesabı C kütüphanesindeki localtime_r() fonksiyonuna dayanır ve sonuç, veritabanını çalıştıran makinenin saat dilimi ayarına bağlıdır. Belgenin "uyarılar" bölümü yaz saati kurallarının yerden yere ve yıldan yıla değiştiğini, eski Windows sürümlerinde tarihsel yaz saati hesaplarının yanlış olabileceğini hatırlatır.
Sunucu tarafında çalışan uygulamalar için en sağlam düzen, tüm zamanları UTC olarak saklamak ve yerel saate çevirmeyi gösterim katmanında yapmaktır. Raporlarınız yaz saati değişikliği olmayan tek bir sabit dilime göre hazırlanıyorsa (örneğin UTC+3), '+3 hours' gibi sabit bir değiştirici işinizi görür ve sunucunun saat dilimi ayarından etkilenmez. Farklı ülkelerdeki kullanıcılara hizmet veriyorsanız dönüşümü dil bağlayıcınızın saat dilimi kütüphanesine bırakmak daha doğrudur.
Uygulamalı örnek: siparişlerde aylık rapor ve tarih aralığı
Aşağıdaki tablo oluşturulma zamanını UTC metin olarak saklıyor ve varsayılan değeri datetime('now') ile atıyor. Örnek veride ay sonuna denk gelen iki kayıt bilinçli olarak yer alıyor.
CREATE TABLE siparis(
id INTEGER PRIMARY KEY,
tutar INTEGER NOT NULL,
olusturma TEXT NOT NULL DEFAULT (datetime('now'))
);
INSERT INTO siparis(tutar, olusturma) VALUES
(250, '2026-09-28 22:15:00'),
(120, '2026-10-03 09:00:00'),
(480, '2026-10-31 23:59:59'),
(300, '2026-11-01 00:00:00'),
(90, '2026-11-06 13:45:00');
-- Aylık özet
SELECT strftime('%Y-%m', olusturma) AS ay, count(*), sum(tutar)
FROM siparis GROUP BY ay ORDER BY ay;
-- çıktı:
-- 2026-09|1|250
-- 2026-10|2|600
-- 2026-11|2|390
-- Hatalı: BETWEEN gün sonunu kaçırır
SELECT id FROM siparis WHERE olusturma BETWEEN '2026-10-01' AND '2026-10-31';
-- çıktı: 2
-- Doğru: yarı açık aralık
SELECT id FROM siparis
WHERE olusturma >= '2026-10-01' AND olusturma < date('2026-10-01', '+1 month');
-- çıktı:
-- 2
-- 3
-- UTC kaydı Türkiye saatine (UTC+3) çevirmek
SELECT id, datetime(olusturma, '+3 hours') FROM siparis WHERE id = 3;
-- çıktı: 3|2026-11-01 02:59:59Aylık özet için strftime('%Y-%m', ...) yeterlidir. Asıl ders ikinci sorguda: BETWEEN '2026-10-01' AND '2026-10-31' koşulu "2026-10-31 23:59:59" değerini kaçırdı, çünkü metin olarak bu değer "2026-10-31" metninden büyüktür. Saat içeren sütunlarda her zaman alt sınırı dahil, üst sınırı hariç tutan yarı açık aralık yazın ve üst sınırı date(..., '+1 month') ile hesaplayın. Bu biçim aynı zamanda sütun üzerindeki bir indeksle aralık araması yapılabilmesini sağlar; sütunu bir fonksiyona sararsanız (ör. WHERE strftime('%Y-%m', olusturma) = '2026-10') sıradan bir indeks kullanılamaz. Aynı ifadeyi sık sorguluyorsanız ifade indeksi yazısındaki yöntemle ifadeyi indeksleyebilirsiniz. Gruplama ayrıntıları için GROUP BY yazısına bakın.
SQLite 3.54 ile gelen değiştiriciler
3.54.0 sürüm notları tarih ve saat fonksiyonlarına iki yenilik ekler: end of month, end of year ve end of day değiştiricileri tarihi ilgili dönemin son milisaniyesine ilerletir; weekday değiştiricisi de artık negatif değer kabul ederek tarihi verilen günün bir önceki örneğine geri götürür. N değeri -6 ile +6 arasında olmalıdır; belgeye göre weekday -0 önceki pazara gider. Aşağıdaki iki sorgu belgedeki örneklerdir ve SQLite 3.54.0 veya sonrasını gerektirir. Elimizdeki çalışma ortamları daha eski olduğu için çıktı göstermiyoruz; eski bir sürümde bu değiştiriciler tanınmaz ve fonksiyon NULL döner.
-- SQLite 3.54.0 ve sonrası gerekir
SELECT date('now', 'end of month');
SELECT date('2026-01-01', 'end of year', 'weekday -5');Bu sürümle gelen diğer değişikliklerin özeti SQLite 3.54 yenilikler yazısında bulunuyor.
Sık Sorulan Sorular
Tarihleri SQLite'ta hangi biçimde saklamalıyım?
Belge üç biçimi de destekler ve fonksiyonlar aralarında serbestçe dönüşüm yapar. ISO-8601 metin okunaklıdır ve doğru biçimdeyse metin karşılaştırması tarih sırasıyla uyumludur. Unix zaman damgası tamsayı olduğu için küçük ve hesaplaması kolaydır. Jülyen gün sayısı kesirli gün hesaplarında pratiktir. Hangisini seçerseniz seçin, tüm tabloda tek bir biçim ve UTC kullanın; karışık biçimler karşılaştırmaları bozar.
datetime('now') neden yerel saatimi göstermiyor?
Çünkü 'now' her zaman UTC'dir. Yerel saat için datetime('now', 'localtime') yazın; sonuç, sorguyu çalıştıran makinenin saat dilimi ayarına göre hesaplanır.
İki tarih arasındaki gün sayısını nasıl bulurum?
julianday(B) - julianday(A) kesirli gün sayısını verir; tam gün için sonucu CAST(... AS INTEGER) ile kesebilir veya round() uygulayabilirsiniz. Saniye cinsinden fark için unixepoch(B) - unixepoch(A) kullanın.
Tarih fonksiyonları hangi tarih aralığında doğru çalışır?
Belgeye göre 0000-01-01 00:00:00 ile 9999-12-31 23:59:59 arasında; bu aralığın dışındaki sonuçlar tanımsızdır. Hesaplar Gregoryen takvimi ve her günün tam 86400 saniye olduğunu varsayar, artık saniyeler dikkate alınmaz.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: SQLite öğrenme rehberi · Veri tipleri ve affinity · GROUP BY ve aggregate · Partial ve ifade indeksi · Matematik fonksiyonları · SQLite 3.54 yenilikler