Представление 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.

Этот подход более точен, но требует большего взаимодействия с пользователем.

Также, пожалуйста, в будущих постах задавайте только один вопрос за раз. И, пожалуйста, поищите, прежде чем отправлять сообщения - я написал это в той или иной форме много раз.

Другие вопросы по тегам