Epoch etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Epoch etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

2 Ocak 2016 Cumartesi

C'de Time API'leri

C Dili
C dilinde zaman (time) ile çalışmak maharet istiyor.
Aşağıda buradan aldığım C dilinde kullanılabilecek çağrıların birbiri ile olan ilişkisini gösteren güzel bir şekil var. Bu yazıda şekildeki metodları ve eksik kalan kısımlar için POSIX tarafından sağlanan yetenekleri incelemeye çalıştım.


Veri Yapıları
time_t 
std::time_t yazısına taşıdım.

struct time - broken time olarak ta bilinir
struct tm yazısına taşıdım.

time

time metodu hem Posix hem de Windows sistemlerde 1 Ocak 1970 (UTC) gece yarısından (00:00:00) beri geçen süreyi saniye çözünürlüğünde alabilmemizi sağlar. (C dili biraz eski olduğu için çözünürlük saniye seviyesinde kalmış, daha yeni dillerde çözünürlük saniyeden yüksek olabiliyor) Bu API tarafindan döndürülen time_t veri tipi C standardı tarafından sabitlenmemiştir ve bazı platformlarda 32 bit bazılarındaysa 64 bit olabilmektedir. Eğer metodu çağrısında hata varsa -1 döner

The time function returns the implementation’s best approximation to the current calendar time. The value (time_t)(-1) is returned if the calendar time is not available.
time() ile ilgili notlar
Gnu C kütüphanesi Unix için derlenirse time API'si gettimeofday metodunu çağırarak tv.sec alanının değerini döndürür.
Linux içinse bir kernel çağrısı yaparak ve aldığı değeri döndürür.

ctime ve asctime (sabit formatta string'e çevirme)
ctime ve asctime ürettikleri string'in sonuna yeni satır (eol) karakteri eklerler. Bu metodlar yerine strftime kullanılması daha iyi olur.

ctime
verilen time_t saniyesini yerel saat diliminde string'e çevirir. Bu metod yerine, varsa ctime_r kullanılmalı.

asctime
asctime() metodu yazısına taşıdım.

strftime - struct tm yapısını formatlı string'e çevirir
Bu metodu kullanabilmek için şu satır dahil edilir.
#include <time.h>
Kullanılabilecek çevirme seçenekleri burada. Her seçenek portable değil. Portable olmak için kullanılabilecek seçenekler şunlar


If you want your code to be portable, your format ("fmt")
argument should use only the conversion specifiers defined by
the ANSI C standard (C89, to play safe).  These are
"aAbBcdHIjmMpSUwWxXyYZ%".
Seçeneklerde küçük harfler kısaltılmış isimleri, büyükler ise uzun hallerini göstermek için kulllanılmış.
Aşağıda bazılarını açıklamaları var.

Çok kullanılan seçenekler şöyle%b : Kısaltılmış ay ismi (Jun vs. g.b.)
%c : Seçili locale'e göre formatlama yapar. Çok kullanışlıdır.

%d : gün 
%m : ay
%Y : Yıl

%H : 24 saat cinsinden saat
%M : Dakika
%S: Saniye

Örneği buradan aldım.
char buff[20];
struct tm * timeinfo = localtime (&mtime);
strftime(buff, sizeof(buff), "%b %d %H:%M", timeinfo);
Şöyle kullanırız.
struct tm ts = ...;

char buffer[1024];

char format[512] = "%Y%m%d-%H%M%S";
strftime(buffer, sizeof(buffer), format, &ts); 
Çıktı olarak şunu alırız.
20151229-212305

strftime alternatifi
std::put_time kullanılabilir. Örnek:
#include <iostream>
#include <iomanip>
#include <ctime>

int main()
{
    auto t = std::time(nullptr);
    auto tm = *std::localtime(&t);
    std::cout << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") << std::endl;
}

strptime -  formatlı string'i struct tm yapısına çevirir
strptime yazısına taşıdım.

gmtime ve localtime - saniyeden broken-down time'e çevirme

locattime
localtime ve gmtime yazısına taşıdım.

gmtime
localtime ve gmtime yazısına taşıdım.

mktime ve timegm ile ilgili notlar - broken-down time'dan saniyeye çevirme
mktime
std::mktime metodu yazısına taşıdım.

timegm
Bazı platformlarda - örneğin Linux gibi - timegm() tanımlı. Bu metod broken-down formattaki UTC saati  tekrar time_t veriyapısına döndürür. Eğer timegm metodu çalışılan platformda yoksa aşağıdaki gibi bir çözüm üretilebilir.

Bu metod içinde tzset() çağırılınca <time.h> içinde extern long timezone olarak tanımlanan değeri de atıyor. Bu değer bulunduğumuz saat dilimine göre UTC ile aramızdaki saniye farkını tutuyor. mktime metodu içinde saniye hesaplanırken timezone değeri de hesaba katılıyor(Satır 117)

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