Data i czas w C# i .NET – DateTime, DateTimeOffset i TimeSpan

Pisząc w środowisko C# i .NET mamy do dyspozycji 3 bardzo wygodne klasy pozwalające na operowanie na datach i czasie. Klasy te oferują wiele bardzo przydatnych metod – zarówno statycznych, jak i nie-statycznych. W niniejszym tekście skupię się natomiast tylko na ogólnym zastosowaniu powyższych klas, by dać czytelnikowi wiedzę o tym, do czego służą poszczególne klasy.

Klasa DateTime służy do przechowywania informacji o konkretnym punkcie w czasie. Może to być punkt w czasie licząc czasem lokalnym, licząc czasem UTC lub nie precyzując bliżej jaki to czas. Obrazuje to poniższy fragment kodu.

//2012-06-14 21:30:00 strefa nieokreślona
DateTime date = new DateTime(2012, 6, 14, 21, 30, 0);
//2012-06-14 21:30:00 strefa lokalna
DateTime dateLocal = new DateTime(2012, 6, 14, 21, 30, 0, DateTimeKind.Local);
//2012-06-14 21:30:00 strefa UTC
DateTime dateUtc = new DateTime(2012, 6, 14, 21, 30, 0, DateTimeKind.Utc);

W przypadku powyższej klasy mamy proste mechanizmy do konwertowania pomiędzy dwoma powyższymi strefami czasowymi.

DateTime dateLocalToUtc = dateLocal.ToUniversalTime();
DateTime dateUtcToLocal = dateUtc.ToLocalTime();

Wykonując operacje matematyczne na powyższym typie danych nie zostaje uwzględniona strefa czasowa, czyli wykonując operacje matematyczne na nich uwzględniana jest tylko sama data w nie wpisana. Przykładem niech będzie poniższy kod, który dodatkowo wprowadza nową klasę obiektów – TimeSpan.

TimeSpan ts = dateLocal - dateUtc;
Console.WriteLine(ts);
//Zwraca 00:00:00

Zauważyć tu trzeba 2 kwestie. Po pierwsze, poznajemy klasę TimeSpan, która nie reprezentuje już punktu w czasie, ale reprezentuje pewien odcinek czasu. Za chwilę omówimy jeszcze trochę tą klasę, ale najpierw skupmy się nad powyższym kodem.

Odejmujemy dwie daty. Daty te mają tą samą wartość, ale występuj w różnych strefach czasowych, więc oznaczają dwa różne punkty czasowe. Jednakże wykonując operację odejmowania na nich dostajemy 0, gdyż brana jest pod uwagę tylko wartość (bez strefy czasowej).

Jako, że TimeSpan oznacza tylko i wyłącznie odcinek czasu, dysponuje innymi konstruktorami niż DateTime. W konstruktorach nie możemy podać oni lat, ani miesiecy – conajwyżej dni i czas. Ilustruje to poniższy fragment kodu.

TimeSpan ts1 = new TimeSpan(00, 01, 30);
//00:01:30 - półtorej minuty
TimeSpan ts2 = new TimeSpan(1, 12, 00, 00);
//1.12:00:00 - półtorej dnia
TimeSpan ts3 = new TimeSpan(2, -30, 15);
//01:30:15 - 2h-30m+15s

Teraz wracamy do naszych dat. Istnieje klasa DateTimeOffset. Tworząc takowy obiekt, podajemy różnicę w czasie względem czasu UTC (tzw. offset).

//polski czas +01:00
TimeSpan off1 = new TimeSpan(1, 0, 0);
DateTimeOffset dtOffset1 = new DateTimeOffset(2012, 6, 14, 10, 30, 0, off1 );
//czas UTC +00:00
TimeSpan off2 = new TimeSpan(0, 0, 0);
DateTimeOffset dtOffset2 = new DateTimeOffset(2012, 6, 14, 10, 30, 0, off2);
//aktualny polski czas +01:00
DateTimeOffset dtOffsetLocalNow = DateTimeOffset.Now;
//aktualny czas UTC +00:00
DateTimeOffset dtOffsetUtcNow = DateTimeOffset.UtcNow;

Najważniejsza różnica między DateTime i DateTimeOffset, to że przy wykonywaniu operacji arytmetycznych na nich uwzględniana jest strefa czasowa.

TimeSpan tsOffset = dtOffset1 - dtOffset2;
Console.WriteLine(tsOffset);
//Zwraca -01:00:00