Czas UTC a DateTime.Kind

time zones

Ostatnio pierwszy raz w życiu miałem okazję ustawiać nagłówki HTTP odnośnie cachowania. Pogłębiłem trochę temat i okazało się, że trzeba myśleć nie w kategoriach czasu lokalnego, tylko czasu uniwersalnego. Na nasze: zamiast DateTime.Now trzeba używać DateTime.UtcNow.

Wstęp: Przechowujesz datę i czas? Może warto w UTC?

DateTime ma właściwość Kind typu DateTimeKind:

public enum DateTimeKind
{
  Unspecified,
  Utc,
  Local,
}

GMT to dla naszych potrzeb to samo co UTC. Nasz czas lokalny to GMT + 1. Kod poniżej ilustruje różne zachowania związane z DateTime.Kind.

var now = DateTime.Now;
Console.WriteLine(now.Hour); // 18
Console.WriteLine(now.Kind); // Local

var utcNow = DateTime.UtcNow;
Console.WriteLine(utcNow.Hour); // 17
Console.WriteLine(utcNow.Kind); // Utc

var changedToUniversal = now.ToUniversalTime(); // checks Kind internally
Console.WriteLine(changedToUniversal.Hour); // 17
Console.WriteLine(changedToUniversal.Kind); // Utc

var myDate = new DateTime(2014, 3, 8, 18, 0, 0);
Console.WriteLine(myDate.Kind); // Unspecified

var myDateIsLocal = new DateTime(2014, 3, 8, 18, 0, 0, DateTimeKind.Local);
Console.WriteLine(myDateIsLocal.Kind); // Local

Gdy pewna metoda oczekuje czas uniwersalnego, może dzięki Kind wyciągnąć sobie ten czas nawet z daty w czasie lokalnym. Dzieję się tak na przykład podczas ustawiania nagłówków HTTP dla cachowania:

cache.SetExpires(DateTime.UtcNow); // calls ToUniversalTime() internally
cache.SetExpires(DateTime.Now); // calls ToUniversalTime() internally
// Now and UtcNow produce the same output

Zmiana czasu

Teraz mamy zimę (można się było domyślić patrząc na różnicę między godziną 18 a 17). Jeśli jednak mamy czas letni (można to sprawić przestawiajac czas w Windowsie) to output będzie:

var now = DateTime.Now;
Console.WriteLine(now.Hour); // 18
Console.WriteLine(now.Kind); // Local

var utcNow = DateTime.UtcNow;
Console.WriteLine(utcNow.Hour); // 16
Console.WriteLine(utcNow.Kind); // Utc

Wtedy różnica między UTC a lokalnym będzie wynosić 2 godziny. Podczas operacji zmiany czas zimowy->letni (czy w drugą strone) UtcNow będzie się liniowo zmieniać, natomiast Now przeskoczy sobie w nocy o godzinkę. W niektórych zastosowaniach operowanie czas lokalnym sprawi wiele problemów.

BTW przechodzenie na czas letni/zimowy w dziesiejszych czasach straciło sens i powoduje wymierne straty.

3 Comments on “Czas UTC a DateTime.Kind

  1. Pingback: DateTimeOffset zamiast DateTime | Show me the code

  2. Pingback: dotnetomaniak.pl