Ana içeriğe geç
Kesit Python
Orta · Standart kütüphane

Tarih ve saat: datetime, timedelta, biçimleme ve saat dilimi

date/datetime/timedelta ile tarih aritmetiği yapar, strftime/strptime ile metne çevirir, zoneinfo ile saat dilimi ekler, ay sonu ve iş günü hesaplarını güvenle kurarsın.

Uygulamada aç, 22 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Neden önemli?

Ilgaz Bank'ta "vadesi 30 gün sonra dolan krediler", Anadolu Market'te "son 7 günün siparişleri", İK'da "deneme süresi biten çalışanlar"... Hemen her iş sorusunun içinde tarih var. Tarihler metin olarak gelir ("2026-03-15", "15.03.2026", "15 Mart 2026"), hesap yapmak için nesneye çevirmen, sonra tekrar insanın okuyacağı biçime döndürmen gerekir. Yanlış yapılan tarih hesabı sessiz hata üretir: program çökmez ama rapor yanlış çıkar. Bu ders o hataları en baştan engellemeyi öğretir.

Üç temel tip

TipNe tutarÖrnek
dateYıl, ay, gündate(2026, 3, 15)
datetimeTarih + saat (+ isteğe bağlı saat dilimi)datetime(2026, 3, 15, 14, 30)
timedeltaSüre (gün, saniye, mikrosaniye)timedelta(days=30, hours=2)
timeYalnızca saattime(9, 0)
from datetime import date, datetime, timedelta

basvuru = date(2026, 3, 15)
vade = basvuru + timedelta(days=30)
print("Başvuru:", basvuru, "| Vade:", vade)
print("Haftanın günü (0=Pzt):", vade.weekday(), "| ISO (1=Pzt):", vade.isoweekday())

fark = date(2026, 12, 31) - basvuru
print("Yıl sonuna kalan gün:", fark.days, "| tip:", type(fark).__name__)

t = datetime(2026, 3, 15, 14, 30)
print("2 saat 45 dk sonra:", t + timedelta(hours=2, minutes=45))
print("Parçalar:", t.year, t.month, t.day, t.hour, t.minute)
print("Yalnızca tarih:", t.date(), "| yalnızca saat:", t.time())
print("Replace ile ayın 1'i:", t.replace(day=1).date())
timedelta neyi bilmez?timedelta'da ay ve yıl yoktur; çünkü "1 ay" 28-31 gün arasında değişir. Ay eklemek için replace() ile yıl/ayı kendin hesaplarsın (aşağıda) ya da üçüncü parti dateutil.relativedelta kullanırsın.

Metne çevirme: strftime ve strptime

strftime (string format time) nesneyi metne, strptime (string parse time) metni nesneye çevirir. İkisi de aynı biçim kodlarını kullanır. Ezberlemek yerine tabloya bak:

KodAnlamÖrnek
%Y / %yYıl 4 / 2 hane2026 / 26
%m / %dAy / gün (sıfır dolgulu)03 / 05
%H / %M / %SSaat / dakika / saniye14 / 05 / 09
%B / %bAy adı uzun / kısa (dile bağlı)March / Mar
%A / %aGün adı uzun / kısaSunday / Sun
%jYılın günü074
%z / %ZUTC farkı / dilim adı+0300 / +03
from datetime import datetime

t = datetime(2026, 3, 5, 9, 7)
print(t.strftime("%d.%m.%Y %H:%M"))      # Türkiye'de alışık olunan biçim
print(t.strftime("%Y-%m-%d"))            # ISO, sıralanabilir
print(t.strftime("%d/%m/%y"))

# Metinden nesneye: biçim metinle BİREBİR uyuşmalı
s = "15.03.2026 14:30"
d = datetime.strptime(s, "%d.%m.%Y %H:%M")
print(d, type(d).__name__)

# Türkçe ay adı: %B yerel dile bağlıdır, kendin eşle
AYLAR = ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran",
         "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"]
print(f"{d.day} {AYLAR[d.month - 1]} {d.year}")
%B ve %A dile bağlıdırSunucunun yerel ayarına (locale) göre "March" ya da "Mart" basar; çoğu sunucuda İngilizcedir. Türkçe rapor için ay/gün adlarını kendi listenden al; strptime ile Türkçe ay adı ayrıştırmaya çalışma.

ISO 8601: makineler arası ortak dil

2026-03-15T14:30:00+03:00 biçimi ISO 8601'dir. API'ler, JSON ve veritabanları bunu kullanır; metin olarak sıralandığında kronolojik sıra çıkar. Python'da isoformat() ve fromisoformat() bu işi görür.

from datetime import datetime, date

d = datetime(2026, 3, 15, 14, 30)
print(d.isoformat())
print(d.isoformat(sep=" ", timespec="minutes"))
print(datetime.fromisoformat("2026-03-15T14:30:00"))
print(datetime.fromisoformat("2026-03-15 14:30:00+03:00"))
print(date.fromisoformat("2026-03-15"))

# Metin sıralaması = kronolojik sıralama
print(sorted(["2026-03-15", "2025-12-01", "2026-01-20"]))

Saat dilimi: naive ve aware

Saat dilimi bilgisi olmayan datetime'a naive, olana aware denir. İkisini karşılaştıramaz ya da çıkaramazsın; Python TypeError verir. Kural: sistem içinde UTC sakla, kullanıcıya gösterirken yerel dilime çevir. zoneinfo (3.9+) IANA veritabanını kullanır; Türkiye için "Europe/Istanbul" (2016'dan beri sabit +03).

from datetime import datetime, timezone, timedelta
try:
    from zoneinfo import ZoneInfo
    IST = ZoneInfo("Europe/Istanbul")
except Exception:            # bazı kısıtlı ortamlarda tz veritabanı olmayabilir
    IST = timezone(timedelta(hours=3), "+03")

utc_an = datetime(2026, 3, 15, 11, 30, tzinfo=timezone.utc)
ist_an = utc_an.astimezone(IST)
print("UTC   :", utc_an.isoformat())
print("İst.  :", ist_an.isoformat())
print("Aynı an mı?", utc_an == ist_an)

# Naive'e dilim ekleme: replace (dönüştürmez, etiketler)
yerel = datetime(2026, 3, 15, 14, 30).replace(tzinfo=IST)
print("UTC'ye:", yerel.astimezone(timezone.utc).strftime("%H:%M %Z"))
print("Şu an UTC:", datetime.now(timezone.utc).tzinfo)
utcnow() kullanmadatetime.utcnow() naive döner ve 3.12'de kullanımdan kaldırılma uyarısı verir. Doğrusu datetime.now(timezone.utc); yerel saat için datetime.now(IST).

İş hesapları: ay sonu, iş günü, yaş

Finans ve İK'da en çok şu üç hesap sorulur. Hepsini standart kütüphaneyle çözebilirsin.

from datetime import date, timedelta
import calendar

def ay_sonu(d: date) -> date:
    son_gun = calendar.monthrange(d.year, d.month)[1]
    return d.replace(day=son_gun)

def ay_ekle(d: date, n: int) -> date:
    toplam = d.year * 12 + (d.month - 1) + n
    yil, ay = divmod(toplam, 12)
    gun = min(d.day, calendar.monthrange(yil, ay + 1)[1])   # 31 Ocak + 1 ay = 28/29 Şubat
    return date(yil, ay + 1, gun)

def is_gunu_ekle(d: date, n: int) -> date:
    while n > 0:
        d += timedelta(days=1)
        if d.weekday() < 5:        # 0-4 hafta içi
            n -= 1
    return d

print("Şubat sonu:", ay_sonu(date(2026, 2, 10)))
print("31 Ocak + 1 ay:", ay_ekle(date(2026, 1, 31), 1))
print("Cuma + 3 iş günü:", is_gunu_ekle(date(2026, 3, 13), 3))
print("Mart 2026 takvimi:", calendar.monthrange(2026, 3))   # (ilk gün, gün sayısı)

dogum = date(1998, 7, 20); bugun = date(2026, 3, 15)
yas = bugun.year - dogum.year - ((bugun.month, bugun.day) < (dogum.month, dogum.day))
print("Yaş:", yas)

Pratik: son 7 gün filtresi

from datetime import date, timedelta

siparisler = [
    ("S-1", "2026-03-10", 450), ("S-2", "2026-03-14", 120),
    ("S-3", "2026-03-01", 800), ("S-4", "2026-03-15", 60),
]
bugun = date(2026, 3, 15)
esik = bugun - timedelta(days=7)
son_hafta = [s for s in siparisler if date.fromisoformat(s[1]) >= esik]
print("Son 7 gün:", [s[0] for s in son_hafta], "toplam", sum(s[2] for s in son_hafta))
for s in son_hafta:
    gun_once = (bugun - date.fromisoformat(s[1])).days
    print(f"{s[0]}: {gun_once} gün önce")
Derinleş

Derinleş: süreler, zaman damgaları, haftalar ve yaz saati

Tarih ayrıştırma, biçimleme, saat dilimi ve iş günü hesabını gördün. Raporlama ve bankacılık kodunda bunların yanında şu sorular da sık gelir: "Bu iş ortalama kaç saat sürüyor?", "API bana 1773570600 gönderdi, bu ne zaman?", "Bu sipariş yılın kaçıncı haftasında?", "Berlin'deki müşteriye 24 saat sonra hatırlatma gönder, ama yaz saati değişiyor." Bu bölüm bunları yanıtlıyor. pandas ile zaman serisi işlemleri "Zaman serisi analizi" dersinde.

1. Süre aritmetiği: timedelta'yı sayıya çevirmek

timedelta içte yalnız gün, saniye ve mikrosaniye tutar. Toplam süreyi tek sayıya çevirmek için total_seconds() kullanırsın; .seconds niteliği ise günleri saymaz (yalnız gün içindeki kalan saniyeler) ve klasik bir hata kaynağıdır. İki timedelta birbirine bölünebilir: / ondalık oran, // tam kat sayısı verir. Negatif süreler "-1 day, 23:00:00" gibi görünür: Python günü negatif, saniyeyi pozitif tutar.

from datetime import datetime, timedelta

acilis = datetime(2026, 3, 14, 22, 40)
kapanis = datetime(2026, 3, 16, 9, 15)
sure = kapanis - acilis
print(sure)
print(sure.seconds, "<- günler YOK")
print(sure.total_seconds() / 3600, "saat")

saat, kalan = divmod(int(sure.total_seconds()), 3600)
print(f"{saat} sa {kalan // 60:02d} dk")

vardiya = timedelta(hours=7, minutes=30)
print(vardiya / timedelta(minutes=45), vardiya // timedelta(hours=2))
print(timedelta(hours=-1), timedelta(hours=-1).total_seconds())

2. Unix zaman damgası (timestamp)

Birçok API ve log sistemi zamanı "1970-01-01 00:00 UTC'den beri geçen saniye" olarak verir. datetime.fromtimestamp(ts, tz=timezone.utc) bunu aware datetime'a, dt.timestamp() geri çevirir. Tuzak: tz vermezsen sonuç sunucunun yerel saatine göre naive üretilir; naive bir datetime'ın timestamp()'ı da yerel saat varsayar. Aynı kod İstanbul'daki dizüstünde ve UTC'deki sunucuda 3 saat farklı sonuç verir.

from datetime import datetime, timezone, timedelta

ts = 1773570600
utc = datetime.fromtimestamp(ts, tz=timezone.utc)
print(utc.isoformat())
print(utc.astimezone(timezone(timedelta(hours=3))).strftime("%d.%m.%Y %H:%M"))
print(int(utc.timestamp()) == ts)
ms = 1773570600123                       # JavaScript/Java milisaniye verir
print(datetime.fromtimestamp(ms / 1000, tz=timezone.utc).isoformat(timespec="milliseconds"))

3. Haftalar, çeyrekler ve ISO yılı

İfadePazartesiPazarNot
d.weekday()06Hafta içi: < 5
d.isoweekday()17ISO standardı
d.isocalendar()(ISO yılı, hafta no, gün)Hafta Pazartesi başlar
(d.month - 1) // 3 + 1Çeyrek (1-4)Finans raporları
d - timedelta(days=d.weekday())Haftanın Pazartesi'siHaftalık kova anahtarı

Dikkat: ISO hafta yılı takvim yılıyla her zaman aynı değildir. 30 Aralık 2024 Pazartesi, ISO'ya göre 2025'in 1. haftasıdır; 1 Ocak 2027 ise 2026'nın 53. haftasıdır. Haftalık raporda anahtar olarak d.year değil isocalendar().year kullanmazsan yıl başındaki hafta ikiye bölünür.

from datetime import date, timedelta

for d in [date(2024, 12, 30), date(2026, 3, 15), date(2027, 1, 1)]:
    y, h, g = d.isocalendar()
    pazartesi = d - timedelta(days=d.weekday())
    ceyrek = (d.month - 1) // 3 + 1
    print(d, f"{y}-W{h:02d}", "gün", g, "| hafta başı", pazartesi, "| Ç" + str(ceyrek))
print(date.fromisocalendar(2026, 11, 1))      # ISO haftadan tarihe

4. Tarih aralıkları üretmek

range tarihlerle çalışmaz; kendi küçük üretecini (generator) yazarsın. Ay ay ilerlemek için gün eklemek yerine (yıl, ay) çiftini artırırsın. Bu iki kalıp raporlama kodunun yarısında vardır: eksik günleri sıfırla doldurmak, her ay için bir satır üretmek.

from datetime import date, timedelta

def gunler(bas, bit, adim=1):
    d = bas
    while d <= bit:
        yield d
        d += timedelta(days=adim)

def aylar(bas, bit):
    y, m = bas.year, bas.month
    while (y, m) <= (bit.year, bit.month):
        yield f"{y}-{m:02d}"
        y, m = (y + 1, 1) if m == 12 else (y, m + 1)

satis = {date(2026, 3, 2): 120, date(2026, 3, 4): 90}
print([satis.get(d, 0) for d in gunler(date(2026, 3, 1), date(2026, 3, 5))])
print(list(aylar(date(2025, 11, 20), date(2026, 2, 3))))

5. Yaz saati: duvar saati mi, gerçek süre mi?

Türkiye 2016'dan beri yaz saati (DST) uygulamıyor, ama Avrupa'daki müşteri, tedarikçi ya da sunucu uyguluyor. zoneinfo ile aware bir datetime'a timedelta(days=1) eklediğinde Python duvar saatini korur: saat değişimi gecesinde "1 gün sonra" gerçekte 23 saat sonradır. Gerçek geçen süre istiyorsan hesabı UTC'de yap, sonra yerele çevir.

from datetime import datetime, timedelta, timezone
try:
    from zoneinfo import ZoneInfo
    berlin = ZoneInfo("Europe/Berlin")
    once = datetime(2026, 3, 28, 12, 0, tzinfo=berlin)       # yaz saatinden önceki gün
    duvar = once + timedelta(days=1)                            # duvar saati: yine 12:00
    gercek = (once.astimezone(timezone.utc) + timedelta(hours=24)).astimezone(berlin)
    print(duvar, "| UTC farkı:", duvar.utcoffset())
    print(gercek, "<- gerçekten 24 saat sonra")
    print(duvar.astimezone(timezone.utc) - once.astimezone(timezone.utc), "<- '+1 gün' ile gerçekte geçen")
    print(gercek - once, "<- aynı dilimde çıkarma duvar saatine bakar (yanıltıcı)")
except Exception as e:
    print("Bu ortamda saat dilimi veritabanı yok:", type(e).__name__)
Aynı dilimdeki aware datetime farkıAynı tzinfo nesnesine sahip iki aware datetime'ı çıkarırsan Python duvar saatlerini çıkarır, UTC farkını hesaba katmaz. DST geçişini kapsayan süre ölçümlerinde iki tarafı önce astimezone(timezone.utc) ile UTC'ye çevir.
Ay eklemenin hazır yolupip install python-dateutil ile gelen relativedelta(months=+1) ay sonunu doğru kırpar (31 Ocak + 1 ay = 28/29 Şubat) ve "ayın son Cuma'sı" gibi kurallar yazdırır. Tarayıcıdaki bu ortamda da çalışır; kendi ay_ekle fonksiyonunu bakımını üstlenmek istemiyorsan tercih et.
Gerçek işteSLA (hizmet seviyesi) raporlarında süre "takvim saati" değil "çalışma saati" olarak ölçülür: hafta içi 09:00-18:00 dışında geçen zaman sayılmaz. Haftalık KPI tablolarında ISO hafta numarası kullanılır ve anahtar 2026-W11 biçiminde yazılır ki metin sıralaması kronolojik olsun. Dış sistemlerden gelen zaman damgalarının saniye mi milisaniye mi olduğu (10 hane / 13 hane) entegrasyonun ilk gününde doğrulanır; yanlış birim 1970 ya da 58000 yılı gibi absürt tarihler üretir.
Uzmanlaş

Üretim sistemlerinde tarih disiplini

Kurumsal kodda üç kural yerleşmiştir: (1) Veritabanı ve log'da her zaman UTC ve aware datetime sakla; (2) dönüşümü yalnızca sunum katmanında yap; (3) "bugün"ü fonksiyon içinde date.today() ile değil, parametre olarak al. Üçüncü kural test yazarken hayat kurtarır: vade_gecti(islem, bugun=date(2026,1,1)) deterministik test edilir, date.today() içeren fonksiyon ise her gün farklı sonuç verir.

Zaman damgası (timestamp) ile çalışırken datetime.fromtimestamp(ts, tz=timezone.utc) kullan; dilimsiz çağrı makinenin yerel saatini varsayar ve sunucu-geliştirici makinesi arasında fark çıkar. Yaz saati uygulayan dilimlerde (Avrupa, ABD) bir günün 23 ya da 25 saat olabileceğini unutma; "yarın aynı saat" hesabı için yerel dilimde timedelta(days=1) ekleyip tekrar astimezone yapmak gerekir. Türkiye 2016'dan beri sabit +03 kullandığı için bu sorun yerelde görünmez ama uluslararası müşteri verisinde görünür.

Performans ipucu: milyonlarca satırda strptime yavaştır; biçim sabit ISO ise fromisoformat birkaç kat hızlıdır. pandas'a geçtiğinde pd.to_datetime(..., format=...) aynı mantıkla çalışır.

Alıştırmalara geç (22)