Представление DateTime как локальный для удаленного пользователя
Здравствуйте!
Я запутался в проблеме часовых поясов. Я пишу веб-приложение, которое будет содержать некоторые новости с датами публикации, и я хочу, чтобы клиент видел дату публикации новостей в виде соответствующего местного времени. Однако я не знаю, в каком часовом поясе находится клиент.
У меня три вопроса.
Я должен спросить на всякий случай: делает DateTimeOffset.UtcNow
всегда возвращает правильную дату и время UTC, независимо от того, зависит ли сервер от перехода на летнее время? Например, если в первый раз я получу значение этого свойства за две минуты до перехода на летнее время (или до перехода с перехода на летнее время назад), а второй раз через 2 минуты после передачи, будет ли значение свойства во всех дела отличаются только на 4 минуты? Или тут нужна какая-то дополнительная логика? (Вопрос № 1)
Пожалуйста, посмотрите следующий пример и скажите мне, что вы думаете.
Я разместил новости на сайте. Я предполагаю что DateTimeOffset.UtcNow
учитывает часовой пояс сервера и летнее время, поэтому я сразу получаю правильное время UTC сервера при нажатии кнопки "Отправить". Я записываю это значение в базу данных MS SQL в поле типа datetime2 (0).
Затем пользователь открывает страницу с новостями и неважно, как долго после публикации. Это может произойти даже через много лет. Я не просил его ввести свой часовой пояс. Вместо этого я получаю смещение его текущего местного времени из UTC, используя функцию javascript следующим образом:
function GetUserTimezoneOffset()
{
var offset = new Date().getTimezoneOffset();
return offset;
}
Далее я делаю расчет даты и времени публикации, который покажет пользователю:
public static DateTime Get_Publication_Date_In_User_Local_DateTime(
DateTime Publication_Utc_Date_Time_From_DataBase,
int User_Time_Zone_Offset_Returned_by_Javascript)
{
int userTimezoneOffset = User_Time_Zone_Offset_Returned_by_Javascript; // For
// example Javascript returns a value equal to -300, which means the
// current user's time differs from UTC to 300 minutes. Ie offset
// is UTC +6. In this case, it may be the time zone UTC +5 which
// currently operates summer time or UTC +6 which currently operates the
// standard time.
// Right? (Question #2)
DateTimeOffset utcPublicationDateTime =
new DateTimeOffset(Publication_Utc_Date_Time_From_DataBase,
TimeSpan.Zero); // get an instance of type DateTimeOffset for the
// date and time of publication for further calculations
DateTimeOffset publication_DateTime_In_User_Local_DateTime =
utcPublicationDateTime.ToOffset(new TimeSpan(0, - userTimezoneOffset, 0));
return publication_DateTime_In_User_Local_DateTime.DateTime;// return to user
}
Является ли полученное значение правильным? Это правильный подход к решению этой проблемы? (Вопрос № 3)
ОБНОВЛЕНО 19 октября в 6:58 (я пытался опубликовать его как комментарий, но он слишком длинный, 668 символов)
Мэтт Джонсон, спасибо за такой подробный ответ, несмотря на тот факт, что вы делаете это не в первый раз. Спасибо, что нашли время объяснить этот конкретный случай, а не просто предоставить ссылки на другие сообщения.
Я прочитал информацию, которую вы предоставили. Может быть, я до сих пор не полностью осведомлен обо всех деталях, но если я правильно понимаю, для правильного преобразования DateTime (который был написан много лет назад в базе данных) из UTC в момент того же пользователя, мне нужно знать UTC смещение, которое он имел в тот момент. И это сложно с учетом того, что правила перехода на летнее время меняются постоянно. И даже сейчас, хотя платформа ".NET" содержит некоторую TZDB, которая используется для типа TimeZoneInfo, я не могу воспользоваться этим без точной позиции пользователя.
Но что, если меня интересует только дата и время начала этого года, и только в России, где DST был отменен в 2011 году? Насколько я понимаю, это означает, что при правильно настроенных часах на компьютере пользователя, расположенного в России, такой подход всегда даст правильный результат. А с 2011 года смещение UTC часов пользователя всегда должно быть одинаковым. Соответственно, показатели смещения в разных браузерах не будут отличаться для российского пользователя.
1 ответ
Ответ на вопрос 1
... делает
DateTimeOffset.UtcNow
всегда возвращает правильную дату и время UTC, независимо от того, зависит ли сервер от перехода на летнее время?
Да. Пока ваши часы установлены правильно, UtcNow
всегда относится к времени UTC. Настройки часового пояса сервера на это не влияют. Значение в вашем примере всегда будет 4 минуты, независимо от летнего времени.
Ответ на вопрос 2
var offset = new Date().getTimezoneOffset();
поскольку new Date()
возвращает текущую дату и время, это вернет вам текущее смещение. Затем вы продолжаете применять это текущее смещение к некоторому прошлому значению, которое может быть или не быть правильным смещением для этого определенного времени. Пожалуйста, прочтите вики-тег с часовым поясом, особенно раздел под названием "Часовой пояс!= Смещение".
Ответ на вопрос 3
Является ли полученное значение правильным? Это правильный подход к решению этой проблемы?
Нет, это не правильный подход. У вас есть несколько вариантов:
Первый вариант
- Просто передайте значение UTC в JavaScript без изменений.
- Отправьте его в формате ISO8601, например
2013-10-18T12:34:56.000Z
, Вы можете легко получить это в.Net используяyourDateTime.ToString("o")
,- Будь уверен
DateTime
вы начинаете с.Kind == DateTimeKind.Utc
иначе он не получитZ
в конце, что важно. - Если вы ориентируетесь на старые браузеры, которые не могут анализировать ISO8601, то вам понадобится библиотека, такая как moment.js, чтобы проанализировать ее для вас.
- Будь уверен
- Кроме того, вы можете передать число миллисекунд с 01.01.1970 UTC как число и загрузить его в JavaScript
Date
вместо разбора. - Теперь вы можете просто отобразить
Date
объект с использованием JavaScript. Пусть браузер конвертирует его из UTC в местное время пользователя.
Внимание, при таком подходе некоторые преобразования могут быть неправильными из- за проблемы, которую я здесь описываю.
Второй вариант
- Как и в первом варианте, передайте метку времени UTC в JavaScript
- Используйте это, чтобы получить смещение для этой отметки времени.
- Передайте смещение обратно на сервер в обратном вызове или вызове ajax.
- Применить смещение на сервере
- Вывести местный часовой пояс
Мне не особенно нравится этот вариант из-за поездки туда и обратно. Вы можете также рассчитать его в JavaScript, как первый вариант.
Третий вариант
- Получить часовой пояс пользователя. Лучший способ - попросить их об этом, обычно на странице настроек пользователя.
- Используйте его, чтобы полностью преобразовать время UTC в местное время на сервере.
- Вы можете использовать часовые пояса Windows и
TimeZoneInfo
класс, чтобы сделать преобразования. - Или вы можете использовать часовые пояса IANA/Olson и библиотеку времени Noda.
- Если вы сделаете это, вы можете по желанию использовать средство выбора часового пояса на основе карты.
- Вы можете сделать предположение о часовом поясе пользователя с помощью jsTimeZoneDetect.
Этот подход более точен, но требует большего взаимодействия с пользователем.
Также, пожалуйста, в будущих постах задавайте только один вопрос за раз. И, пожалуйста, поищите, прежде чем отправлять сообщения - я написал это в той или иной форме много раз.