Подтвердить что ты не робот

Спецификация DateTime.Now и культура/Timezone

Наше приложение было разработано для работы с пользователем из другого географического местоположения.

Мы не можем определить, какое текущее конечное пользовательское время и часовой пояс. Они выбирают другую культуру, такую ​​как sv-se, en-us, ta-In даже они получают доступ из часовой пояс в Европе/Лондоне..

Мы размещали его на сервере хостинга в США, пользователи приложений от Norway/Denmark/Sweden/UK/USA/India

Проблема заключается в том, что мы использовали DateTime.Now для хранения созданной/обновленной даты записи и т.д.

Поскольку Сервер работает в США, все пользовательские данные сохраняются как время США: (

После исследования в SO мы решили сохранить все даты истории в БД как DateTime.UtcNow

Проблема:

enter image description here

Существует запись, созданная на 29 Dec 2013, 3:15 P.M Swedish time.

 public ActionResult Save(BookingViewModel model)
    {
        Booking booking = new Booking();
        booking.BookingDateTime = model.BookingDateTime; //10 Jan 2014 2:00 P.M
        booking.Name = model.Name;
        booking.CurrentUserId = (User)Session["currentUser"].UserId;
        //USA Server runs in Pacific Time Zone, UTC-08:00
        booking.CreatedDateTime = DateTime.UtcNow; //29 Dec 2013, 6:15 A.M
        BookingRepository.Save(booking);
        return View("Index");
    }

Мы хотим показать одно и то же время истории для пользователя, который вошел в систему в Индии/Швеции/США.

В настоящее время мы используем текущего пользователя культуры, зарегистрированного и выбираем часовой пояс из файла конфигурации и используем для преобразования с классом TimeZoneInfo

<appSettings>
    <add key="sv-se" value="W. Europe Standard Time" />
    <add key="ta-IN" value="India Standard Time" />
</appSettings>

    private DateTime ConvertUTCBasedOnCuture(DateTime utcTime)
    {
        //utcTime is 29 Dec 2013, 6:15 A.M
        string TimezoneId =                  
                System.Configuration.ConfigurationManager.AppSettings
                [System.Threading.Thread.CurrentThread.CurrentCulture.Name];
        // if the user changes culture from sv-se to ta-IN, different date is shown
        TimeZoneInfo tZone = TimeZoneInfo.FindSystemTimeZoneById(TimezoneId);

        return TimeZoneInfo.ConvertTimeFromUtc(utcTime, tZone);
    }
    public ActionResult ViewHistory()
    {
        List<Booking> bookings = new List<Booking>();
        bookings=BookingRepository.GetBookingHistory();
        List<BookingViewModel> viewModel = new List<BookingViewModel>();
        foreach (Booking b in bookings)
        {
            BookingViewModel model = new BookingViewModel();
            model.CreatedTime = ConvertUTCBasedOnCuture(b.CreatedDateTime);
            viewModel.Add(model);
        }
        return View(viewModel);
    }

Просмотр кода

   @Model.CreatedTime.ToString("dd-MMM-yyyy - HH':'mm")

ПРИМЕЧАНИЕ. Пользователь может изменить культуру/язык до входа в систему. Это приложение на основе локализации, работающее на американском сервере.

Я видел NODATIME, но я не мог понять, как это может помочь в веб-приложении с несколькими культурами, размещенном в другом месте.

Вопрос

Как я могу показать дату создания такой же даты записи 29 Dec 2013, 3:15 P.M для пользователей, зарегистрированных в INDIA/USA/Anywhere`?

На данный момент моя логика в ConvertUTCBasedOnCuture основана на регистрации пользователя в культуре. Это должно быть независимо от культуры, поскольку пользователь может войти в систему, используя любую культуру из Индии/США.

КОЛОНКА БАЗЫ ДАННЫХ

CreatedTime: SMALLDATETIME

ОБНОВЛЕНИЕ: ПОПЫТНОЕ РЕШЕНИЕ:

DATABASE COLUMN TYPE: DATETIMEOFFSET

Пользовательский интерфейс

Наконец, я отправляю текущее локальное время пользователя, используя нижеуказанный код Momento.js в каждом запросе

$.ajaxSetup({
    beforeSend: function (jqXHR, settings) {
        try {
      //moment.format gives current user date like 2014-01-04T18:27:59+01:00
            jqXHR.setRequestHeader('BrowserLocalTime', moment().format());
        }
        catch (e) {
        }
    }
});

ПРИМЕНЕНИЕ

public static DateTimeOffset GetCurrentUserLocalTime()
{
    try
    {
      return 
      DateTimeOffset.Parse(HttpContext.Current.Request.Headers["BrowserLocalTime"]);
    }
    catch
    {
        return DateTimeOffset.Now;
    }
}

после чего вызывается

 model.AddedDateTime = WebAppHelper.GetCurrentUserLocalTime();

В представлении

@Model.AddedDateTime.Value.LocalDateTime.ToString("dd-MMM-yyyy - HH':'mm")

В поле отображается локальное время для пользователя, однако я хочу видеть как dd-MMM-yyyy CET/PST (2 часа назад).

Это 2 часа назад должно рассчитываться с локального времени пользователя. Точно так же, как вопрос, созданный/отредактированный, с отображением часового пояса и расчетами локальных пользователей.

Пример: answered Jan 25 '13 at 17:49 CST (6 hours/days/month ago) Таким образом, другой просмотр пользователя из США/Индии может действительно понять, что эта запись была создана ровно через 6 часов с текущего времени в Индии/США

Почти я думаю, что я достиг всего, кроме формата отображения и расчета. Как я могу это сделать?

4b9b3361

Ответ 1

Похоже, вам нужно сохранить DateTimeOffset вместо DateTime. Вы можете просто сохранить локальный DateTime для пользователя, создающего это значение, но это означает, что вы не можете выполнять какие-либо операции заказа и т.д. Вы не можете просто использовать DateTime.UtcNow, так как это не будет хранить ничего, чтобы указать локальный дата/время пользователя, когда запись была создана.

В качестве альтернативы вы можете сохранить момент времени вместе с пользовательским часовым поясом - это труднее достичь, но даст вам больше информации, так как тогда вы сможете сказать такие вещи, как "Какое местное время пользователя один час позже?"

Хостинг сервера должен быть неактуальным - вы никогда не должны использовать часовой пояс сервера. Тем не менее, вам нужно будет знать соответствующее UTC-смещение (или часовой пояс) для пользователя. Это невозможно сделать на основе только культуры - вы захотите использовать Javascript на пользовательской машине, чтобы определить смещение UTC в то время, когда вас интересует (не обязательно "сейчас" ).

Как только вы определили, как сохранить это значение, получить его просто: если вы уже сохранили время UTC и смещение, вы просто применяете это смещение, и вы вернетесь в исходное местное время пользователя, Вы не сказали, как вы конвертируете значения в текст, но просто должны просто просто отказаться - просто отформатируйте значение, и вы должны получить исходное местное время.

Если вы решили использовать Noda Time, вы просто используете OffsetDateTime вместо DateTimeOffset.

Ответ 2

Стандартный подход заключается в том, чтобы всегда хранить данные времени в формате UTC, если важный момент времени имеет важное значение. На это время не влияют изменения и культуры часовых поясов.

Наиболее распространенный подход к показу времени с часовым поясом - сохранение времени в формате UTC и преобразование в текущую комбинацию пользовательской культуры/часового пояса при отображении значения. Для этого подхода требуется только одно время, указанное в хранилище.

Обратите внимание, что для веб-кейсов (например, ASP.Net) вам может понадобиться сначала определить культуру пользователя/часовой пояс и отправить его на сервер (поскольку эта информация не требуется в запросах GET) или форматирование времени в браузер.

В зависимости от того, что "показывает одно и то же время истории", вам может потребоваться хранить дополнительную информацию, например текущую культуру и/или текущее смещение. Если вам нужно показать время точно так же, как это сделал его оригинальный пользователь, вы также можете сохранить строковое представление (потому что форматы/переводы могут измениться позже, а значение будет выглядеть по-другому, также это необычно).

Примечание. Культура и часовой пояс не связаны друг с другом, поэтому вам нужно решить, как вам нужно обрабатывать такие случаи, как IN-IN culture в часовом поясе PST США.

Ответ 3

Меня немного смущает формулировка вашего вопроса, но кажется, что вы хотите определить часовой пояс своего пользователя.

  • Вы просили расспросить их? Во многих приложениях пользователь выбирает свой часовой пояс в пользовательских настройках.

  • Вы можете выбрать из раскрывающегося списка или пару списков (страна, затем часовой пояс внутри страны) или управление выбором часовых поясов на основе карт.

  • Вы можете принять предположение и использовать это как значение по умолчанию, если ваш пользователь не изменит его.

Если вы спуститесь по этому маршруту, вам нужно будет использовать часовые пояса IANA/Olson, где находится Noda Time в игру. Вы можете получить к ним доступ из DateTimeZoneProviders.Tzdb.

Место размещения не имеет значения, если вы используете UTC. Это хорошая вещь.

Кроме того, если вы используете Noda Time, то вам, вероятно, следует использовать SystemClock.Instance.Now вместо DateTime.UtcNow.

См. также здесь и здесь.

Кроме того, альтернативным решением было бы просто передать время UTC браузеру и загрузить его в объект JavaScript Date. Браузер может преобразовать это в локальное время пользователя. Вы можете также использовать библиотеку, например moment.js, чтобы сделать это проще.


Update

Относительно вашего подхода к отображению кодов культуры в часовые пояса:

<appSettings>
    <add key="sv-se" value="W. Europe Standard Time" />
    <add key="ta-IN" value="India Standard Time" />
</appSettings>

Это не сработает по нескольким причинам:

  • Многие люди используют разные настройки культуры на своем компьютере, чем область, в которой они физически находятся. Например, я мог бы быть американским-американским докладчиком, живущим в Германии, мой код культуры, вероятно, все еще en-US, а не de-DE.

  • Код культуры, содержащий страну, используется для различения диалектов языка. Когда вы видите es-MX, это означает "испанский, как говорят в Мексике". Это не означает, что пользователь фактически находится в Мексике. Это просто означает, что пользователь говорит о диалекте испанского языка по сравнению с es-ES, что означает "испанский, как говорят в Испании".

  • Даже если страновая часть кода культуры может быть надежной, есть много стран, которые имеют несколько часовых поясов! Например, что бы вы поместили в свой список сопоставлений для en-US? Вы не можете просто предположить, что все мы находимся на восточном стандартном времени.

Теперь я объяснил, почему ваш нынешний подход не будет работать, я настоятельно рекомендую вам взять мой первоначальный совет. Очень просто:

  • Определите часовой пояс пользователя, желательно, спросив их, возможно, с некоторой помощью одной из утилит, с которыми я связан выше.

  • Вы сохраняете UTC, поэтому просто перейдите в этот часовой пояс для отображения.

    Использование временных зон Microsoft
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
    DateTime localDatetime = TimeZoneInfo.ConvertTimeFromUtc(yourUTCDateTime, tz);
    
    Использование временных зон IANA и времени Noda
    DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/Stockholm"];
    Instant theInstant = Instant.FromDateTimeUtc(yourUTCDateTime);
    LocalDateTime localDateTime = theInstant.InZone(tz);
    

Ответ 4

Мы столкнулись с аналогичной проблемой с приложением, над которым я работал в последнее время. Во время разработки каждый находился в одном и том же часовом поясе, и проблема не была замечена. И в любом случае было много устаревшего кода, который был бы болью для изменения, не говоря уже о преобразовании всей информации о дате, которая уже была в БД. Поэтому переход на DateTimeOffset не был вариантом. Но нам удалось добиться этого, переходя от времени сервера к пользовательскому времени на выходе и перехода от времени пользователя к серверному времени на пути. Важно также делать это с любыми сравнениями времени, которые были границей. Поэтому, если пользователь ожидал, что какое-то время истечет в полночь, тогда мы будем конвертировать это время в серверное время и делать все сравнения во время сервера. Это звучит как много работы, но гораздо меньше работы, чем преобразование всего приложения и БД для использования DateTimeOffsets.

Слух - это поток, который похож на несколько хороших решений для часового пояса.

Определите часовой пояс пользователя

Ответ 5

Если вы хотите показать согласованную историю даты и времени для пользователя, независимо от языкового стандарта, из которого они просматривают историю, выполните следующие действия:

  • Во время Save сохраняйте не только дату/время создания "UTC", но и обнаруженную локаль
  • Использовать сохраненный saved from locale для вычисления исходной даты/времени и испускать строку для отображения (т.е. не использовать текущую локаль пользователя, когда вы ее разворачиваете).

Если у вас нет возможности изменить ваше хранилище, возможно, вы можете изменить свою отправку, чтобы отправить "текущее клиентское время", сохранить его буквально (не конвертировать в UTC), а затем отобразить буквально (не конвертировать к обнаруженной культуре)

Но, как я говорю в своем комментарии по вашему вопросу, я не уверен, что правильно понял ваши требования.