ASP.NET 4.6 MVC Как вернуть результат Json, включая данные даты и времени с правильным часовым поясом?

Я хочу вернуть результат Json, содержащий дату и время, запрошенные из базы данных. Тестирование на моем локальном компьютере прошло без проблем, однако после публикации на рабочем сервере все даты и время отображаются на 3 часа вперед. Я предполагаю, что это связано с тем, что сервер находится в другом часовом поясе.

Нужна помощь в решении этой проблемы.

Данные в базе данных (MS SQL):

StartDt: 2019-07-02 04:00:00.000

Контроллер.cs:

 [HttpGet]
        public ActionResult GetAll()
        {
            CalendarModel calendarModel = new CalendarModel();

            var calendarEvents = from cal in db.Calendar
                                 orderby cal.created descending
                                 select cal;


            return Json(calendarEvents, JsonRequestBehavior.AllowGet);
        }

Результат Json получен с моего компьютера:

[
    {
        //some data
        "startDt": "/Date(1562054400000)/",
        //some data
    },

Приведенная выше дата и время анализируется как «2019-07-02T04:00:00.000-04:00», что правильно.

Результат Json получен с рабочего сервера (запрошен из той же базы данных):

[
    {
        //some data
        "startDt": "/Date(1562065200000)/",
        //some data
    },

Это дата и время «2019-07-02T07:00:00.000-04:00», что неверно.

--------Обновите мое решение-------

Спасибо, ответ @TommasoBertoni вдохновил меня на то, что основная причина этой проблемы связана с типом Unspecified DateTime по умолчанию, но во время сериализации Json становится local. Поэтому просто нужно установить тип DateTime на UTC, чтобы решить эту проблему, но имейте в виду, что синтаксический анализ DateTime во внешнем интерфейсе также должен принимать его как UTC, иначе он будет считаться local по умолчанию.

Контроллер.cs:

 [HttpGet]
public ActionResult GetAll()
        {
            CalendarModel calendarModel = new CalendarModel();

            var calendarEvents = from cal in db.Calendar
                                 orderby cal.created descending
                                 select cal;
            //Add this
            foreach (var item in calendarEvents)
            {
                item.startDt = DateTime.SpecifyKind(item.startDt, DateTimeKind.Utc);
            }

            return Json(calendarEvents, JsonRequestBehavior.AllowGet);
        }

.js (с использованием библиотеки moment.js)

//parse it as utc
  moment.utc(startDt).format("YYYY-MM-DDTHH:mm:ss")

person Lei.L    schedule 18.07.2019    source источник
comment
Вы проверили часовой пояс на производстве? Это несоответствие часового пояса   -  person tech-y    schedule 18.07.2019
comment
Что это за база данных и как она настроена? Что именно вы хотите, чтобы значение в базе данных означало?   -  person Jon Skeet    schedule 18.07.2019
comment
Как вы превращаете Date в объект даты и откуда вы получаете свою дату (2019-07-02T07:00:00.000-04:00) на стороне клиента? Это похоже на представление даты для часового пояса UTC-4, поэтому ваша оценка 2019-07-02T04:00:00.000-04:00 верна кажется неверной.   -  person Liam    schedule 18.07.2019
comment
Даты, вероятно, не сохраняются как UTC.   -  person Tommaso Bertoni    schedule 18.07.2019
comment
@ tech-y Я не могу настроить базу данных. Поэтому я думаю, что единственный способ сделать что-то на сервере.   -  person Lei.L    schedule 18.07.2019
comment
@JonSkeet дата и время в базе данных 4 утра. Я хочу 4 утра. Вот и все. Но на рабочем сервере он возвращает мне 7 утра.   -  person Lei.L    schedule 18.07.2019
comment
Я хочу 4 утра - при возврате даты/времени в формате JSON, который вы используете (/Date(...)/ нет такой вещи, как просто 4 утра. Он представляет собой момент во времени. Какой момент вас интересует? Этот момент времени может быть 4 утра в некоторых местах и ​​7 утра в других местах.Вы действительно пытаетесь представить момент времени или местную дату/время, которые будут означать разные моменты времени в разных часовых поясах?   -  person Jon Skeet    schedule 18.07.2019
comment
@TommasoBertoni, ты имеешь в виду, что я должен что-то сделать в столбце даты и времени?   -  person Lei.L    schedule 18.07.2019
comment
(И опять же, что это за сервер базы данных и как он настроен? Хотя вы не можете изменить конфигурацию, все же важно ее понять.)   -  person Jon Skeet    schedule 18.07.2019
comment
@JonSkeet Это MS SQL. Тип данных — datetime, допустим null.   -  person Lei.L    schedule 18.07.2019
comment
@ Lei.L, можете ли вы проверить Kind дат, полученных из базы данных? (date.Kind) это может быть Unspecified, Local или Utc. Это должно помочь нам понять, происходит ли преобразование во время выборки значений или во время сериализации json.   -  person Tommaso Bertoni    schedule 18.07.2019
comment
Хорошо, стоило бы включить эту информацию в вопрос. Теперь, что точно вы собираетесь представлять в базе данных? Должно ли это быть мгновением во времени или плавающим 2019-07-02 04:00:00.000, где бы я ни находился? Это совсем разные ценности.   -  person Jon Skeet    schedule 18.07.2019
comment
@TommasoBertoni Это Unspecified   -  person Lei.L    schedule 18.07.2019
comment
Поэтому это может быть сериализация json, которая предполагает, что дата указана по местному времени, поскольку она не универсальна.   -  person Tommaso Bertoni    schedule 18.07.2019
comment
@TommasoBertoni Я согласен с вашим мнением, так же, как и то, что я исследовал до того, как я думаю. Но я заблудился, пытаясь. Не могли бы вы написать свой ответ ниже?   -  person Lei.L    schedule 18.07.2019
comment
@Lei.L - это результат json, который вы показали нам, возвращенный непосредственно из API, или он был впервые получен и проанализирован интерфейсом javascript?   -  person Tommaso Bertoni    schedule 18.07.2019
comment
@TommasoBertoni Получено из API напрямую через Postman   -  person Lei.L    schedule 18.07.2019
comment
@Lei.L Lei.L, вы используете сериализатор json по умолчанию? есть ли пользовательская конфигурация при запуске?   -  person Tommaso Bertoni    schedule 18.07.2019
comment
@TommasoBertoni Это значение по умолчанию. Я ничего не делал с сериализатором json.   -  person Lei.L    schedule 18.07.2019


Ответы (1)


Проблема в том, что ASP.NET использует пользовательский Microsoft JSON формат даты, который кодирует значения DateTime как /Date(ticks)/, где тики представляют миллисекунды с начала эпохи (UTC).
Таким образом, 29 ноября 1989 г., 4:55:30 утра, в UTC кодируется как /Date(628318530718)/ (см. здесь для получения дополнительной информации).

Пример:

  • Формат даты Microsoft JSON: /Date(1563464520158)/
  • Формат ISO 8601: 2019-07-18T15:42:02.4592008Z

Если DateTime имеет тип Unspecified, он будет считаться Local, и значение будет преобразовано в UTC, чтобы получить тики с эпохи.

Этот формат json по-прежнему используется в MVC, но не в Web API: это означает, что когда вы находитесь в Controller и сериализуете результат с помощью Json(...), вы получите формат, отличный от ISO, но если вы ApiController сериализатором по умолчанию является Json.NET, который поддерживает формат ISO 8601 и не будет преобразовывать значение DateTime.

Таким образом, чтобы исправить это поведение, либо вы переключаетесь на веб-API, либо хотите продолжать использовать контроллер MVC, вы можете просмотреть ответы на следующие вопросы:

...или вы можете принудительно установить тип DateTime в формате Utc прямо перед сериализацией json, но я бы не рекомендовал этого делать.


class TestRepo
{
    public IEnumerable<DateTime> GetDates()
    {
        var now = DateTime.Now;

        // Use DateTime instances with different Kind
        // showing that it doesn't impact the serialization format.
        var utc = DateTime.SpecifyKind(new DateTime(now.Ticks), DateTimeKind.Utc);
        var local = DateTime.SpecifyKind(new DateTime(now.Ticks), DateTimeKind.Local);
        var unspecified = DateTime.SpecifyKind(new DateTime(now.Ticks), DateTimeKind.Unspecified);

        return new DateTime[] { utc, local, unspecified };
    }
}
// MVC controller
public class MVCValuesController : Controller
{
    public ActionResult Index()
    {
        IEnumerable<DateTime> dates = new TestRepo().GetDates();
        return Json(dates, JsonRequestBehavior.AllowGet);
    }
}

// json result:
[
    "/Date(1563465361835)/", // <-- Utc
    "/Date(1563458161835)/", // <-- Local
    "/Date(1563458161835)/"  // <-- Unspecified
]
// Web API controller
public class ValuesController : ApiController
{
    public IEnumerable<DateTime> Get()
    {
        IEnumerable<DateTime> dates = new TestRepo().GetDates();
        return dates;
    }
}

// json result:
[
    "2019-07-18T15:56:03.6401158Z",      // <-- Utc
    "2019-07-18T15:56:03.6401158+02:00", // <-- Local
    "2019-07-18T15:56:03.6401158"        // <-- Unspecified
]
person Tommaso Bertoni    schedule 18.07.2019
comment
Ценю ваше прекрасное объяснение, которое вдохновило меня на решение моей проблемы! - person Lei.L; 19.07.2019