İçindekiler
- Temel kural: Sakla UTC, göster yerel
- Tuzak 1: Sunucu, veritabanı ve PHP farklı saat dilimlerinde
- Tuzak 2: MySQL'de DATETIME ile TIMESTAMP
- Tuzak 3: Saat dilimini sabit bir fark sanmak
- Tuzak 4: Yaz saati geçişleri
- Tuzak 5: "Bugün" kimin bugünü?
- Tuzak 6: Belirsiz biçimler
- Tuzak 7: Gelecekteki yerel olaylar
- Özet
Tarih ve saat, yazılımda en basit görünen ama en çok hata çıkaran konulardan biridir. Bir günün 24 saat olduğunu, her yılın 365 gün olduğunu ya da her ülkenin saatinin sabit olduğunu varsayan kod, bir gün mutlaka yanılır.
Türkiye 2016'dan bu yana yaz saati uygulamıyor ve yıl boyunca UTC+3 kullanıyor. Bu durum yerel sistemlerde işleri kolaylaştırıyor. Ama yurt dışından müşterisi olan, yurt dışındaki sunucularda çalışan ya da yabancı servislerle konuşan her uygulama yine bu tuzaklarla karşılaşıyor.
Temel kural: Sakla UTC, göster yerel
Bütün tarihleri ve saatleri UTC olarak saklayın ve işleyin. Yerel saate yalnızca kullanıcıya gösterirken çevirin.
Neden? Çünkü UTC hiçbir zaman ileri ya da geri alınmaz ve belirsizlik içermez. "14:30" tek başına bir anlam taşımaz: İstanbul'da mı, Londra'da mı, New York'ta mı? Oysa 2026-09-25T11:30:00Z dünyanın her yerinde aynı ânı ifade eder.
Tuzak 1: Sunucu, veritabanı ve PHP farklı saat dilimlerinde
Üç farklı katmanın üç farklı saat dilimi olabilir:
timedatectl # işletim sistemi
php -r 'echo date_default_timezone_get();' # PHP
mysql -e "SELECT @@global.time_zone, @@session.time_zone, NOW();"
Biri UTC, biri Europe/Istanbul, biri SYSTEM ise; NOW() ile veritabanına yazılan zaman ile PHP'nin date() ile ürettiği zaman arasında üç saat fark çıkar. Bu katmanları tutarlı hâle getirin ve uygulamanın saat dilimini kodda açıkça belirtin:
date_default_timezone_set('UTC');
Tuzak 2: MySQL'de DATETIME ile TIMESTAMP
- DATETIME, ne yazarsanız onu saklar ve saat dilimi bilgisi tutmaz.
2026-09-25 14:30:00yazarsanız hangi saat diliminde olduğunu siz bilmek zorundasınız. - TIMESTAMP, değeri oturumun saat dilimine göre UTC'ye çevirerek saklar ve okurken tekrar oturumun saat dilimine çevirir. Ayrıca MySQL'de
2038-01-19tarihine kadar olan değerleri tutabilir.
Tutarlı bir yaklaşım şudur: DATETIME kullanın, içine her zaman UTC yazın ve bunu sütun adında ya da şema belgesinde belirtin (olusturma_utc).
Tuzak 3: Saat dilimini sabit bir fark sanmak
// YANLIŞ: Saat dilimi = sabit fark varsayımı
$yerel = $utc + 3 * 3600;
Bu kod Türkiye için bugün doğru sonuç verir. Ama Almanya'daki bir müşteri için yazda +2, kışın +1 olmalıdır. Saat dilimleri siyasi kararlarla değişir. Türkiye'nin kendisi de 2016'da değiştirdi. Bu yüzden sabit fark yerine her zaman IANA saat dilimi adlarını kullanın:
$an = new DateTimeImmutable('2026-09-25 11:30:00', new DateTimeZone('UTC'));
echo $an->setTimezone(new DateTimeZone('Europe/Berlin'))->format('d.m.Y H:i');
İşletim sisteminin saat dilimi veritabanı (tzdata) bu değişiklikleri takip eder. Bu yüzden onu da güncel tutun.
Tuzak 4: Yaz saati geçişleri
Yaz saati uygulayan ülkelerde yılda bir gün 23 saat, bir gün 25 saat sürer. Bunun sonuçları:
- Var olmayan saatler: Mart'ta saatler 02:00'den 03:00'e atlıyorsa, "02:30" o gün hiç yaşanmaz.
- İki kez yaşanan saatler: Ekim'de 02:30 iki kez yaşanır. Bu saatte zamanlanmış bir görev iki kez çalışabilir ya da hiç çalışmayabilir.
- "Bir gün ekle" ile "24 saat ekle" farklıdır. Geçiş gününde
+1 dayile+24 hoursfarklı sonuçlar verir.
Sunucularınızı ve cron görevlerinizi UTC'de çalıştırmak, bu sorunların hepsini sunucu tarafında ortadan kaldırır.
Tuzak 5: "Bugün" kimin bugünü?
"Bugünkü siparişler" raporu, UTC'de gece yarısından gece yarısına çalışırsa Türkiye saatiyle 03:00'ten 03:00'e kadar olan siparişleri gösterir. Gece 01:00'de verilen bir sipariş "dünün" raporunda görünür.
Bir günün sınırlarını kullanıcının saat diliminde hesaplayın, sonra sorgu için UTC'ye çevirin:
$tz = new DateTimeZone('Europe/Istanbul');
$bas = (new DateTimeImmutable('today', $tz))->setTimezone(new DateTimeZone('UTC'));
$son = $bas->modify('+1 day');
// WHERE olusturma_utc >= :bas AND olusturma_utc < :son
Tuzak 6: Belirsiz biçimler
09/10/2026 ABD'de 10 Eylül, Türkiye'de 9 Ekim demektir. Sistemler arası veri alışverişinde ve API'lerde her zaman ISO 8601 kullanın ve saat dilimini ekleyin:
2026-09-25T11:30:00Z
2026-09-25T14:30:00+03:00
İkisi de aynı ânı ifade eder ve yanlış okunamaz.
Tuzak 7: Gelecekteki yerel olaylar
Bir kural istisnası: Gelecekteki yerel bir olayı, örneğin "her gün İstanbul saatiyle 09:00'da" yapılacak bir toplantıyı, yalnızca UTC olarak saklarsanız ve o ülke saat kuralını değiştirirse toplantı kayar. Bu tür kayıtlarda yerel saati ve saat dilimi adını birlikte saklayın (09:00, Europe/Istanbul), UTC'ye çevirmeyi de olaya yakın zamanda yapın.
Özet
- Sunucular, veritabanı ve uygulama UTC'de çalışsın.
- Saklama ve hesaplama UTC'de; gösterim kullanıcının saat diliminde.
- Sabit fark değil,
Europe/Istanbulgibi saat dilimi adları. - Sistemler arasında ISO 8601.
- Tarih kütüphanelerinin değişmez (immutable) sürümlerini tercih edin;
DateTimeyerineDateTimeImmutable.
Bu beş kural, tarih ve saat hatalarının büyük çoğunluğunu ortaya çıkmadan önler.
Yorumlar
Henüz yorum yok. İlk yorumu siz yazın.