DateTime vs DateTimeOffset

Obecnie mamy standardowy sposób radzenia sobie z. Net DateTimes w sposób świadomy strefy czasowej: za każdym razem, gdy tworzymy DateTime robimy to w UTC( np. używając DateTime.UtcNow), a za każdym razem, gdy go wyświetlamy, konwertujemy z UTC na czas lokalny użytkownika.

To działa dobrze, ale czytałem o DateTimeOffset i jak rejestruje czas lokalny i UTC w samym obiekcie. Więc pytanie brzmi, Jakie byłyby zalety używania DateTimeOffset vs tego, co już robiliśmy?

Author: ice1000, 2010-12-02

8 answers

DateTimeOffset jest reprezentacją czasu chwilowego (znanego również jako czasu absolutnego ). Przez to, mam na myśli moment w czasie, który jest uniwersalny dla wszystkich (nie licząc sekund przestępnych, lub relatywistycznych efektów dylatacji czasu). Innym sposobem reprezentacji czasu chwilowego jest DateTime Gdzie .Kind jest DateTimeKind.Utc.

Różni się to od czasu kalendarza (znanego również jako czasu cywilnego ), który jest pozycją na czyjejś kalendarz, i istnieje wiele różnych kalendarzy na całym świecie. Nazywamy te kalendarze strefami czasowymi . Czas kalendarzowy jest reprezentowany przez DateTime Gdzie .Kind to DateTimeKind.Unspecified, lub DateTimeKind.Local. I .Local ma znaczenie tylko w scenariuszach, w których masz dorozumiane zrozumienie, gdzie znajduje się komputer, który korzysta z wyniku. (Na przykład stacja robocza użytkownika)

Więc dlaczego DateTimeOffset zamiast UTC DateTime? chodzi o perspektywę. użyjmy analogii - będziemy udawać fotografów.

Wyobraź sobie, że stoisz na osi czasu kalendarza, wskazując kamerę na osobę na chwilowej osi czasu ułożonej przed tobą. Ustawiasz aparat zgodnie z zasadami strefy czasowej - które zmieniają się okresowo ze względu na czas letni lub inne zmiany prawnej definicji strefy czasowej. (Nie masz stabilnej ręki, więc twój aparat się trzęsie.)

Osoba stojąca na zdjęciu widzi kąt z którego pochodzi Twoja kamera. Gdyby inni robili zdjęcia, mogliby być pod różnymi kątami. To właśnie reprezentuje Offset część DateTimeOffset.

Więc jeśli oznaczasz swój aparat "czas Wschodni", czasami wskazujesz od -5, a czasami wskazujesz od -4. Są kamery na całym świecie, wszystkie oznaczone różnymi rzeczami, i wszystkie wskazujące na tę samą chwilową oś czasu z różnych kątów. Niektóre z nich są tuż obok (lub na) siebie, więc po prostu znajomość przesunięcia nie wystarczy, aby określić, z którą strefą czasową jest związany.

A co z UTC? Cóż, to jedyna kamera, która ma gwarancję, że będzie miała stałą rękę. Jest na statywie, mocno zakotwiczony w ziemi. Nigdzie się nie wybiera. Jego kąt widzenia nazywamy przesunięciem zerowym.

Chwilowy czas a Wizualizacja czasu w kalendarzu

Więc - co ta analogia nam mówi? Zawiera kilka intuicyjnych wytycznych.

  • Jeśli reprezentujesz czas względny do jakiegoś miejsca w szczególności, reprezentować go w czasie kalendarzowym za pomocą DateTime. Tylko upewnij się, że nigdy nie pomylisz jednego kalendarza z innym. To powinno być twoje założenie. Local jest użyteczny tylko z DateTime.Now. Na przykład, mogę uzyskać DateTime.Now i zapisać go w bazie danych - ale kiedy go odzyskam, muszę założyć, że jest to Unspecified. Nie mogę polegać na tym, że mój lokalny kalendarz jest tym samym kalendarzem, z którego został pierwotnie wzięty.

  • Jeśli zawsze musisz być pewny chwili, upewnij się, że reprezentujesz chwilowy czas. Użyj DateTimeOffset, aby go wymusić, lub użyj UTC DateTime zgodnie z konwencją.

  • Jeśli chcesz śledzić chwilę chwilowego czasu, ale chcesz również wiedzieć "o której godzinie użytkownik myślał, że jest w swoim lokalnym kalendarzu?"- wtedy musisz użyć DateTimeOffset. Jest to bardzo ważne na przykład w przypadku systemów pomiaru czasu - zarówno w kwestiach technicznych, jak i prawnych.

  • Jeśli kiedykolwiek będziesz musiał zmodyfikować wcześniej nagrany DateTimeOffset - nie masz wystarczającej ilości informacji w samym offsecie, aby upewnić się, że nowy offset jest nadal odpowiedni dla użytkownika. Musisz również zapisać identyfikator strefy czasowej (pomyśl - potrzebuję nazwy tego aparatu, aby móc zrobić nowe zdjęcie, nawet jeśli pozycja się zmieniła).

    Należy również zwrócić uwagę, że czas Noda ma do tego celu reprezentację o nazwie ZonedDateTime, podczas gdy Biblioteka klas bazowych. Net nie ma niczego podobnego. Musisz przechowywać zarówno DateTimeOffset i TimeZoneInfo.Id wartość.

  • Czasami będziesz chciał reprezentować czas kalendarzowy, który jest lokalny dla "każdego, kto na niego patrzy". Na przykład, definiując co dzisiaj oznacza. Dziś jest zawsze od północy do północy, ale reprezentują one niemal nieskończoną liczbę nakładających się zakresów na chwilowej osi czasu. (W praktyce mamy skończoną liczbę stref czasowych, ale można wyrazić przesunięcia aż do kleszcza), więc w takich sytuacjach upewnij się, że rozumiesz, jak albo ograniczyć " kto pyta?"pytanie w dół do jednej strefy czasowej, lub radzić sobie z tłumaczeniem ich z powrotem do czasu chwilowego, stosownie do potrzeb.

Oto kilka innych drobiazgów na temat DateTimeOffset, które wspierają tę analogię, i kilka wskazówek, jak utrzymać ją prosto: {]}

  • Jeśli porównasz dwie wartości DateTimeOffset, są one najpierw znormalizowane do zerowego przesunięcia przed porównaniem. Innymi słowy, 2012-01-01T00:00:00+00:00 i 2012-01-01T02:00:00+02:00 odnoszą się do tej samej chwili i dlatego są odpowiednik.

  • Jeśli wykonujesz jakieś testy jednostkowe i musisz być pewien offsetu, przetestuj zarówno wartość DateTimeOffset, jak i właściwość .Offset oddzielnie.

  • Istnieje jednokierunkowa ukryta konwersja wbudowana w. Net framework, która pozwala przekazać DateTime do dowolnego parametru lub zmiennej DateTimeOffset. Kiedy to robimy, .Kind ma znaczenie . Jeśli zdasz Typ UTC, będzie on przenoszony z zerowym przesunięciem, ale jeśli zdasz .Local lub .Unspecified, będzie Załóżmy, że jest lokalny . Framework w zasadzie mówi: "Cóż, poprosiłeś mnie o konwersję czasu kalendarza na chwilowy czas, ale nie mam pojęcia, skąd to się wzięło, więc po prostu użyję lokalnego kalendarza."To jest ogromna gotcha, jeśli załadujesz nieokreślony DateTime na komputer z inną strefą czasową. (IMHO-to powinno rzucić wyjątek-ale nie.)

Wtyczka Bezwstydna:

Wiele osób podzieliło się ze mną, że znajdują ta analogia jest niezwykle cenna, więc włączyłem ją do mojego kursu Pluralsight, podstawy daty i czasu . Krok po kroku zapoznasz się z analogią kamery w drugim module, "context Matters", w klipie zatytułowanym"czas kalendarzowy a czas Chwilowy".

 891
Author: Matt Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-19 00:51:58

From Microsoft:

Te zastosowania dla wartości DateTimeOffset są znacznie bardziej powszechne niż dla wartości DateTime. W związku z tym DateTimeOffset powinien być uważany za domyślny typ daty i czasu dla tworzenia aplikacji.

źródło: "wybór pomiędzy DateTime, DateTimeOffset, TimeSpan i TimeZoneInfo", MSDN

Używamy DateTimeOffset dla prawie wszystkiego, ponieważ nasza aplikacja zajmuje się określonymi punktami w czasie (np. kiedy rekord został utworzony/zaktualizowany). Na marginesie, używamy DATETIMEOFFSET również w SQL Server 2008.

Widzę DateTime jako przydatne, gdy chcesz zajmować się tylko datami, tylko godzinami lub zajmować się albo w sensie ogólnym. Na przykład, jeśli masz alarm, który chcesz wyłączyć codziennie o 7 rano, możesz zapisać go w DateTime wykorzystując DateTimeKind z Unspecified, ponieważ chcesz, aby wyłączyć go o 7 rano, niezależnie od czasu letniego. Ale jeśli chcesz reprezentować historię wystąpień alarmów, użyj DateTimeOffset.

Należy zachować ostrożność przy stosowaniu kombinacji DateTimeOffset i DateTime, zwłaszcza przy przypisywaniu i porównywaniu typów. Ponadto, porównuj tylko instancje DateTime, które są takie same DateTimeKind, Ponieważ DateTime ignoruje przesunięcie strefy czasowej podczas porównywania.

 209
Author: Clay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-09 03:54:31

DateTime może przechowywać tylko dwa różne czasy, czas lokalny i UTC. Właściwość Kind wskazuje, które.

DateTimeOffset rozszerza się o to dzięki możliwości przechowywania czasu lokalnego z dowolnego miejsca na świecie. Zapisuje również offset pomiędzy tym czasem lokalnym A UTC. Zauważ, że DateTime nie może tego zrobić, chyba że dodasz dodatkowego członka do swojej klasy, aby zapisać przesunięcie UTC. Lub tylko pracować z UTC. Co samo w sobie jest dobrym pomysłem btw.

 56
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-02 17:47:08

Jest kilka miejsc, gdzie DateTimeOffset ma sens. Po pierwsze, gdy masz do czynienia z powtarzającymi się wydarzeniami i czasem letnim. Powiedzmy, że chcę włączyć alarm o 9 rano każdego dnia. Jeśli używam reguły "store as UTC, display as local time", wtedy alarm zostanie wyłączony o innym czasie, gdy obowiązuje czas letni.

Są prawdopodobnie inne, ale powyższy przykład jest tak naprawdę taki, na który natknąłem się w przeszłości (to było przed dodaniem DateTimeOffset do BCL-moim rozwiązaniem w tym czasie było jawne przechowywanie czasu w lokalnej strefie czasowej i zapisywanie informacji o strefie czasowej wzdłuż jej boku: zasadniczo to, co DateTimeOffset robi wewnętrznie).

 31
Author: Dean Harding,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-02 02:59:58

Najważniejszą różnicą jest to, że DateTime nie przechowuje informacji o strefie czasowej, podczas gdy DateTimeOffset tak.

Chociaż DateTime rozróżnia UTC i Local, nie ma absolutnie żadnego wyraźnego przesunięcia strefy czasowej związanego z nim. Jeśli zrobisz jakikolwiek rodzaj serializacji lub konwersji, Strefa czasowa serwera będzie używana. Nawet jeśli ręcznie utworzysz czas lokalny, dodając minuty w celu przesunięcia czasu UTC, nadal możesz uzyskać bit w kroku serializacji, ponieważ (ze względu na brak wyraźnego przesunięcia w DateTime) użyje przesunięcia strefy czasowej serwera.

Na przykład, jeśli serializujesz wartość DateTime z Kind = Local używając Json.Net i format daty ISO, otrzymasz ciąg znaków jak 2015-08-05T07:00:00-04. Zauważ, że ostatnia część (-04) nie miała nic wspólnego z Twoją DateTime lub jakimkolwiek offsetem, którego użyłeś do jej obliczenia... to tylko przesunięcie strefy czasowej serwera.

Tymczasem DateTimeOffset jawnie zawiera offset. Może nie zawierać nazwy czasu Strefa, ale przynajmniej zawiera offset, a jeśli go serializujesz, dostaniesz jawnie włączone offset w wartości zamiast tego, co lokalny czas serwera dzieje się.

 16
Author: Triynko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-05 23:00:02

Większość odpowiedzi jest dobra, ale pomyślałem o dodaniu kilku linków do MSDN, aby uzyskać więcej informacji

 9
Author: dekdev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-29 00:47:03

Główną różnicą jest to, że DateTimeOffset może być używany w połączeniu z TimeZoneInfo do konwersji na czasy lokalne w strefach czasowych innych niż bieżąca.

Jest to przydatne w aplikacji serwerowej (np. ASP.NET), do którego dostęp mają użytkownicy w różnych strefach czasowych.

 7
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-02 13:03:40

Jedyną negatywną stroną DateTimeOffset widzę, że Microsoft "zapomniał" (z założenia) wspierać go w swojej klasie XmlSerializer. Ale od tego czasu został dodany do klasy użytkowej xmlconvert.

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

Mówię śmiało i używaj DateTimeOffset i TimeZoneInfo ze względu na wszystkie korzyści, po prostu uważaj podczas tworzenia encji, które będą lub mogą być serializowane do lub z XML (wszystkie obiekty biznesowe wtedy).

 2
Author: Tony Wall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-27 19:41:33