Ders 09 / 12
Zaman İşlemleri
Bir anın metne dönüp geri gelmesi ölçülür: an kendisi Equal ile korunuyor, ama sayısal saat dilimi ofseti geri kurulsa bile bölgenin adı taşınmıyor. time.Duration'ın bir sayı değil kendi tipiyle taşınan bir değer olduğu gösterilir. Sınırlayıcı ölçüm, aynı anı gösteren iki değerin Equal ile eşit çıkarken == ve String() ile farklı çıktığını koşturarak ortaya koyuyor.
İçindekiler
Önceki ders bir bağın koşum zamanına değil derleme zamanına kurulabileceğini gösterdi: bir kaynak dosyanın içeriği, programın kendisiyle birlikte donuyordu. Zaman bunun tam tersi bir örnek sunuyor — hiçbir değer, “an” kadar koşumun kendisine bağlı. Bir an, ölçüldüğü ânın kendisiyle tanımlanıyor; onu bir metne yazıp geri okumak, önceki iki derste ölçülen kayıplardan farklı bir tür kayba yol açıyor.
Bu farkın somut sonucu şu: dosya ve arabellek dersinde sınırı geçen veri hiçbir zaman tip bilgisi taşımıyordu — kayıp her defasında tamdı. Zaman değerinde durum daha ince: sınırı geçen metin bazı bilgiyi taşıyor, bazısını taşımıyor, ve hangisinin hangi kategoriye girdiği kullanılan yazım düzenine bağlı. Bu ders üç ayrı değeri (an, saat dilimi, süre) aynı “metne dönüp geri gelme” sınavından geçirip her birinin ne kadarının hayatta kaldığını ayrı ayrı ölçüyor.
Belirlenimcilik notu: bu derste gerçek saat hiç okunmuyor (time.Now hiçbir yerde
çağrılmıyor). Bütün örnekler sabit bir metinden ayrıştırılan tek bir ana dayanıyor;
karşılaştırmalar da bu sabit anın üzerine kuruluyor.
Sabit Bir Andan Başlamak
Go’nun standart kitaplığında bir anı temsil eden tip time.Time; time.Parse bir dizgiyi
belirli bir düzene (layout) göre okuyup bir time.Time değeri üretiyor. Bu derste
kullanılan düzen RFC 3339 — ağ ve dosya biçimlerinde yaygın kullanılan, saat dilimini de
taşıyan bir zaman yazımı.
// sabit-an.go — gercek saat okunmuyor, an sabit bir metinden ayristiriliyor
package main
import (
"fmt"
"time"
)
func main() {
sabitAn, err := time.Parse(time.RFC3339, "2026-08-23T10:00:00Z")
if err != nil {
panic(err)
}
fmt.Printf("ayristirilan deger tipi=%T\n", sabitAn)
fmt.Println("saat dilimi (Z = UTC) :", sabitAn.Location())
}
ayristirilan deger tipi=time.Time saat dilimi (Z = UTC) : UTC
Dizginin sonundaki Z harfi, RFC 3339 düzeninde “UTC, sıfır ofset” anlamına geliyor;
time.Parse bunu tanıyıp sonucu doğrudan time.UTC konumuna bağlıyor. Bu ders boyunca
kullanılan tek “gerçek” veri bu sabit dizgi; ondan sonraki her adım bu değerden türetiliyor.
time.Parsein ikinci argümanı ayrıştırılacak metin, ilk argümanı ise beklenen düzen —
metnin kendisi değil, metnin biçiminin bir örneği. Bu örnek yalnızca yer tutucu değil, gerçek
bir tarih: time.RFC3339 sabiti Go’nun standart kitaplığında 2006-01-02T15:04:05Z07:00
biçiminde tanımlı, ve bu belirli tarih (ay/gün/saat/dakika/saniye/yıl’ın Go’nun kendi iç
sıralamasında karşılık gelen rakamları) düzeni belirtmenin standart yolu; başka bir dilin
YYYY-MM-DD gibi soyut yer tutucularından farklı bir yaklaşım.
Bir time.Time değeri her zaman aynı türden veri taşımıyor. Yalnızca time.Now()
çağrısından gelen değerler, saat farkı ölçümlerinde kullanılan bir tek yönlü sayaç
okuması (duvar saatinden ayrı, yalnızca ileri akan bir sayım) taşıyor; time.Parse gibi
bir metinden türeyen değerler bu ek okumayı hiç taşımıyor, yalnızca düz bir duvar saati
değeri oluyor. Bu ders yalnızca time.Parse kullandığından, ölçülen her değer bu ikinci,
“tek düze” türden.
Zaman Metne Dönüp Geri Geldiğinde
SS8. — Sabit an Format ile metne çevrilip aynı düzenle geri Parse ediliyor. Bu, Ağ ve
Veri Biçimleri konusunun ortak çekirdeğinde de bir satır olarak geçen geçiş (zaman değeri
metne ve geri: yeniden kuruluyor); o konunun bağlayıcı 1/5/7 dağılımına burada tek cümleyle
atıf yapılıyor, çekirdek yeniden kurulmuyor. Bu ders o satırı doğrulayıp saat dilimine özgü
bir ayrıntıyla genişletiyor.
// metne-geri.go — bir an metne yazilip ayni duzenle geri okunuyor
package main
import (
"fmt"
"time"
)
func main() {
sabitAn, _ := time.Parse(time.RFC3339, "2026-08-23T10:00:00Z")
metin := sabitAn.Format(time.RFC3339)
geri, _ := time.Parse(time.RFC3339, metin)
fmt.Println("metne donen yazim :", metin)
fmt.Println("ayni an mi (Equal):", geri.Equal(sabitAn))
fmt.Printf("geri gelen tip : %T\n", geri)
}
metne donen yazim : 2026-08-23T10:00:00Z ayni an mi (Equal): true geri gelen tip : time.Time
Bir time.Time değeri bir dizgiye yazıldığında tip bilgisi tamamen kayboluyor — sonuç sıradan
bir metin, içinde “bu bir zaman” diyen hiçbir işaret yok. Ama time.Parse çağıran taraf
elindeki dizginin bir zaman olduğunu zaten biliyor ve doğru düzeni vererek onu geri
kuruyor; kayan bilgi, çağıranın elindeki bu bilgiyle yeniden kuruluyor. Equal sonucu
true: geri kurulan an, orijinal anla birebir aynı. Konunun ölçü ekseninde bu satır
“yeniden kuruluyor” sınıfına giriyor — tıpkı bir yapı etiketinin JSON’dan geri kurulması gibi,
burada da geri kurmayı sağlayan şey dizginin kendisi değil, çağıranın bildiği düzen.
Saat Dilimi Adı Taşınmıyor
Yukarıdaki ölçüm bir ayrıntıyı gizliyor: sabit an tam olarak UTC’de, yani sıfır ofsette.
RFC 3339 düzeni sıfır ofseti Z harfiyle özel olarak işaretlediği için, geri kurulan değer de
tam olarak time.UTC’ye bağlanıyor. Sıfırdan farklı bir ofset söz konusu olduğunda tablo
değişiyor.
SS9. — Sabit an, adı BolgeA olan (gerçek bir coğrafi bölgeye değil, yalnızca +03:00
ofsetine sahip kurgusal bir saat dilimine) çevriliyor, metne yazılıp geri okunuyor. Ölçüm
hem sayısal ofseti hem bölge adını ayrı ayrı karşılaştırıyor.
// bolge-adi.go — sayisal ofset korunuyor, bolgenin adi tasinmiyor
package main
import (
"fmt"
"time"
)
func main() {
sabitAn, _ := time.Parse(time.RFC3339, "2026-08-23T10:00:00Z")
bolgeA := time.FixedZone("BolgeA", 3*3600)
yerel := sabitAn.In(bolgeA)
metin := yerel.Format(time.RFC3339)
geri, _ := time.Parse(time.RFC3339, metin)
yerelAd, yerelOfset := yerel.Zone()
geriAd, geriOfset := geri.Zone()
fmt.Println("metne donen yazim :", metin)
fmt.Println("ayni an mi (Equal):", yerel.Equal(geri))
fmt.Println("ofset korunuyor mu:", yerelOfset == geriOfset)
fmt.Println("bolge adi tasindi mi:", yerelAd == geriAd)
}
metne donen yazim : 2026-08-23T13:00:00+03:00 ayni an mi (Equal): true ofset korunuyor mu: true bolge adi tasindi mi: false
RFC 3339 düzeni bir saat diliminin adını hiç taşımıyor, yalnızca sayısal ofsetini
(+03:00) yazıyor. Geri okunduğunda time.Parse bu ofseti doğru biçimde geri kuruyor — an
hâlâ aynı, Equal hâlâ true, ofset karşılaştırması da true. Ama bölge adı
("BolgeA") hiçbir yerde metne yazılmadığı için geri gelmiyor; Parsein ürettiği yeni
konumun adı, orijinal "BolgeA"dan farklı çıkıyor. Bu, kaydın önceki dersle aynı biçimde
okunuyor: sayısal ofset yeniden kuruluyor, çünkü metnin kendisi onu taşıyor; bölge adı ise
kayboluyor, çünkü metnin kendisi onu hiç taşımıyor. Aynı geçiş, taşınan bilginin parçasına
göre iki farklı sınıfa giriyor.
time.Duration Bir Tiptir, Bir Sayı Değil
Bir sürenin kendisi de Go’da ayrı bir tip taşıyor: time.Duration, altında bir int64
saklayan ama kendi başına bir tip olan, nanosaniye cinsinden bir süre. Bir sayıdan farkı,
bir sayının aksine kendi biçimlendirmesini (1h30m0s gibi) taşıması ve başka bir süreyle
karıştırılmasının derleyici tarafından reddedilmesi.
SS10. — Bir süre dizgisi ("1h30m") ayrıştırılıp tipi ve biçimlendirilmiş yazımı
ölçülüyor; ardından aynı değerin ham bir tam sayı karşılığıyla eşit olup olmadığı ve iki katına
katlandığında biçimlendirmenin nasıl değiştiği koşturuluyor.
// sure-tipi.go — time.Duration kendi tipini tasiyor, ham sayidan ayri
package main
import (
"fmt"
"time"
)
func main() {
sure, _ := time.ParseDuration("1h30m")
fmt.Printf("sure tipi=%T deger=%s\n", sure, sure)
fmt.Println("int64 karsiligina esit mi:", int64(sure) == 5400_000_000_000)
ikiKat := sure * 2
fmt.Println("iki kati:", ikiKat)
fmt.Println("saat cinsinden:", ikiKat.Hours())
}
sure tipi=time.Duration deger=1h30m0s int64 karsiligina esit mi: true iki kati: 3h0m0s saat cinsinden: 3
sure değişkeninin altında yatan veri bir int64 — nanosaniye sayısı — ama %T onu ham bir
tam sayı olarak değil, time.Duration olarak gösteriyor, ve fmt.Println onu doğrudan
yazdırdığında 5400000000000 değil 1h30m0s çıkıyor. Bu, tam sayı ile süre arasında
derleyicinin kurduğu bir ayrım: sureün altındaki int64 değeri ile başka bir int64
değişken arasında örtük dönüşüm yok, ikisi ayrı tipler. ikiKat := sure * 2 çarpımı süre
tipini koruyor ve biçimlendirmesi de otomatik olarak güncelleniyor — 3h0m0s, Hours()
yöntemiyle sorgulandığında da 3 veriyor.
Süre de, tıpkı bir an gibi, metne dönüp geri gelebiliyor — ve bu geçiş bir anın geçişinden daha az kayıplı çıkıyor.
SS11. — sure değişkeni String() ile metne çevrilip aynı metin ParseDuration ile
geri okunuyor; ayrıca sözdizimi bozuk bir metin de deneniyor.
// sure-metne-geri.go — sure metne donup geri geliyor, gecersiz metin reddediliyor
package main
import (
"fmt"
"time"
)
func main() {
sure, _ := time.ParseDuration("1h30m")
metin := sure.String()
geri, hataGeri := time.ParseDuration(metin)
fmt.Println("sure metne donusumu:", metin)
fmt.Println("geri ayni deger mi :", geri == sure)
fmt.Println("geri okuma hatasiz mi:", hataGeri == nil)
_, hataGecersiz := time.ParseDuration("1h30mx")
fmt.Println("gecersiz metin hata verdi mi:", hataGecersiz != nil)
}
sure metne donusumu: 1h30m0s geri ayni deger mi : true geri okuma hatasiz mi: true gecersiz metin hata verdi mi: true
Bir anın metne dönüşünden farklı olarak, time.Durationın yazımı hiçbir bilgi kaybetmiyor:
geri == sure karşılaştırması true, çünkü String()ün ürettiği metin (1h30m0s) süreyi
oluşturan her birimi (saat, dakika, saniye) tam olarak taşıyor — bir sürenin bir “adı” ya da
“bağlamı” yok, taşıdığı tek bilgi kendi uzunluğu, ve bu bilgi metne tam olarak sığıyor. Bu,
saat diliminin adını taşıyamayan bir önceki ölçümle tam bir karşıtlık: kaybın olup olmaması,
taşınan değerin kendisinin ne kadar bilgi içerdiğine bağlı, kullanılan yazım biçiminin
kendisine değil. Son satır ayrı bir gözlem ekliyor: sözdizimi bozuk bir süre metni sessizce
0a düşmüyor, ParseDuration bunu bir hatayla reddediyor — süre burada da derleme zamanı
değil ama en azından ayrıştırma zamanı bir denetimden geçiyor.
Sınırlayıcı Ölçüm: Aynı An, İki Yazım
Buraya kadarki ölçümler bir anı tek bir saat diliminde inceledi. Aşağıdaki adım aynı anı
iki farklı kurgusal bölgede temsil edip üç ayrı karşılaştırma aracını (Equal, ==,
String) yan yana koşturuyor.
SS12. — İki kurgusal bölge (BolgeA +03:00, BolgeB −05:00) aynı sabit ana uygulanıyor.
Üçü de “aynı mı” sorusuna farklı bir yanıt veriyor.
// iki-yazim.go — ayni an iki bolgede, uc ayri karsilastirma araciyla olculuyor
package main
import (
"fmt"
"time"
)
func main() {
sabitAn, _ := time.Parse(time.RFC3339, "2026-08-23T10:00:00Z")
bolgeA := time.FixedZone("BolgeA", 3*3600)
bolgeB := time.FixedZone("BolgeB", -5*3600)
gorunumA := sabitAn.In(bolgeA)
gorunumB := sabitAn.In(bolgeB)
fmt.Println("BolgeA yerel saat :", gorunumA.Format("15:04"))
fmt.Println("BolgeB yerel saat :", gorunumB.Format("15:04"))
fmt.Println("Equal ile ayni mi :", gorunumA.Equal(gorunumB))
fmt.Println("== ile ayni mi :", gorunumA == gorunumB)
fmt.Println("String() ayni mi :", gorunumA.String() == gorunumB.String())
}
BolgeA yerel saat : 13:00 BolgeB yerel saat : 05:00 Equal ile ayni mi : true == ile ayni mi : false String() ayni mi : false
Üç karşılaştırma aracı üç farklı soruya yanıt veriyor. Equal, iki değerin temsil ettiği
anı karşılaştırıyor — ikisi de aynı ana ait olduğu için sonuç true. == işleci ise
time.Timein iç yapısını birebir karşılaştırıyor; bu yapı saat dilimi bilgisini de
içerdiğinden, iki farklı bölgeye bağlı iki değer == ile eşit çıkmıyor. String() de aynı
nedenle farklı: her biri kendi yerel saatini ve ofsetini yazıyor, biri “13:00 +03:00” biri
“05:00 -05:00” — metin düzeyinde hiç örtüşmüyorlar.
Bu üçü arasındaki fark, Go’nun belgesinde açıkça uyarılan bir tuzak: == işleci time.Time
değerleri için kullanılmaması gereken bir karşılaştırma, çünkü onu kullanan kod “aynı an
mı” sorusunu değil, “aynı temsil mi” sorusunu yanıtlıyor — çoğu zaman istenen ilk soru, ama
işlecin kendisi ikinciye yanıt veriyor. Konunun ölçü ekseninde bu, bu derse özgü bir sınır
çiziyor: iki değer aynı anı gösterdiğinde bile, karşılaştırma aracının kendisi seçilen
soruyu belirliyor; “aynı” sözcüğü tek başına hangi karşılaştırmanın kastedildiğini söylemiyor.
Aynı ayrım sıralama için de geçerli. Before ve After yöntemleri, tıpkı Equal gibi,
temsile değil ana bakıyor; iki değer farklı bölgelerde yazılmış olsa bile hangisinin
zamanda daha önce geldiğini doğru söylüyor.
// siralama.go — Before ve After da temsile degil ana bakiyor
package main
import (
"fmt"
"time"
)
func main() {
sabitAn, _ := time.Parse(time.RFC3339, "2026-08-23T10:00:00Z")
birSaatSonra := sabitAn.Add(time.Hour)
bolgeA := time.FixedZone("BolgeA", 3*3600)
gorunumA := sabitAn.In(bolgeA)
gorunumSonraB := birSaatSonra.In(time.FixedZone("BolgeB", -5*3600))
fmt.Println("gorunumA, gorunumSonraB'den once mi:", gorunumA.Before(gorunumSonraB))
fmt.Println("gorunumSonraB, gorunumA'dan sonra mi:", gorunumSonraB.After(gorunumA))
}
gorunumA, gorunumSonraB'den once mi: true gorunumSonraB, gorunumA'dan sonra mi: true
İki değer birbirinden tamamen farklı bölgelerde ve tamamen farklı yerel saatlerde yazılı
olmasına rağmen Before ve After doğru sırayı buluyor, çünkü ikisi de karşılaştırmayı
temsil edilen an üzerinden yapıyor — == işlecinin yaptığı gibi iç yapının birebir
eşleşmesini aramıyor. Bu, önceki paragrafın genel kuralını doğruluyor: time.Time
değerlerini karşılaştırırken Equal, Before ve After güvenli bir taban oluşturuyor, ==
ise yalnızca iki değerin aynı bölgede, aynı biçimde üretildiği bilindiğinde güvenli.
Özet
- Bir an metne dönüp aynı düzenle geri okunduğunda
Equalile korunuyor; bu, konunun ortak çekirdeğindeki “yeniden kuruluyor” satırının doğrulanmış hâli. - RFC 3339 düzeni bir saat diliminin sayısal ofsetini taşıyor ama adını taşımıyor: geri kurulan değerin ofseti orijinaliyle eşleşirken bölge adı eşleşmiyor.
time.Durationaltında birint64taşısa da kendi tipini koruyor; biçimlendirmesi ve aritmetiği bu tipe göre işliyor, ham bir sayıyla karıştırılmıyor. Metne dönüp geri gelmesi de bilgi kaybetmiyor, çünkü taşıdığı tek şey kendi uzunluğu.- Sınırlayıcı ölçüm: aynı anı gösteren iki değer
Equalile eşit çıkarken==veString()ile farklı çıkıyor — üç araç üç farklı soruya yanıt veriyor. BeforeveAfterdaEqualgibi ana bakıyor, temsile değil;==işleci ise yalnızca iki değerin aynı bölgede ve biçimde üretildiği bilindiğinde güvenli bir karşılaştırma.
Sonraki Adım
Bu derste bir zaman değerinin metne dönüşürken alan adı taşımadığı, yalnızca ham bir yazım
ürettiği görüldü — 2026-08-23T13:00:00+03:00 metninin hangi alanın hangi değeri olduğunu
söyleyen hiçbir işaret yok, yalnızca konumdan çıkarılabiliyor. Bir okuyucu bu metni doğru
yorumlayabiliyor çünkü RFC 3339 düzenini zaten biliyor — tıpkı bir önceki dersteki gömme
yönergesinin, hedef tipi zaten bilen bir değişkene içerik yerleştirmesi gibi. Ama her biçim bu
kadar cömert değil: konumdan çıkarılan bir alan, konum kaybolduğunda ya da biçim değiştiğinde
bir daha çıkarılamıyor.
Sıradaki ders bu boşluğu tam olarak dolduran bir biçimi ölçüyor: bir olayı günlüğe yazarken alan adlarını da taşımak, aynı kaydı hem insan hem başka bir program için ayrıştırılabilir kılıyor mu? Bu dersin sabit zaman damgası, sıradaki dersin günlük kayıtlarında da aynen kullanılacak — zaman burada da gerçek saatten değil, sabit bir metinden gelecek.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.