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
jest DateTimeKind.Unspecified
lub DateTimeKind.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 UTC DateTime
? 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 z DateTime.Now
. Na przykład mogę go pobrać DateTime.Now
i zapisać w bazie danych - ale kiedy go odzyskam , muszę założyć, że tak jest Unspecified
. 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 UTC DateTime
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 i TimeZoneInfo.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
i 2012-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
dowolnego DateTimeOffset
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”.