25 Aralık 2014 Perşembe

Epoch ve API

EPOCH
Bilgisayar sistemlerinde sistem saati için bir başlangıç zamanı belirlenir. Sistem saati bu başlangıç zamanından itibaren geçen süre olarak tutulur. Bu başlangıç yani referans zamana epoch denilir. Epoch kelime anlamı olarak tarih başlangıcı anlamına gelir.

POSIX uyumlu sistemlerde Epoch 1 Ocak 1970 (UTC) gece yarısından (00:00:00) başlar. Açıklaması şöyle.
Unix time (also known as Epoch time, POSIX time, seconds since the Epoch, or UNIX Epoch time) is a system for describing a point in time. It is the number of seconds that have elapsed since the Unix epoch, that is the time 00:00:00 UTC on 1 January 1970, minus leap seconds.
Windows'ta ise Epoch 1 Ocak 1601 tarihinden başlar. Bu tarih tuhaf gibi görünebilir ancak sebebi şöyle açıklanıyor.
"The Gregorian calendar operates on a 400-year cycle, and 1601 is the first year of the cycle that was active at the time Windows NT was being designed. In other words, it was chosen to make the math come out nicely."
Tüm işletim sistemleri Epoch'tan itibaren geçen süreyi alabilmemizi sağlayan bazı API'ler sunarlar. Her API belli bir süre birimi kullanır. Kimisi sadece saniye, kimisi saniye + milisaniye, kimisi saniye + mikrosaniye, kimisi de saniye + nanosaniye birimlerini kullanır. Her ne kullanılırsa kullanılsın tüm API'lerin amacı sistem saatini almaktır.

İşletim sistemi epochtan beri geçen süreyi belli bir çözünürlükte artırır. Bu artırıma resolution denir. Resolution süresi geçmeden arka arkaya aynı API'yi çağırırsak aynı değer alabiliriz.

Esasen sistem saati kronometre olarak kullanılmamalıdır. Sebebi ise bir çok işletim sisteminin NTP ile sistem saatini sürekli ileri geri küçük küçük oynatmasıdır. Dolayısıyla aşağıdaki sürüyle API'nin hiçbirisi sistem saatini göstermek dışında bence fazla bir işe yaramıyor :)

POSIX

ftime - obsolete
Posix Epoch'tan beri geçen süreyi saniye + milisaniye çözünürlüğünde alabilmemizi sağlar. Ancak bu metod obsolete edilmiştir ve saniye seviyesinde çözünürlük için time, mikrosaniye seviyesinde çözünürlük için gettimeofday veya nanosaniye seviyesinde çözünürlük için clock_gettime metodlarından birisinin kullanıması tavsiye edilmektedir.

gettimeofday - obsolete
gettimeofday yazısına taşıdım.

clock_gettime 
clock_gettime yazısına taşıdım

clock_gettime ve clock_getres ilişkisi
Yazının başında da belirttiğim gibi Epoch'tan beri geçen süre belli bir çözünürlükte artırılır. Bu çözünürlüğü öğrenmek için clock_getres kullanılır. Örnek:

void pr(int clock,const char *n)
{
  struct timespec tv = {1,2};
  struct timespec res = {3,4};
  int rc;


  rc=clock_getres(clock,&res);
  if (rc) {
    printf("getres return code on %s=%d errno=%d\n",n,rc,errno);
  }
  rc=clock_gettime(clock,&tv);
  if (rc) {
   printf("gettime return code on %s=%d errno=%d\n",n,rc, errno);
  }
  else {
    printf("%25s=% 11d.%09d resolution=% 2d.%09d\n",n,tv.tv_sec,tv.tv_nsec,res.tv_sec,res.tv_nsec);
  }
}


Bu metod aşağıdaki gibi çağırılırsa
pr(CLOCK_REALTIME,"CLOCK_REALTIME");

şu sonucu alırız.


CLOCK_REALTIME= 1097084999.490116229 resolution= 0.000000040
Yani Epoch'tan beri geçen süre saniye + nanosaniye , resolution ise 40 nanosaniyedir.


WINDOWS
 
GetSystemTime
GetSystemTime yazısına taşıdım.

GetSystemTimeAsFileTime
GetSystemTimeAsFileTime yazısına taşıdım.

NtQuerySystemTime
NtQuerySystemTime yazısına taşıdım.

_ftime
_ftime ile 1 Ocak 1970 (UTC) gece yarısından (00:00:00) itibaren geçen süreyi saniye + milisaniye çözünürlüğünde almamızı sağlar. Posix ile gelen ftime metodunun eşleniğidir.

QueryPerformanceCounter
Bu metodu sonra yazacağım.
 
C Dili
C dilinde time() metodu kullanılır. Epoch'tan beri geçen süreyi saniye çözünürlüğünde almamızı sağlar. Konu uzun olduğu için C'de Time API'leri başlıklı yazıya taşıdım.

C++ Dili
Boost
get_system_time
Bu metod boost derlenirken microsec_clock tanımlı ise onu kullanır, değilse second_clock'ı kullanır. thread_time.hpp dosyasında kod görülebilir.

Thread veya condition bekleme kodlarında da aşağıdakine benzer satırlar görülebilir.
boost::system_time  timeout = boost::get_system_time( ) + boost::posix_time::milliseconds(1000);

second_clock
boost ile gelen date_time kütüphanesinde second_clock sınıfı ile saniye çözünürlüğünde zaman ölçümü yapabilmek mümkün.

microsec_clock

boost ile gelen date_time kütüphanesindeki microsec_clock sınıfı ile mikrosaniye çözünürlüğünde zaman ölçümü yapabilmek mümkün. Hatta bu kütüphane ile linux/windows arasında portable kod yazabilmek te mümkün. Bu kütüphane Linux üzerinde çalışırken yukarıda bahsedilen gettimeofday() metodunu çağırıyor.
Ancak Windows üzerinde çalışırken ftime() metodunu çağırıyor. ftime() Windows için yeterince iyi aralıkta güncellenmediği için microsec_clock'ın Windows üzerinde mikrosaniye çözünürlüğünde zaman ölçümü için kullanılması bence doğru değil. Buradaki soruda da konuyla ilgili açıklama var.

using namespace boost::posix_time;
ptime now = microsec_clock::local_time();
Bir diğer örnekte ise UTC saat alınmış.
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::posix_time;
ptime now = microsec_clock::universal_time();

Eğer istenirse boost::posix_time daha az hassasiyetle de kullanılabilir. Örneğin from_time metodu ile yeni bir nesne yaratmak mümkün.
Ancak bu nesne sadece saniye hassasiyetinde olacaktır.

Boost chrono high resolution clock
Bu kütüphane ile nanosaniye çözünürlüğünde zaman ölçümü yapabilmek mümkün.

#include <boost/chrono.hpp>

int main() {
    boost::chrono::high_resolution_clock::time_point t1 = boost::chrono::high_resolution_clock::now();

    boost::chrono::high_resolution_clock::time_point t2 = boost::chrono::high_resolution_clock::now();

    std::cout << boost::chrono::duration_cast<boost::chrono::nanoseconds>(t2-t1) << "\n";
    // boost chrono has more advanced output, .count() isn't needed, nor is manually writing out the units
}


C++11 high resolution clock
chrono sınıfları yazısına taşıdım.

QT
QDateTime::currentDateTime() ile milisaniye çözünürlüğünde zaman ölçümü yapabilmek mümkün. Aşağıdaki şekil QDateTime nesnesinin nasıl çalıştığını gösteriyor.



Java Dili
System.currentTimeMillis :
Bu API 1 Ocak 1970 (UTC) gece yarısından (00:00:00) beri geçen süreyi milisaniye çözünürlüğünde alabilmemizi sağlar. Bu metodun hassasiyeti her zaman çok iyi olmayabilir. Örneğin System.currentTimeMillis() metodunu bir döngü içinde çağırırsak gelen sonuçlarda 10-20-30 milisaniyelik atlamalar görebiliriz. Bu durumu teyit eden bir yazıyı burada görebilirsiniz.
Benim yaptığım küçük bir deneyde de Epoch Converter sitesindeki saniye ile System.currentTimeMillis(); hemen hemen (1374955419 saniye ve 1374955418858 milisaniye) aynı değerleri verdiler.

Java'da Epoch zamanını yerel saat olarak nasıl gösteriyor?
Akış aşağıdaki gibi
DateFormat df = DateFormat.getDateTimeInstance (..) şeklinde formatlama sınıfı çağırılınca
DateFormat arayüzünü gerçekleştiren SimpleDateFormat sınıfı döndürülür. Bu sınıf içinde bir Calendar nesnesi barındırır. Calendar arayüzünü gerçekleştiren GregorianCalendar sınıfı'nın setTime() metodu çağırılınca, bu sınıf kendi içindeki computeFields() metodunu çağırır ve saat dilimin hesaba katarak yerel saati bulur.
Yani her formatlama işleminde UTC saatini yerel saate çeviren hesaplama yapılır.

Javada'ki Date ve etrafındaki sınıfların hiyerarşisini gösteren bir şekli buradan aldım ve aşağıya ekledim.



Aşağıda ise bir başka şekil bulunmakta.


Joda
ReadableInstant sınıfı saat diliminden bağımsız olarak epoch'tan beri geçen süreyi milisaniye olarak alabilmemizi sağlar. Aşağıda bu arayüzün hiyerarşisi var.

Bu sınıf ta altta System.currentTimeMillis(); metodunu kullanır. Örnek:
new DateTime ().getMillis();

C# dili

C# ile 1 Ocak 1970 (UTC) gece yarısından (00:00:00) beri geçen süreyi milisaniye çözünürlüğünde alabilmemizi sağlayan bir metod bulunmamaktadır ancak kolayca da yazılabilir.

TimeSpan timeSpan = DateTime.UtcNow-new DateTime(1970, 1, 1,0,0,0,DateTimeKind.Utc);
long milliSeconds = timeSpan.TotalMilliseconds;

EPOCH'tan Geriye Dönüş

Epoch'tan beri geçen süreyi bazen tekrar bir nesneye çevirmek gerekir. Bunun için aşağıda bazı notlar aldım.

C++ Dili

Boost

ptime someUTC_Time(date(2008, Jan, 1), time_duration(0, 0, 0, 0));

QT

QDateTime::fromTime_t(uint seconds) : Bu metod ile Epoch'tan beri geçen saniyeler tekrar nesneye çevirilebilir.

Java Dili
public Date(long date) : Bu metod ile ile Epoch'tan beri geçen milisaniyeler tekrar nesneye çevirilebilir.
Calendar.setTimeInMillis (long)  : Bu metod ile Epoch'tan beri geçen milisaniyeler tekrar nesneye çevirilebilir.

UTC ve Leap Second (Artık Saniye) 
UTC ve Leap Second yazısına taşıdım

Hiç yorum yok:

Yorum Gönder