DateTimeOffset
являє собою подання миттєвого часу (також відомого як абсолютний час ). Маючи на увазі, я маю на увазі момент, який є універсальним для всіх (не враховуючи високосних секунд або релятивістські ефекти розширення часу ). Ще один спосіб представити миттєвий час - це " DateTime
де .Kind
є" DateTimeKind.Utc
.
Це відрізняється від календарного часу (також відомого як громадянський час ), який є позицією в чиємусь календарі, і є багато різних календарів по всьому світу. Ці календарі ми називаємо часовими поясами . Час календаря представлено символом, DateTime
де .Kind
є DateTimeKind.Unspecified
, або DateTimeKind.Local
. І .Local
має сенс лише в сценаріях, де ви розумієте, де розташований комп'ютер, який використовує результат. (Наприклад, робоча станція користувача)
Тож чому DateTimeOffset
замість UTC DateTime
? Вся справа в перспективі. Давайте скористаємось аналогією - будемо робити вигляд, що фотографи.
Уявіть, що ви стоїте на календарній шкалі часу, вказуючи камеру на людину на миттєвій часовій шкалі, розкладеній перед вами. Ви розміщуєте камеру за правилами свого часового поясу, які періодично змінюються через літній час або через інші зміни в законодавчому визначенні вашого часового поясу. (У вас немає стійкої руки, тому ваша камера хитка.)
Людина, що стоїть на фотографії, побачила б кут, під яким вийшла ваша камера. Якби інші фотографували, вони могли бути з різних куточків. Це те, що представляє Offset
частина DateTimeOffset
представників.
Тож якщо ви позначаєте свою камеру "Східним часом", іноді ви вказуєте від -5, а іноді - від -4. У всьому світі є камери, на яких позначено різні речі, і всі вказують на одну і ту ж миттєву шкалу часу з різних ракурсів. Деякі з них знаходяться поруч (або зверху), тому просто знаючи зміщення недостатньо, щоб визначити, з яким часовим поясом пов’язаний час.
А як щодо UTC? Ну, це одна камера, яка гарантовано матиме стійку руку. Це на штативі, міцно закріпленому в землі. Нікуди не дінеться. Ми називаємо його кут точки зору нульовим зміщенням.
Отже - що нам говорить ця аналогія? Він пропонує інтуїтивні вказівки,
Якщо ви представляєте час відносно якогось місця, зокрема, представляйте його в календарному часі за допомогою а DateTime
. Просто будьте впевнені, що ви ніколи не плутаєте один календар з іншим. Unspecified
має бути вашим припущенням. Local
корисний лише звідки DateTime.Now
. Наприклад, я можу отримати DateTime.Now
і зберегти його в базі даних, але коли я його завантажую, я повинен припустити, що він є Unspecified
. Я не можу розраховувати, що мій місцевий календар - це той самий календар, з якого він був взятий.
Якщо ви завжди повинні бути впевнені в моменті, переконайтесь, що ви представляєте миттєвий час. Використовуйте DateTimeOffset
для його виконання або використовуйте UTC DateTime
за умовами.
Якщо вам потрібно відстежити момент миттєвого часу, але ви хочете також знати "Який час користувач думав, що це в їхньому місцевому календарі?" - тоді ви повинні використовувати a DateTimeOffset
. Це дуже важливо, наприклад, для систем хронометражу - як для технічних, так і для юридичних питань.
Якщо вам коли-небудь потрібно змінити записаний раніше DateTimeOffset
- вам не вистачає інформації лише в зміщенні, щоб переконатися, що новий зсув все ще актуальний для користувача. Ви також повинні зберегти ідентифікатор часового поясу (подумайте - мені потрібна назва цієї камери, щоб я міг сфотографуватись, навіть якщо позиція змінилася).
Слід також зазначити, що для цього Noda Time має представництво ZonedDateTime
, тоді як бібліотека базового класу .Net не має нічого подібного. Вам потрібно буде зберігати DateTimeOffset
і TimeZoneInfo.Id
значення, і значення.
Іноді вам потрібно представити календарний час, який є локальним для того, щоб "хто дивиться на це". Наприклад, при визначенні того, що сьогодні означає. Сьогодні завжди опівночі до півночі, але вони являють собою майже нескінченну кількість діапазонів, що перетинаються на миттєвій шкалі часу. (На практиці у нас є обмежена кількість часових поясів, але ви можете виразити зміщення до кліща). Тому в цих ситуаціях переконайтеся, що ви розумієте, як обмежити "хто просить?" запитання до єдиного часового поясу або вирішити їх переклад назад у миттєвий час, якщо це доречно.
Ось кілька інших невеликих шматочків щодо DateTimeOffset
резервної копії цієї аналогії, а також кілька порад щодо її правильності:
Якщо порівнювати два DateTimeOffset
значення, вони спочатку нормалізуються до нульового зміщення перед порівнянням. Іншими словами, 2012-01-01T00:00:00+00:00
і 2012-01-01T02:00:00+02:00
посилаються на той самий миттєвий момент, і тому є рівнозначними.
Якщо ви робите якісь - або модульного тестування і повинні бути впевнені в офсетного, випробування як в DateTimeOffset
вартості, і .Offset
власності окремо.
Існує одностороння неявна конверсія, вбудована в .Net фреймворк, що дозволяє переходити DateTime
до будь-якого DateTimeOffset
параметра або змінної. При цьому, питання . Якщо ви передаєте тип UTC, він здійснюватиметься з нульовим зміщенням, але якщо ви передасте або, або він вважатиметься локальним . В основному рамки говорять: "Ну, ви попросили мене перетворити календарний час на миттєвий час, але я не маю уявлення, звідки це взялося, тому я просто збираюся використовувати місцевий календар". Це величезна проблема, якщо ви завантажуєте не визначене на комп'ютер з іншим часовим поясом. (IMHO - це повинно кинути виняток, але це не так.).Kind
.Local
.Unspecified
DateTime
Безсоромний штекер:
Багато людей поділилися зі мною, що вважають цю аналогію надзвичайно цінною, тому я включив її у свій курс з питань плюралізму, Основи дати та часу . Покрокове ознайомлення з аналогією камери ви знайдете у другому модулі "Контекстні питання" у кліпі під назвою "Час календаря проти миттєвого часу".