Cron ve Saat Dilimi: Türkiye'nin 2016 Kararı ve Yaz Saatinde Kaybolan İşler

Cron duvar saatine bakar, duvar saati de kararnameyle değişir
Özet (TL;DR)
- POSIX crontab saat dilimi hakkında tek kelime etmez.
CRON_TZ,TZve yaz saati geçişindeki davranış tamamen kullandığınız cron yazılımına bağlıdır. - Türkiye 8 Eylül 2016'da kalıcı +03'e geçti ve IANA bunu tzdata 2016g ile aldı. Daha eski veriyle kalan bir sistem bugün hâlâ kışın bir saat geride, yazın ise doğru görünür.
Etc/GMT-3UTC+3 demektir, işaret POSIX gereği terstir. Türkiye için doğru adEurope/Istanbul, çünkü 2016 öncesi geçmişi ve olası yeni kararları yalnızca o taşır.- Yaz saati uygulanan bir bölgede 02:30'a kurulu iş ilkbaharda hiç çalışmayabilir, sonbaharda iki kez çalışabilir. Alpine'daki BusyBox crond ikisini de yapar.
- Sağlam kurulum üç parçalıdır: zamanlayıcı UTC'de tetikler, yerel saat kararını uygulama verir, iş de ait olduğu iş gününe göre bir anahtarla tekrar çalışmaya karşı kilitlenir.
Her gece 03:00'te başlaması gereken yedekleme, loglarda 02:00'de başlamış görünüyor. Crontab'a kimse dokunmamış, NTP senkron, sunucu aylardır yeniden başlatılmamış. Sunucunun date çıktısıyla telefonunuzdaki saat arasında tam bir saat fark var ve bu fark yalnızca kış aylarında ortaya çıkıyor.
Cron'un burada bir kabahati yok. Kendisine söylenen yerel saate bakıyor, o yerel saati hesaplayan veri ise eski. Yaz saati uygulayan ülkelerde aynı kökten yılda iki gece ayrıca hasar çıkar: hiç çalışmayan ve iki kez çalışan işler. Sırayla cron'un saati nereden okuduğuna, Türkiye'nin 2016 kararına ve kalıcı çözüme bakalım.
Cron saati nereden okur
Cron daemon'ı her dakika uyanır, o anki yerel saati hesaplar ve crontab satırlarını bu saatle karşılaştırır. Yerel saat, daemon sürecinin ortamındaki TZ değişkeninden, o yoksa /etc/localtime dosyasından gelir. Debian'ın cron man sayfası daemon'ın varsa /etc/timezone dosyasındaki tanımı kullandığını da yazar. Saat dilimini değiştirdiyseniz cron servisini yeniden başlatın. Çalışan bir sürecin eski ayarı tutmadığından ancak böyle emin olursunuz.
Standart bu konuda şaşırtıcı derecede sessiz. POSIX'in crontab tanımı (IEEE Std 1003.1-2017) beş zaman alanını ve komutun hangi ortam değişkenleriyle çalışacağını anlatır ama saat diliminden, yaz saatinden ya da saat geri alındığında ne olacağından hiç söz etmez. Boşluğu her cron yazılımı kendince doldurmuş:
| Cron yazılımı | Nerede | Crontab başına saat dilimi | Yaz saati geçişi |
|---|---|---|---|
| Vixie cron (Debian paketi) | Debian, Ubuntu | Yok. TZ= yalnızca komutun ortamını değiştirir | 3 saatten küçük kaymada sabit saatli işleri telafi eder |
| cronie | RHEL, CentOS, Fedora | CRON_TZ= ile var | Aynı telafi: atlanan iş hemen çalışır, tekrarlanan saatte ikinci kez çalışmaz |
| BusyBox crond | Alpine ve küçük imajlar | Yok | Telafi yok: atlanan saat atlanır, tekrarlanan saat yeniden çalışır |
| Kubernetes CronJob | Cluster | spec.timeZone (1.24 alfa, 1.25 beta) | Belge nadiren iki Job ya da hiç Job oluşabileceğini yazar |
Uyarı
TZ=Europe/Berlin yazmak işlerin saatini değiştirmez. Debian'ın crontab(5) sayfasına göre daemon tek bir saat diliminde çalışır ve TZ yalnızca çalıştırılan komutların ortamına geçer. cronie'de CRON_TZ= satırı takvimi gerçekten değiştirir. Bir dağıtımdan diğerine taşınan crontab bu yüzden sessizce farklı davranabilir.Türkiye 2016: saat kımıldamadı, veri eskidi
Türkiye 2016'ya her yıl olduğu gibi yaz saatiyle girdi. 14 Mart 2016 tarihli ve 2016/8589 sayılı Bakanlar Kurulu Kararı, saatlerin 30 Ekim 2016 Pazar günü 04.00'te bir saat geri alınacağını söylüyordu. Sonra 8 Eylül 2016 tarihli ve 29825 sayılı Resmî Gazete'de 2016/9154 sayılı karar yayımlandı. Yaz saati yıl boyu sürecek, 30 Ekim'deki geri alma ibaresi yürürlükten kalkacaktı. Duvardaki saat açısından bunun tek sonucu sonbaharda saatin geri alınmamasıydı. tzdata açısından ise Türkiye EET/EEST düzeninden çıkıp kalıcı +03'e geçti.
IANA Time Zone Database bu değişikliği 13 Eylül 2016 tarihli 2016g sürümüyle aldı. NEWS dosyasındaki kayıt iki satır. Türkiye EET/EEST'den (+02/+03) kalıcı +03'e geçmiş, yürürlük tarihi 2016-09-07. Duyuru tz-announce listesine 28 Eylül'de düştü. Sürümden 30 Ekim'e yedi haftadan az vardı ve bu sürede verinin dağıtım paketlerine, oradan sunuculara, gömülü cihazlara ve imajlara ulaşması gerekiyordu. O gece güncellenmemiş her sistem eski kurala uydu ve saati 04:00'te 03:00'e aldı.
Sinsi olan kısım sonrası. Eski tzdata Türkiye için AB kuralını her yıl uygulamaya devam eder. Mart sonundan ekim sonuna kadar +03, geri kalanında +02. 2016f'de kalmış bir sistem yazın doğru saati gösterir, kışın bir saat geride kalır. Hata mevsimlik olduğu için temmuzda yapılan bir testte görünmez, ekim sonunda ortaya çıkar. Aşağıdaki betik IANA'nın 2016f ve 2016g arşivlerini zic ile derleyip aynı UTC anlarını üç ayrı bölgeye çeviriyor:
from datetime import datetime, timezonefrom zoneinfo import ZoneInfo def yukle(yol, ad): with open(yol, "rb") as f: return ZoneInfo.from_file(f, key=ad) bolgeler = [ yukle("zi2016f/Europe/Istanbul", "2016f Istanbul"), yukle("zi2016g/Europe/Istanbul", "2016g Istanbul"), yukle("zi2016g/Etc/GMT-3", "Etc/GMT-3"),]anlar = [datetime(2015, 12, 1, 9, 0, tzinfo=timezone.utc), datetime(2022, 1, 15, 9, 0, tzinfo=timezone.utc), datetime(2022, 7, 15, 9, 0, tzinfo=timezone.utc)] for an in anlar: print(f"UTC {an:%Y-%m-%d %H:%M}") for tz in bolgeler: yerel = an.astimezone(tz) print(f" {tz.key:15} {yerel:%H:%M} {yerel.tzname():4} {yerel:%z}")$ python3 eski_tzdata.pyUTC 2015-12-01 09:00 2016f Istanbul 11:00 EET +0200 2016g Istanbul 11:00 EET +0200 Etc/GMT-3 12:00 +03 +0300UTC 2022-01-15 09:00 2016f Istanbul 11:00 EET +0200 2016g Istanbul 12:00 +03 +0300 Etc/GMT-3 12:00 +03 +0300UTC 2022-07-15 09:00 2016f Istanbul 12:00 EEST +0300 2016g Istanbul 12:00 +03 +0300 Etc/GMT-3 12:00 +03 +030015 Ocak 2022 satırı meselenin özeti. 2016f'ye takılmış bir makine bugün 11:00 diyor, doğrusu 12:00. Container imajları burada ayrıca risklidir, çünkü bir imaj oluşturulduğu günün tzdata paketini dondurur ve altındaki sunucuyu güncellemek içindeki saati düzeltmez. Kendi saat dilimi kopyasını taşıyan yazılımlar da işletim sisteminin paketinden habersizdir. JDK bunlardan biri ve Oracle onun için ayrı bir tzupdater aracı yayımlar. Kaynaktan varsayılan ayarlarla derlenmiş PostgreSQL de kendi kopyasını kullanır.
Bir sistemin hangi kurala inandığını görmek için zdump yeter. Güncel veride 2016'nın sonbaharında tek geçiş 7 Eylül'dedir, eski veride ise 30 Ekim'deki geri alma hâlâ durur:
$ zdump -v -c 2016,2018 Europe/Istanbul | grep -E '(Sep|Oct) .* 2016 UT'# tzdata 2016gEurope/Istanbul Tue Sep 6 20:59:59 2016 UT = Tue Sep 6 23:59:59 2016 EEST isdst=1 gmtoff=10800Europe/Istanbul Tue Sep 6 21:00:00 2016 UT = Wed Sep 7 00:00:00 2016 +03 isdst=0 gmtoff=10800# tzdata 2016fEurope/Istanbul Sun Oct 30 00:59:59 2016 UT = Sun Oct 30 03:59:59 2016 EEST isdst=1 gmtoff=10800Europe/Istanbul Sun Oct 30 01:00:00 2016 UT = Sun Oct 30 03:00:00 2016 EET isdst=0 gmtoff=7200Kısaltma da değişti. 2016g yeni saat için uydurma bir ad yerine +03 kullanıyor. Logları EEST ya da EET kısaltmasıyla ayrıştıran bir betik 7 Eylül 2016'dan beri hiçbir şey bulamaz.
Europe/Istanbul mu, Etc/GMT-3 mü
2016'dan sonra sorunu kökünden çözmek isteyen bazı kurulumlar Etc/GMT-3 gibi sabit bir bölgeye geçti. Bugün ve gelecek için iki seçenek aynı saati verir, geçmiş için vermez. Yukarıdaki çıktının ilk bloğu 1 Aralık 2015'e ait. O sabah İstanbul'da saat 11:00'di (+02), Etc/GMT-3 ise 12:00 diyor. 2016 öncesi bir kaydı yerel saate çeviren her rapor bir saat kayar. Türkiye saat politikasını bir daha değiştirirse güncellemeyi de yalnızca Europe/Istanbul alır, Etc/GMT-3 sonsuza kadar +03 kalır.
İşaret ayrı bir tuzak. Etc bölgeleri POSIX TZ sözdizimini izler ve POSIX'te pozitif işaret Greenwich'in batısı demektir. tzdata'nın etcetera dosyası da bunun çoğu kişinin beklediğinin tersi olduğunu açıkça yazar. Yani Etc/GMT-3 UTC+3, Etc/GMT+3 ise UTC-3'tür:
$ TZ=Etc/GMT-3 date -d @1663095120 "+%F %R %Z %z"2022-09-13 21:52 +03 +0300$ TZ=Etc/GMT+3 date -d @1663095120 "+%F %R %Z %z"2022-09-13 15:52 -03 -0300İnsanların yaşadığı bir yer için her zaman bölge adını kullanın. Etc/GMT ailesi, tzdata'nın kendi notuna göre POSIX TZ dizesi kullanamayan denizdeki gemiler gibi özel durumlar için duruyor.
Yaz saati olan ülkelerde 02:30 işi
Türkiye 2016'dan beri yaz saati uygulamıyor, ama sunucularınız ya da kullanıcılarınız Avrupa'daysa mesele kapanmış değil. Orta Avrupa'da saat mart ayının son pazarı 02:00'de 03:00'e atlar, ekimin son pazarı 03:00'te 02:00'ye döner. 2022'de bu geceler 27 Mart ve 30 Ekim. İlkinde 02:00 ile 03:00 arası hiç yaşanmaz, ikincisinde iki kez yaşanır.
BusyBox crond'un mantığı çok düz. Her döngüde geçen süreyi dakika dakika yürür, her dakikayı localtime() ile yerel saate çevirir ve eşleşen işleri başlatır. Saniyeler UTC'de aktığı için yerel saat ilkbaharda 02:30'u hiç görmez, sonbaharda iki kez görür. Aynı algoritmayı Python'da kurup 30 2 * * * için Berlin saatiyle çalıştırdığımızda tablo şu:
from datetime import datetime, timedelta, timezonefrom zoneinfo import ZoneInfo BERLIN = ZoneInfo("Europe/Berlin") def saf_zamanlayici(baslangic, bitis, saat, dakika, tz): """Her UTC dakikasını yerel saate çevirip '30 2 * * *' ile eşleştirir.""" an = baslangic while an < bitis: yerel = an.astimezone(tz) if (yerel.hour, yerel.minute) == (saat, dakika): print(f" çalıştı: {yerel:%Y-%m-%d %H:%M %Z} (UTC {an:%H:%M})") an += timedelta(minutes=1) for gun in (datetime(2022, 3, 26), datetime(2022, 10, 29)): bas = gun.replace(tzinfo=timezone.utc) print(f"{gun:%Y-%m-%d} itibarıyla üç gün:") saf_zamanlayici(bas, bas + timedelta(days=3), 2, 30, BERLIN)$ python3 saf_cron.py2022-03-26 itibarıyla üç gün: çalıştı: 2022-03-26 02:30 CET (UTC 01:30) çalıştı: 2022-03-28 02:30 CEST (UTC 00:30)2022-10-29 itibarıyla üç gün: çalıştı: 2022-10-29 02:30 CEST (UTC 00:30) çalıştı: 2022-10-30 02:30 CEST (UTC 00:30) çalıştı: 2022-10-30 02:30 CET (UTC 01:30) çalıştı: 2022-10-31 02:30 CET (UTC 01:30)27 Mart satırı yok, 30 Ekim iki kez var. Günlük rapor üreten bir iş için bu bir eksik ve bir çift rapor demek. Para çeken, e-posta gönderen ya da fatura kesen bir işte aynı hata müşteriye kadar ulaşır.
Vixie cron ve cronie bu iki geceyi telafi etmeye çalışır. Atlanan sabit saatli işi geçişten hemen sonra çalıştırır, geri dönüşte ikinci kez çalıştırmaz. Bu koruma 3 saatten küçük kaymalarla ve belirli bir saatte çalışan işlerle sınırlı. Debian'ın man sayfasına göre saat ya da dakika alanında * bulunan satırlar yeni saate göre hemen çalışır. */10 2 * * * gibi bir satır ilkbaharda o saatin altı çalışmasının hepsini kaybeder, sonbaharda iki katı çalışır.
Tehlikeli saat bölgeye göre değişir. ABD'de geçiş yerel 02:00'de olur ve geri dönüşte tekrarlanan aralık 01:00 ile 02:00 arasıdır. New York'ta 02:30 işi 13 Mart 2022'de atlanır ama 6 Kasım'da tek kez çalışır, iki kez çalışan 01:30'daki iştir. "Gece 02:00 ile 03:00 arasına iş koymayın" kuralı Berlin'de işe yarar, New York'ta yarı yarıya.
Kubernetes'te CronJob takvimi varsayılan olarak kube-controller-manager'ın saat dilimine göre yorumlanır. Mayıs 2022'de çıkan 1.24, spec.timeZone alanını CronJobTimeZone feature gate'inin arkasında alfa olarak getirdi. Ağustos 2022'de çıkan 1.25'te feature gate beta oldu ve varsayılan olarak açık geliyor. Alan bir IANA bölge adı alır, sistemde saat dilimi verisi yoksa binary'lere gömülü Go standart kütüphanesi verisi yedek olarak kullanılır. Belgenin "CronJob limitations" bölümü de bu konuda dürüst davranıyor ve planlanan tek bir çalışma için nadiren iki Job oluşabileceğini ya da hiç oluşmayabileceğini, bu yüzden işlerin idempotent olması gerektiğini açıkça yazıyor.
Saati cron'dan alıp uygulamaya vermek
Sunucu UTC'de durur
UTC'nin yaz saati yok ve hiçbir bakanlar kurulu onu değiştiremez. Sunucuyu, container'ları ve veritabanı oturumlarını UTC'de tutarsanız cron'un gördüğü saat yıl boyu düz bir çizgi olur, loglar da makineler arasında doğrudan karşılaştırılabilir hâle gelir. Linux'ta bunun yolu timedatectl set-timezone Etc/UTC. HTTP de Date başlığını her zaman GMT ile yazar, saat dilimi pazarlığına hiç girmez.
UTC'nin bir bedeli var. İstanbul'da 09:00'da çalışması gereken iş crontab'da 0 6 * * * olur. Türkiye için bu dönüşüm 2016'dan beri sabit. Berlin için değil. Orada 09:00 yazın 07:00 UTC, kışın 08:00 UTC eder. Yerel saate bağlı her işte hesabı cron'dan alıp uygulamaya vermek gerekir. Alanları tek tek doğrulamak için Cron İfadesi Oluşturucu işinizi görür.
Yerel saat kararını uygulama verir
Kalıp basit. Cron UTC'de sık aralıklarla, örneğin her beş dakikada bir tetik yollar, "bölgemde bu işin zamanı geldi mi" sorusunu ise uygulama cevaplar. Python 3.9 ile standart kütüphaneye giren zoneinfo (PEP 615) bu hesabı dış bağımlılık olmadan yapıyor. İncelik, var olmayan ve iki kez yaşanan saatler için bilinçli bir politika seçmekte:
from datetime import date, datetime, time, timezonefrom zoneinfo import ZoneInfo def calisma_ani(gun, saat, tz): """Yerel 'gun saat' için tek bir UTC anı döndürür. Var olmayan saat (ileri geçiş): boşluk kadar ileri kaydırılır. İki kez yaşanan saat (geri geçiş): ilk geçiş seçilir (fold=0). """ yerel = datetime.combine(gun, saat, tzinfo=tz) # fold=0 utc = yerel.astimezone(timezone.utc) geri = utc.astimezone(tz) if geri.replace(tzinfo=None) != yerel.replace(tzinfo=None): durum = "yok, kaydırıldı" elif yerel.replace(fold=1).utcoffset() != yerel.utcoffset(): durum = "iki kez var, ilki" else: durum = "normal" return utc, geri, durum berlin = ZoneInfo("Europe/Berlin")for gun in (date(2022, 3, 27), date(2022, 9, 13), date(2022, 10, 30)): utc, yerel, durum = calisma_ani(gun, time(2, 30), berlin) print(f"{gun} UTC {utc:%H:%M} yerel {yerel:%H:%M %Z} {durum}")$ python3 gunluk_is.py2022-03-27 UTC 01:30 yerel 03:30 CEST yok, kaydırıldı2022-09-13 UTC 00:30 yerel 02:30 CEST normal2022-10-30 UTC 00:30 yerel 02:30 CEST iki kez var, ilkiKod iki şeye dayanıyor. Birincisi PEP 495 ile gelen fold özniteliği. İki kez yaşanan saatte fold=0 ilk geçişi, fold=1 ikincisini seçer. İkincisi round-trip kontrolü. Var olmayan bir yerel saati UTC'ye çevirip geri getirdiğinizde aynı saati bulamazsınız, 27 Mart'taki 02:30 böyle yakalanıyor ve boşluk kadar ileri, 03:30'a kayıyor. Siz 03:00'ü seçebilir ya da o günü bilerek atlayabilirsiniz. Yeter ki seçim kodda yazılı dursun, zamanlayıcının keyfine kalmasın.
İş iki kez gelirse ikincisi boşa düşer
Hiçbir zamanlayıcı tam olarak bir kez çalışmayı garanti etmiyor. Kubernetes bunu belgesine yazmış, cron'lar yaz saatinde kendi bildiğini okuyor, bir de elle tetiklenen ve yeniden denenen işler var. Savunmayı işin kendisine koyun. İşi çalıştığı ana göre değil, ait olduğu iş gününe göre bir anahtarla kilitleyin:
CREATE TABLE is_calismasi ( is_adi TEXT NOT NULL, is_gunu TEXT NOT NULL, -- iş hangi günün işi (yerel takvim) baslangic TEXT NOT NULL, -- RFC 3339, offset'li PRIMARY KEY (is_adi, is_gunu)); -- 30 Ekim 2022, Berlin: 02:30 CEST ve 02:30 CET, iki tetikINSERT INTO is_calismasi VALUES ('gunluk-rapor', '2022-10-30', '2022-10-30T02:30:00+02:00') ON CONFLICT DO NOTHING;SELECT changes() AS eklenen;INSERT INTO is_calismasi VALUES ('gunluk-rapor', '2022-10-30', '2022-10-30T02:30:00+01:00') ON CONFLICT DO NOTHING;SELECT changes() AS eklenen;$ sqlite3 :memory: < kilit.sql10İkinci tetik 30 Ekim 02:30 CET'te gelse de aynı (is_adi, is_gunu) çiftine çarpar ve hiçbir şey eklemez. Uygulama changes() sıfır döndüğünde işi atlar. Anahtarın UTC anı değil yerel iş günü olması bilinçli. 02:30 CEST ile 02:30 CET iki ayrı UTC anıdır, ama ikisi de 30 Ekim'in raporudur. PostgreSQL'de aynı şeyi INSERT ... ON CONFLICT DO NOTHING ve bir birincil anahtarla yaparsınız.
Zaman damgasını saat diliminden bağımsız saklayın
Olmuş bir olayı UTC olarak saklayın, Unix epoch ya da offset'li RFC 3339 biçiminde (2022-09-13T18:52:00Z veya 2022-09-13T21:52:00+03:00). Epoch tzdata'dan etkilenmez. 2016'daki karışıklıkta da UTC tutulan kayıtlar değil, yalnızca ekrandaki gösterim yanlıştı. Tek tek değerleri Unix Zaman Damgası Dönüştürücü ile çevirip kontrol edebilirsiniz. RFC 3339'da ince bir ayrım daha var. -00:00 offset'i "UTC anı biliniyor, yerel offset bilinmiyor" demektir, Z ya da +00:00 ise UTC'nin bu zaman için tercih edilen referans olduğunu söyler.
Gelecekteki yerel bir olayda kural tersine döner. Gelecek yıl İstanbul'da 10:00'a konmuş bir toplantıyı bugünden UTC'ye çevirip saklarsanız, kurallar değiştiğinde toplantı 09:00'a ya da 11:00'e kayar. 2016'da eski kuralla önceden hesaplanmış, 30 Ekim sonrasına düşen her UTC anı bu yüzden bir saat yanlıştı. Gelecek için yerel saati ve IANA bölge adını birlikte saklayın, UTC'ye çalışma anında çevirin.
Docker ve Alpine: tzdata yoksa sessizce UTC
Alpine imajı küçük kalmak için tzdata paketini içermez. Hata mesajı yok. musl libc TZ değişkenindeki adı dosya olarak bulamazsa UTC'ye düşer ve kısaltmayı bile UTC diye yazar. Yanlış yazılmış bir ad da aynı sessizlikle geçer:
$ docker run --rm alpine:3.16 sh -c 'TZ="Europe/Istanbul" date -d @1663095120 "+%F %R %Z %z"'2022-09-13 18:52 UTC +0000$ docker run --rm alpine:3.16 sh -c 'TZ="Avrupa/Istanbul" date -d @1663095120 "+%F %R %Z %z"'2022-09-13 18:52 UTC +0000$ docker run --rm alpine:3.16 sh -c 'TZ="<+03>-3" date -d @1663095120 "+%F %R %Z %z"'2022-09-13 21:52 +03 +0300<+03>-3 biçimindeki POSIX dizesi dosya gerektirmediği için tzdata olmadan da çalışır, ama Etc/GMT-3 ile aynı geçmiş sorununu taşır. Python tarafında durum daha iyi, zoneinfo sessiz kalmaz:
$ docker run --rm alpine:3.16 sh -c 'apk add -q python3 && python3 -c "from zoneinfo import ZoneInfo; ZoneInfo(\"Europe/Istanbul\")"'...zoneinfo._common.ZoneInfoNotFoundError: 'No time zone found with key Europe/Istanbul'Çözüm imaja apk add --no-cache tzdata eklemek. UTC kuralıyla çelişmemek için TZ vermeyin, veriyi yalnızca uygulamanın Europe/Istanbul hesaplayabilmesi için kurun. Python'da dağıtım paketine bağlı kalmak istemiyorsanız PyPI'daki tzdata paketini bağımlılık olarak ekleyin, zoneinfo sistemde veri bulamazsa ona bakar. Alpine'da crond çalıştırıyorsanız iki tuzak üst üste biner. Veri yoksa TZ yok sayılır, varsa BusyBox'ın yaz saati davranışı devreye girer. Daemon'ı UTC'de bırakmak ikisinden de kurtarır.
Sonuç
Cron'un neye bakacağını siz seçemezsiniz, ona hangi saati göstereceğinizi seçersiniz. Ben bu konuda tek bir kurala bağlı kalırım: crontab'da yerel saat yazan tek satır bile kalmasın. Zamanlayıcı UTC'de tetiklesin, "İstanbul'da 09:00" kararını kod zoneinfo ile versin, her iş bir iş günü anahtarıyla kilitlensin. Bu üçü yerindeyse bir sonraki saat kararı Resmî Gazete'de çıktığında yapmanız gereken tek iş tzdata'yı güncellemek olur.
Sıkça Sorulan Sorular
- Sunucuyu UTC'ye çektim, mevcut crontab satırlarını nasıl çevireyim?
- Türkiye için fark sabit, her saati üç saat geri alın: 03:00'teki iş
0 0 * * *olur. Asıl dikkat isteyen yer gün sınırı. İstanbul'da pazartesi 01:30'da çalışan iş UTC'de pazar 22:30'dur, haftanın günü alanını da bir gün geri kaydırmanız gerekir. Ayın ilk günü 02:00'de çalışan iş UTC'de önceki ayın son gününe düşer ve standart cron sözdiziminde "ayın son günü" yazmanın bir yolu yoktur. Böyle işleri UTC'de her gün tetikleyip tarih kontrolünü koda bırakın. - Sistemdeki tzdata sürümünü nasıl öğrenirim?
- Debian ve Ubuntu'da
dpkg -s tzdata, RHEL ailesinderpm -q tzdata, Alpine'daapk info -ev tzdatapaket sürümünü verir ve sürüm adında IANA sürümü (örneğin 2022c) görünür. Paket güncel olsa bile JVM ya da kendi kopyasını taşıyan bir veritabanı eski veriyle çalışıyor olabilir. Uygulamanın gözünden en güvenilir test bilinen bir anı çevirmek: Ocak 2022'deki bir an içinEurope/Istanbul+03 vermiyorsa veri 2016g'den eskidir. - Kubernetes 1.25'ten eski bir cluster'da saat dilimini nasıl veririm?
spec.timeZoneyoksa takvim kube-controller-manager'ın saat dilimine göre yorumlanır. Takvimin başınaCRON_TZ=eklemek bazı sürümlerde çalışıyor gibi görünür, ama Kubernetes belgesi bunun resmî olarak desteklenmediğini, kullanılan iç kütüphanenin bir implementasyon detayı olduğunu ve production'da önerilmediğini yazar. Bulut sağlayıcısının yönettiği cluster'larda control plane'in saat dilimine erişiminiz de olmayabilir. En temiz yol takvimi UTC kabul edip yerel saat kararını işin içine taşımak.- PostgreSQL'de `timestamp` mı, `timestamptz` mi kullanmalıyım?
- Olmuş olaylar için
timestamptz. Adı yanıltıcıdır. Saat dilimini saklamaz, değeri UTC'ye çevirip saklar, okurken oturumunTimeZoneayarına göre gösterir.timestampise duvar saatini offset'siz saklar. Bu sütunanow()yazdığınızda değer oturumunTimeZoneayarıyla duvar saatine çevrilir, farklı ayarla bağlanan iki istemci aynı anı farklı yazar. Temel kavramlar için SQL ve RDBMS rehberi iyi bir başlangıç.
Kaynakça
- Bakanlar Kurulu. (2016, 8 Eylül). Gün ışığından daha fazla yararlanmak amacıyla bütün yurtta yaz saati uygulanması hakkında karar (Karar Sayısı: 2016/9154). Resmî Gazete, 29825. https://www.resmigazete.gov.tr/eskiler/2016/09/20160908-2.pdf
- Eggert, P. (2016, 28 Eylül). 2016g release of tz code and data available [E-posta listesi iletisi]. tz-announce. https://mm.icann.org/pipermail/tz-announce/2016-September/000041.html
- Ganssle, P. (2020). PEP 615: Support for the IANA Time Zone Database in the Standard Library. Python Software Foundation. https://peps.python.org/pep-0615/
- IEEE ve The Open Group. (2018). crontab: Schedule periodic background work. The Open Group Base Specifications Issue 7 (IEEE Std 1003.1-2017). https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html
- Klyne, G., ve Newman, C. (2002). Date and Time on the Internet: Timestamps (RFC 3339). Internet Engineering Task Force. https://doi.org/10.17487/RFC3339
- The Kubernetes Authors. (t.y.). CronJob. Kubernetes Documentation. https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/

Mustafa Kürşad Başer
Kıdemli Yazılım Mühendisi
Karmaşık sorunlara zarif çözümler üretmekten keyif alan, tutkulu bir yazılım mühendisi. Kodlamanın ötesinde, teknoloji, sanat ve insan bilincinin kesişim noktalarını keşfetmekle derinden ilgileniyor.

