2 puan yazan GN⁺ 2024-08-16 | 1 yorum | WhatsApp'ta paylaş
  • SQLite'ın yerleşik tarih işlevleri yetersiz kaldığında, sqlean-time bir eklenti olarak nanosaniye hassasiyetinde Time·Duration tipleri ve tarih/saat işlevleri ekler
  • Time değeri, 0001-01-01 00:00:00 UTC sonrasındaki saniyeler ile içinde bulunulan saniyedeki nanosaniyelerden oluşur; 13 baytlık BLOB olarak saklandığında geçmişte ve gelecekte milyarlarca yıllık aralığı kapsayabilir
  • Unix epoch tabanlı 64 bit NUMBER olarak saklamak da mümkündür, ancak birim küçüldükçe aralık daralır; nanosaniye biriminde yalnızca 1678 ile 2262 yılları arası temsil edilebilir
  • API; oluşturma, alan çıkarma, Unix time dönüşümü, karşılaştırma, aritmetik, kırpma·yuvarlama, ISO 8601 biçimlendirme·ayrıştırmayı içerir ve değerler her zaman UTC olarak saklanıp işlenir
  • Takvim hesaplamaları Gregoryen takvim varsayımıyla yapılır ve artık saniyeler ele alınmaz; bu yüzden gün·ay·yıl hesaplarında Duration tabanlı time_add() yerine time_add_date() kullanılmalıdır

sqlean-time zaman modeli

  • sqlean-time, SQLite'a yüksek hassasiyetli tarih·saat işleme ekleyen bir eklentidir
  • SQLite eklentileri, dosya indirilip veritabanında tek bir komut çalıştırılarak eklenebilir
  • Eklenti iki tür değer etrafında çalışır
    • Time: belirli bir an
    • Duration: süre

Time gösterimi ve saklama aralığı

  • Time, (seconds, nanoseconds) ikilisinden oluşur
    • seconds: zero time olan 0001-01-01 00:00:00 UTC sonrasındaki saniyeleri gösteren 64 bit tamsayı
    • nanoseconds: mevcut saniye içindeki nanosaniye değeri; aralığı 0-999999999
  • En yüksek esneklik gerekiyorsa, Time değeri dahili gösterimi olan 13 baytlık BLOB olarak saklanabilir
    • Bu yöntem, geçmişte ve gelecekte milyarlarca yılı nanosaniye hassasiyetinde ifade eder
  • Unix epoch olan 1970-01-01 00:00:00 UTC sonrasındaki saniye, milisaniye, mikrosaniye, nanosaniyeyi 64 bit tamsayı NUMBER olarak saklama da desteklenir
    • saniye: geçmişte ve gelecekte milyarlarca yılı saniye hassasiyetinde ifade eder
    • milisaniye: 1970'in öncesi ve sonrasında 292 milyon yılı milisaniye hassasiyetinde ifade eder
    • mikrosaniye: -290307 yılından 294246 yılına kadar ifade eder
    • nanosaniye: 1678 yılından 2262 yılına kadar ifade eder
  • Time her zaman UTC olarak saklanır ve işlenir
    • Belirli saat dilimi ofsetlerine dönüşüm mümkündür
  • Takvim hesaplamaları her zaman Gregoryen takvimi varsayar
    • Artık saniyeler kullanılmaz

Duration ve değer oluşturma

  • Duration, nanosaniye cinsinden 64 bit tamsayıdır
    • Yaklaşık 290 yıla kadar olan süreleri ifade edebilir
    • NUMBER olarak saklanabilir
  • Geçerli zaman time_now() ile oluşturulabilir
    • Örn: time_fmt_iso(time_now()), 2024-08-06T21:22:15.431295000Z gibi bir ISO dizgesi döndürür
  • Belirli tarih·saat time_date() ile oluşturulur
    • Yalnızca tarih verilirse UTC gece yarısı olur
    • Saat·dakika·saniye ve nanosaniye birlikte belirtilebilir
    • Saat dilimi ofseti verilirse UTC anına dönüştürülür

Tarih·saat alanlarını çıkarma

  • Tek tek alan çıkarma işlevleri yıl, ay, gün, saat, dakika, saniye, nanosaniye, haftanın günü, yılın günü, ISO yılı, ISO haftasını döndürür
    • Örn: time_get_year(), time_get_month(), time_get_day(), time_get_hour(), time_get_minute(), time_get_second(), time_get_nano()
  • Genel amaçlı time_get() işlevi, alan adı dizgesiyle değer çıkarır
    • Desteklenen örnekler: millennium, century, decade, year, quarter, month, day
    • Zaman birimi örnekleri: hour, minute, second, milli, micro, nano
    • ISO·takvim ile ilgili örnekler: isoyear, isoweek, isodow, yearday, weekday
    • Unix epoch değeri epoch ile alınabilir

Unix time dönüşümü

  • Unix time'dan Time değeri oluşturan işlevler sunulur
    • time_unix(seconds)
    • time_unix(seconds, nanoseconds)
    • time_milli(milliseconds)
    • time_micro(microseconds)
    • time_nano(nanoseconds)
  • Time değerini yeniden Unix time'a çeviren işlevler de vardır
    • time_to_unix()
    • time_to_milli()
    • time_to_micro()
    • time_to_nano()
  • Unix türevi işletim sistemleri zamanı çoğu zaman 32 bit saniye değeriyle kaydeder, ancak time_to_unix() 64 bit değer döndürür
    • Geçmişte ve gelecekte milyarlarca yıllık aralıkta geçerlidir
    • time_to_milli() 1970'in öncesi ve sonrasında 292 milyon yıla kadar ifade eder
    • time_to_micro() -290307 yılından 294246 yılına kadar ifade eder
    • time_to_nano() 1678 yılından 2262 yılına kadar ifade eder

Karşılaştırma ve aritmetik

  • Zaman karşılaştırma işlevleri iki Time değerinin sırasını belirler
    • time_after(): ilk zamanın ikinciden sonra olup olmadığını döndürür
    • time_before(): ilk zamanın ikinciden önce olup olmadığını döndürür
    • time_compare(): sonraysa 1, önceyse -1, eşitse 0 döndürür
    • time_equal(): iki değerin aynı anı gösterip göstermediğini döndürür
  • time_add() bir Time değerine Duration ekler
    • Negatif Duration kullanılarak çıkarma yapılabilir
    • dur_us(), dur_ms(), dur_s(), dur_m(), dur_h() gibi Duration sabitleri birlikte kullanılabilir
  • Gün·ay·yıl eklerken time_add() değil, time_add_date() kullanılmalıdır
    • time_add_date() yıl, ay, gün ekler; negatif değerlerle çıkarma da yapılabilir
  • time_sub() iki Time değeri arasındaki süreyi nanosaniye olarak döndürür
  • time_since() belirtilen andan bu yana geçen süreyi nanosaniye olarak döndürür
  • time_until() belirtilen ana kadar kalan süreyi nanosaniye olarak döndürür

Kırpma ve yuvarlama

  • time_trunc() bir Time değerini belirtilen alan hassasiyetine kadar aşağı yuvarlar
    • Desteklenen örnekler: millennium, century, decade, year, quarter, month, week, day, hour, minute, second, milli, micro
    • Örneğin 2011-11-18T15:56:35.666777888Z değeri hour düzeyine kırpılırsa 2011-11-18T15:00:00Z olur
  • Belirli bir Duration katına göre de aşağı yuvarlanabilir
    • Örn: 12*dur_h(), dur_h(), 30*dur_m(), dur_m(), 30*dur_s(), dur_s()
  • time_round() belirtilen Duration'ın en yakın katına yuvarlar
    • Örn: 2011-11-18T15:56:35.666777888Z değeri dur_h() ile yuvarlanırsa 2011-11-18T16:00:00Z olur
    • Aynı değer dur_s() ile yuvarlanırsa 2011-11-18T15:56:36Z olur

Biçimlendirme ve ayrıştırma

  • time_fmt_iso() bir Time değerini ISO 8601 dizgesi olarak döndürür
    • Saat dilimi ofseti isteğe bağlı olarak alınabilir ve bu ofsete dönüştürüldükten sonra biçimlendirilebilir
    • Nanosaniyeli değerler 2011-11-18T15:56:35.666777888Z gibi gösterilir
    • Ofset belirtilirse 2011-11-18T18:56:35.666777888+03:00 gibi gösterilir
  • time_fmt_datetime(), time_fmt_date(), time_fmt_time() sırasıyla datetime, date, time dizgeleri döndürür
    • Saat dilimi ofseti isteğe bağlı olarak verilebilir
  • time_parse() biçimlendirilmiş dizgeyi Time değerine ayrıştırır
    • ISO 8601 nanosaniye ve saat dilimi içeren dizgeler
    • ISO 8601 nanosaniye ve UTC Z dizgeleri
    • ISO 8601 saat dilimi içeren dizgeler
    • ISO 8601 UTC dizgeleri
    • YYYY-MM-DD HH:MM:SS biçimindeki UTC tarih·saat
    • YYYY-MM-DD biçimindeki UTC tarih
    • HH:MM:SS biçimindeki UTC saat
  • time_parse() tarafından desteklenen layout'lar sınırlı bir kümedir

Duration sabitleri

  • Yaygın Duration'ları nanosaniye olarak döndüren işlevler sunulur
    • dur_ns()1
    • dur_us()1000
    • dur_ms()1000000
    • dur_s()1000000000
    • dur_m()60000000000
    • dur_h()3600000000000

Uygulama temeli ve kurulum

  • Eklenti C ile yazılmış olsa da tasarım ve uygulama büyük ölçüde Go standart kütüphanesindeki time paketine dayanır
    • İlgili paket BSD 3-Clause License lisansına sahiptir
  • Kurulum, latest release dosyasını indirip SQLite CLI içinde eklentiyi yükleme şeklindedir
    • Örn: .load ./time
    • Yüklemeden sonra select time_now(); gibi sorgular kullanılabilir

1 yorum

 
GN⁺ 2024-08-16
Hacker News yorumları
  • Jon Skeet’in meşhur şekilde derlediği saat dilimi değişiklikleri ve yerel saatteki süreksizlikler gibi özel durumları da ele alıp almadığını merak ediyorum
    https://stackoverflow.com/questions/6841333/why-is-subtracti...
    Computerphile da 10 dakikalık bir videoda bunu çok iyi açıklıyor
    https://www.youtube.com/watch?v=-5wpm-gesOY
    Uzun zaman önce tarih/saat ya da kriptografi kütüphanelerini kendim yazmamayı öğrendim. Ölümcül biçimde can yakabilecek uç durumlar bitmek bilmiyor; bu yüzden böyle yeni bir kütüphane görünce şüpheyle yaklaşıyorum

    • Bu kütüphane yerel saat kavramıyla hiç ilgilenmiyor. Tamamı UTC tabanlı zamanlar; kullanıcı saat dilimi ofseti sağlayabiliyor, ama saat dilimi ofsetini hesaplamanın zor kısmını çağıranın yapması gerekiyor
      Belgelerin biraz daha net olabileceğini düşünüyorum. Yazar “time zones” diyor ama kütüphane aslında yalnızca saat dilimi ofsetlerini ele alıyor. Saat dilimi America/New_York gibi bir şeydir; saat dilimi ofseti ise UTC ile arasındaki farktır. New York bugün -14400 saniye, ama yaz saati uygulaması değişikliği nedeniyle birkaç ay sonra -18000 saniye olacak
  • Üç farklı zaman gösterimi/boyutu ilginç. Örneğin milyarlarca yıllık aralıkta nanosaniye hassasiyet gerektiren kullanım senaryosunun ne olduğunu bilmiyorum
    Daha da kafa karıştırıcı olan, zaman çözünürlüğü aşırı inceyken duration tarafında nanosaniye hassasiyetinin aralığının yalnızca ±290 yıl olması

    • Bir kez nanosaniye hassasiyeti kullanmaya karar verdiyseniz, 64 bitlik gösterim yalnızca 584 yıl sığdırır ve bu yeterli değil. 2024 yılını ifade edebilmek için en az 2 bite daha ihtiyaç var
      Ama 2 bit ekleyecekseniz 16 ya da 32 bit eklememek için de bir neden yok. Böylece 30 cm’yi ışığın katetme süresini hesaplayandan evrenin yaşını hesaplayana kadar herkesi kapsayabilirsiniz
      Tasarım kararının muhtemelen böyle geliştiğini hayal ediyorum :)
      Elbette artık saniye desteği olmadan saniye altı doğruluk sunmak zor; insan uygarlığı öncesi artık saniye desteğinin ne anlama geldiği de belirsiz
    • Bu yöntem bana ve binlerce başka Go geliştiricisine çok iyi uydu. Bu yüzden bu yaklaşımı seçtim
  • İlgili bir yan konu ama veritabanları birimleri izlemeli. Bir zaman sütunu varsa, örneğin float64 saniye cinsinden duration diye tanımlanabilmeli
    O zaman SELECT * FROM my_table WHERE duration_s >= 2h gibi yazılabilmeli ve veritabanı “2h” değerini otomatik olarak 7200.0 saniyeye çevirip tablo taraması sırasında aynı birimlerle karşılaştırmalı
    Birkaç yıl önce böyle yerel birim işleme özelliği olan özel amaçlı bir SQL veritabanı yapmıştım; ama öncesinde de sonrasında da görmedim ve bu, UI ekosisteminde bir boşluk gibi görünüyor
    Bunun yalnızca zamanla sınırlı olması gerekmez. Kütle, hacim, bilgi miktarı, sıcaklık vb. tüm birim listesini ele alabilmeli. SELECT 2h + 15kg -- type error! gibi matematiksel olarak anlamsız ifadeleri de veritabanı reddedebilmeli
    Bu, analiz hatalarını erken yakalamada çok yardımcı olur

  • İşaretli tamsayı kullanılıp kullanılmadığının belirtilmesinin önemli olduğunu düşünüyorum. Belgeleri okuyunca işaretli gibi duruyor ama olmayabilir de
    İşaretli tamsayıysa aynı tarih ve saati temsil eden birden fazla bit dizisi ortaya çıkabilir; bu iyi değil

    • Kesinlikle işaretli. “Çıkarmak için negatif duration kullanın” deniyor
      Ancak bit deseni kütüphanenin iç meselesi. Kodda bir hata bulabiliyorsanız elbette işaret edin; mümkünse düzeltme de önerin
    • “Aynı tarih ve saati temsil eden birden fazla bit dizisi” nasıl mümkün olabilir?
  • SQLite3’te genişletilebilir bir tip sistemi olsaydı gerçekten iyi olurdu

    • PostgreSQL’e biraz katkı yapmış biri olarak söylüyorum: Hayır, bu yapılmamalı!!!!
      Genişletilebilir tip sistemi, veritabanı son kullanıcı performansı için berbat. Böyle olunca sorgu ayrıştırma ve optimizasyonunda hiçbir şeyi kestirmeden işleyemezsiniz. Her işlenenin tipini kontrol etmek, doğru operatör implementasyonunu bulmak, doğru indeks operatör ailesini/sınıfını bulmak vb. için sorgu sistem kataloğuna sürekli bakmak gerekir
      Değerlerin giriş/çıkışı da sistem kataloğunda saklanan fonksiyonlardan geçer. select 1 bile sistem kataloğuna bakmadan yanıtlanamaz
      Uygun bir yerleşik tip kümesi ve struct/JSON gibi bileşim yöntemleri olmalı. PostgreSQL dışındaki çoğu veritabanı böyle ve doğru yönün bu olduğuna güçlü biçimde inanıyorum
  • Biraz tembel bir Ask HN tarzı soru ama deneyime göre hangisi daha kullanışlı ya da değerli? Nanosaniye gösterimi mi, yoksa 1678~2200 gibi nanosaniye aralığının dışındaki yılları gösterebilmek mi?
    Ciddi bilimsel işler yapmadığım için nanosaniyenin değerinin çok akıllıca deneylerle ya da daha dar aralıklı finansal işlem takibiyle sınırlı olduğunu düşünüyorum
    Buna karşılık tarihsel tarihleri gösterebilme yeteneğine daha sık ihtiyaç duyulacak gibi. Ne düşünüyorsunuz?

    • Tarihsel tarihler kesinlikle daha önemli
      Hassasiyeti yalnızca 10 nanosaniyeye düşürseniz bile pratikte yeterli bir aralık elde edebilirsiniz
    • Çekiç mi tornavida mı daha kullanışlı diye sormaya benziyor. İşe göre değişir
  • Neden Go tarzı gibi Unix zaman damgasını nanosaniye cinsinden signed int64 olarak kullanmadığını merak ediyorum. Nanosaniye hassasiyetiyle milyonlarca yılı kapsayamazsınız ama buna gerçekten ihtiyaç var mı?

    • Bu hassasiyet ve boyutla yalnızca 1678’den 2262’ye kadar kapsayabilirsiniz; bu da tarihsel tarih ve saatleri temsil etme yeteneğini ciddi biçimde sınırlar
    • Unix zaman damgasını nanosaniye olarak saklamak Go tarzı değil, ama bu eklentiyle bunu yapabilirsiniz
      select time_to_nano(time_now());
      -- 1722979335431295000
  • “epoch’tan sonraki saniyeler” gibi ifadelerin yalnızca gerçekten o anlama geldiğinde kullanılmasını isterdim
    select time_sub(time_date(2011, 11, 19), time_date(1311, 11, 18)); ne döndürür merak ediyorum

    • Neden böyle istiyorsunuz?
      Aklıma birkaç makul neden geliyor ama gerçekten önemli olan tek şey “hangi epoch?” sorusu. UNIX tabanlı sistemlerde ya da onun davranışını taklit etmeye çalışan sistemlerde bu iyi tanımlıdır. Ama şikâyetinizin ne olduğunu söylemediğiniz için bunun neden şu anki gibi olduğunu çürütmek ya da gerekçelendirmek zor
      time_date(1311, 11, 18) çoğu bilgisayar sisteminin kullandığı epoch’ta tanımlı değildir; bu yüzden herhangi bir sonuç mümkün. MAX_INT, MIN_INT, 0, makul görünen ama takvim reformlarını yansıtmayan bir değer, başka bir epoch’a dönüştürüp doğru saniye sayısını hesaplayan bir değer vb. her şey olabilir. GMT/UTC öncesinde her şey yerel saatti; bu yüzden geçerli bir epoch olmadığını da savunabilirsiniz
      Elbette negatif değerlerin desteklenip desteklenmemesi iki yönden de savunulabilir. 1970-1-1 0:00:00 UTC’den tam 24 saat öncesinin -86400 olmasını bekleyebilirsiniz, ama “since” yalnızca pozitifliği güçlü biçimde ima eder
      Başkalarının bambaşka nedenlerle tamamen farklı epoch’ları olabilir ve kullanıldığı alan içinde herkes hemfikirse bu da sorun değildir
      Yoksa başka bir itiraz nedeniniz mi vardı?
    • “Sonuç Duration içinde saklanabilecek en büyük değeri aşarsa, maksimum duration döndürülür” deniyor