Obecnie mamy standardowy sposób radzenia sobie z platformą .NET DateTime
w sposób świadomy DateTime
w strefie czasowej : Za każdym razem, gdy produkujemy coś takiego, robimy to w UTC (np. Za pomocą DateTime.UtcNow
) i za każdym razem, gdy go wyświetlamy, konwertujemy z powrotem z UTC na czas lokalny użytkownika .
To działa dobrze, ale czytałem o DateTimeOffset
tym, jak przechwytuje czas lokalny i czas UTC w samym obiekcie. Pytanie brzmi: jakie byłyby zalety korzystania DateTimeOffset
z tego, co już robiliśmy?
Odpowiedzi:
DateTimeOffset
jest reprezentacją czasu chwilowego (znanego również jako czas bezwzględny ). Rozumiem przez to moment w czasie, który jest powszechny dla wszystkich (nie uwzględniając sekund przestępnych lub relatywistycznych efektów dylatacji czasu ). Innym sposobem przedstawienia chwilowego czasu jest użycie opcji „DateTime
gdzie.Kind
jest”DateTimeKind.Utc
.Różni się to od czasu kalendarzowego (znanego również jako czas cywilny ), który jest pozycją w czyimś kalendarzu, i istnieje wiele różnych kalendarzy na całym świecie. Te kalendarze nazywamy strefami czasowymi . Czas w kalendarzu jest reprezentowany przez
DateTime
gdzie.Kind
jestDateTimeKind.Unspecified
lubDateTimeKind.Local
. I.Local
ma sens jedynie w sytuacjach, gdzie trzeba dorozumianą zrozumienia gdzie komputer, który wykorzystuje efekt jest umieszczony. (Na przykład stacja robocza użytkownika)Dlaczego więc
DateTimeOffset
zamiast UTCDateTime
? Chodzi o perspektywę. Wykorzystajmy analogię - będziemy udawać fotografów.Wyobraź sobie, że stoisz na osi czasu kalendarza, wskazując aparat na osobę na natychmiastowej linii czasu ustawionej przed tobą. Ustaw aparat zgodnie z zasadami obowiązującymi w strefie czasowej - które zmieniają się okresowo ze względu na czas letni lub inne zmiany w prawnej definicji strefy czasowej. (Nie masz pewnej ręki, więc aparat drży.)
Osoba stojąca na zdjęciu zobaczy kąt, z którego pochodzi twój aparat. Gdyby inni robili zdjęcia, mogliby być pod różnymi kątami. To właśnie reprezentuje
Offset
częśćDateTimeOffset
.Jeśli więc oznaczysz kamerę jako „Czas wschodni”, czasami wskazujesz na -5, a czasem na -4. Istnieją kamery na całym świecie, wszystkie oznaczone różnymi rzeczami i wszystkie wskazujące tę samą natychmiastową oś czasu pod różnymi kątami. Niektóre z nich znajdują się obok siebie (lub na sobie), więc sama znajomość przesunięcia nie wystarczy, aby określić, z którą strefą czasową jest związany czas.
A co z UTC? Cóż, jest to jedyny aparat, który ma pewną rękę. Jest na statywie, mocno zakotwiczonym w ziemi. Nigdzie się nie wybiera. Kąt perspektywy nazywamy przesunięciem zerowym.
Co więc mówi nam ta analogia? Zawiera kilka intuicyjnych wskazówek
Jeśli reprezentujesz czas w stosunku do konkretnego miejsca, reprezentuj go w czasie kalendarzowym za pomocą
DateTime
. Tylko upewnij się, że nigdy nie pomylisz jednego kalendarza z drugim.Unspecified
powinno być twoim założeniem.Local
jest użyteczne tylko zDateTime.Now
. Na przykład mogę go pobraćDateTime.Now
i zapisać w bazie danych - ale kiedy go odzyskam , muszę założyć, że tak jestUnspecified
. Nie mogę polegać na tym, że mój kalendarz lokalny jest tym samym kalendarzem, z którego został pierwotnie pobrany.Jeśli musisz zawsze mieć pewność co do chwili, upewnij się, że reprezentujesz natychmiastowy czas. Użyj,
DateTimeOffset
aby go wymusić, lub użyj UTCDateTime
zgodnie z konwencją.Jeśli chcesz śledzić chwilę chwilową, ale chcesz także wiedzieć „O której godzinie użytkownik sądził, że jest to w lokalnym kalendarzu?” - musisz użyć
DateTimeOffset
. Jest to bardzo ważne na przykład w przypadku systemów pomiaru czasu - zarówno ze względów technicznych, jak i prawnych.Jeśli kiedykolwiek będziesz musiał zmodyfikować wcześniej zarejestrowany zapis
DateTimeOffset
- nie masz wystarczającej ilości informacji w samym odsunięciu, aby upewnić się, że nowe przesunięcie jest nadal odpowiednie dla użytkownika. Musisz także zapisać identyfikator strefy czasowej (pomyśl - potrzebuję nazwy tego aparatu, aby móc zrobić nowe zdjęcie, nawet jeśli pozycja uległa zmianie).Należy również zauważyć, że Noda Time ma reprezentację wymaganą
ZonedDateTime
do tego, podczas gdy biblioteka klas podstawowych .Net nie ma niczego podobnego. Musisz przechowywać zarówno wartośćDateTimeOffset
a, jak iTimeZoneInfo.Id
wartość.Czasami będziesz chciał przedstawić czas kalendarzowy lokalny dla „każdego, kto na niego patrzy”. Na przykład przy określaniu, co dzisiaj oznacza. Dzisiaj jest zawsze północ do północy, ale reprezentują one prawie nieskończoną liczbę nakładających się zakresów na natychmiastowej linii czasu. (W praktyce mamy skończoną liczbę stref czasowych, ale można wyrazić przesunięcia aż do tyknięcia). W takich sytuacjach upewnij się, że rozumiesz, jak ograniczyć „kto pyta?” przesłuchać w jednej strefie czasowej lub odpowiednio przełożyć ją na czas natychmiastowy.
Oto kilka innych drobiazgów na
DateTimeOffset
ten temat, które potwierdzają tę analogię, oraz kilka wskazówek, jak zachować prostotę:Jeśli porównasz dwie
DateTimeOffset
wartości, przed porównaniem są one normalizowane do zerowego przesunięcia. Innymi słowy,2012-01-01T00:00:00+00:00
i2012-01-01T02:00:00+02:00
odnoszą się do tej samej chwilowej chwili, a zatem są równoważne.Jeśli robisz żadnych testów jednostkowych i trzeba mieć pewność, offsetowej, test zarówno na
DateTimeOffset
wartości, a.Offset
nieruchomość osobno.Istnieje wbudowana jednokierunkowa konwersja wbudowana w platformę .Net, która umożliwia przekazanie
DateTime
dowolnegoDateTimeOffset
parametru lub zmiennej. Dokonując tego, liczy . Jeśli zdasz typ UTC, zostanie on przeniesiony z zerowym przesunięciem, ale jeśli zdasz jeden z nich lub , przyjmie on, że jest lokalny . Ramy w zasadzie mówią: „Poprosiłeś mnie o konwersję czasu kalendarzowego na czas natychmiastowy, ale nie mam pojęcia, skąd on pochodzi, więc po prostu użyję lokalnego kalendarza”. To ogromna gotcha, jeśli załadujesz nieokreślony na komputerze z inną strefą czasową. (IMHO - to powinno wyrzucić wyjątek - ale tak nie jest.).Kind
.Local
.Unspecified
DateTime
Bezwstydna wtyczka:
Wiele osób podzieliło się ze mną, że uważają tę analogię za niezwykle cenną, dlatego umieściłem ją w moim kursie Pluralsight, Data i godzina Podstawy . Szczegółowy opis analogii kamery znajduje się w drugim module „Kontekst ma znaczenie” w klipie zatytułowanym „Czas kalendarzowy a czas natychmiastowy”.
źródło
DateTimeOffset
w C #, powinieneś zachować toDATETIMEOFFSET
na SQL Server.DATETIME2
lub po prostuDATETIME
(w zależności od wymaganego zakresu) są odpowiednie dla regularnychDateTime
wartości. Tak - czas lokalny można rozwiązać z dowolnej pary stref czasowych + dto lub utc. Różnica polega na tym, czy zawsze chcesz obliczać reguły przy każdym rozwiązaniu, czy też chcesz je wstępnie obliczyć? W wielu przypadkach (czasem z powodów prawnych) lepszym wyborem jest DTO.DateTimeOffset.Now
na serwerze, rzeczywiście otrzymasz przesunięcie serwera. Chodzi o to, żeDateTimeOffset
typ może zachować to przesunięcie. Możesz równie łatwo zrobić to na kliencie, wysłać go na serwer, a wtedy twój serwer będzie wiedział przesunięcie klienta.Od Microsoft:
źródło: „Wybór pomiędzy DateTime, DateTimeOffset, TimeSpan i TimeZoneInfo” , MSDN
Używamy
DateTimeOffset
prawie wszystkiego, ponieważ nasza aplikacja zajmuje się konkretnymi momentami w czasie (np. Kiedy rekord został utworzony / zaktualizowany). Na marginesie, używamy równieżDATETIMEOFFSET
w SQL Server 2008.Uważam,
DateTime
że jest przydatny, gdy chcesz poradzić sobie tylko z datami, tylko czasami lub z ogólnymi. Na przykład, jeśli masz alarm, który chcesz zgaśnie codziennie o 7 rano, można zapisać, że w sposóbDateTime
wykorzystującyDateTimeKind
odUnspecified
bo chcesz go zgaśnie o 7 rano, niezależnie od DST. Ale jeśli chcesz przedstawić historię zdarzeń alarmowych, skorzystaszDateTimeOffset
.Zachowaj ostrożność, używając kombinacji,
DateTimeOffset
aDateTime
zwłaszcza podczas przypisywania i porównywania typów. Ponadto porównuj tylkoDateTime
takie same wystąpienia,DateTimeKind
ponieważDateTime
podczas porównywania ignorowane jest przesunięcie strefy czasowej.źródło
Kind
się, że są takie same, porównanie może być błędne. Jeśli obie stronyDateTimeKind.Unspecified
tak naprawdę nie wiedzą, że pochodzą z tej samej strefy czasowej. Jeśli obie strony sąDateTimeKind.Local
, większość porównań będzie w porządku, ale nadal możesz mieć błędy, ponieważ jedna strona jest niejednoznaczna w lokalnej strefie czasowej. Naprawdę tylkoDateTimeKind.Utc
porównania są niezawodne i tak,DateTimeOffset
zwykle jest preferowane. (Na zdrowie!)DateTime może przechowywać tylko dwa różne czasy, czas lokalny i UTC. Właściwość Kind wskazuje, która.
DateTimeOffset rozwija się dzięki temu, że może przechowywać czasy lokalne z dowolnego miejsca na świecie. Przechowuje również przesunięcie między czasem lokalnym a czasem UTC. Zwróć uwagę, że DateTime nie może tego zrobić, chyba że dodasz dodatkowego członka do swojej klasy, aby zapisać to przesunięcie UTC. Lub tylko kiedykolwiek współpracuje z UTC. Co samo w sobie jest świetnym pomysłem.
źródło
Jest kilka miejsc, w których
DateTimeOffset
ma to sens. Jednym z nich jest sytuacja powtarzających się zdarzeń i czasu letniego. Powiedzmy, że chcę ustawić alarm, aby uruchamiał się codziennie o 9 rano. Jeśli użyję reguły „zapisz jako UTC, wyświetlaj jako czas lokalny”, to alarm wyłączy się w innym czasie, gdy obowiązuje czas letni.Prawdopodobnie są też inne, ale powyższy przykład jest właściwie tym, na który natknąłem się w przeszłości (to było przed dodaniem
DateTimeOffset
do BCL - moim rozwiązaniem było wtedy jawne przechowywanie czasu w lokalnej strefie czasowej i oszczędzanie informacje o strefie czasowej obok: w zasadzie to, coDateTimeOffset
działa wewnętrznie).źródło
Najważniejsze jest to, że DateTime nie przechowuje informacji o strefie czasowej, podczas gdy DateTimeOffset tak.
Mimo że DateTime rozróżnia UTC od lokalnego, nie ma absolutnie żadnego wyraźnego przesunięcia strefy czasowej z tym związane. W przypadku serializacji lub konwersji zostanie użyta strefa czasowa serwera. 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ż (z powodu braku wyraźnego przesunięcia w DateTime) użyje przesunięcia strefy czasowej serwera.
Na przykład, jeśli szeregujesz wartość DateTime za pomocą Kind = Local przy użyciu Json.Net i formatu daty ISO, otrzymasz ciąg podobny do
2015-08-05T07:00:00-04
. Zauważ, że ostatnia część (-04) nie miała nic wspólnego z twoim DateTime ani jakimkolwiek przesunięciem, którego użyłeś do obliczenia ... to po prostu przesunięcie strefy czasowej serwera.Tymczasem DateTimeOffset wyraźnie zawiera przesunięcie. Może nie zawierać nazwy strefy czasowej, ale przynajmniej zawiera przesunięcie, a jeśli go serializujesz, otrzymasz jawnie uwzględnione przesunięcie w swojej wartości zamiast tego, co dzieje się na lokalnym serwerze.
źródło
The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Ten fragment kodu firmy Microsoft wyjaśnia wszystko:
źródło
Większość odpowiedzi jest dobra, ale pomyślałem o dodaniu kilku linków MSDN, aby uzyskać więcej informacji
źródło
Główną różnicą jest to, że
DateTimeOffset
można go używać w połączeniu zTimeZoneInfo
konwertowaniem na czasy lokalne w strefach czasowych innych niż bieżąca.Jest to przydatne w aplikacji serwerowej (np. ASP.NET), do której użytkownicy mają dostęp w różnych strefach czasowych.
źródło
Jedyną negatywną stroną DateTimeOffset, którą widzę, jest to, że Microsoft „zapomniał” (z założenia), aby obsługiwać go w swojej klasie XmlSerializer. Ale od tego czasu został dodany do klasy narzędzia 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 (wtedy wszystkie obiekty biznesowe).
źródło