ASP.NET 4.6 MVC 如何 return Json 结果包括具有正确时区的日期时间数据?

ASP.NET 4.6 MVC How to return a Json result including datetime data with correct timezone?

我希望return Json 结果包含从数据库查询的日期时间。在我的本地机器上测试没有任何问题,但是,在发布到生产服务器后,所有日期时间都提前 3 小时显示。我认为这是由于服务器位于另一个时区。

需要帮助解决这个问题。

数据库中的数据(MS SQL):

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

Controller.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 Kind但是变成了local而Json 序列化。所以只需要将DateTime Kind设置为UTC就可以解决,但是要注意前端解析DateTime也需要将其设为UTC否则会被认为是local默认情况下。

Controller.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")

问题是 ASP.NET 使用 custom Microsoft JSON date format,将 DateTime 值编码为 /Date(ticks)/,其中 ticks 表示自纪元 (UTC) 以来的毫秒数。
所以 1989 年 11 月 29 日,4:55:30 AM,在 UTC 中被编码为 /Date(628318530718)/(更多信息参见 here)。

示例:

  • Microsoft JSON 日期格式:/Date(1563464520158)/
  • ISO 8601 格式:2019-07-18T15:42:02.4592008Z

如果 DateTime 具有 Unspecified 类型,它将被假定为 Local 并且该值将转换为 UTC 以获得自纪元以来的刻度。

这种 json 格式 仍在 MVC 中使用,但不用于 Web API:这意味着当您处于 Controller 并使用 Json(...) 序列化结果,您将获得 non-ISO 格式,但如果您使用的是 ApiController,则默认序列化程序为 Json.NET,它支持 ISO 8601格式,不会转换 DateTime 值。

因此,要解决此问题 要么切换到 Web APIs,要么如果您想继续使用 MVC 控制器,您可以查看这些问题的答案:

  • Setting the default JSON serializer in ASP.NET MVC
  • Using JSON.NET as the default JSON serializer in ASP.NET MVC 3
  • How to use Json.NET for JSON modelbinding in an MVC5 project

...或者您可以在 json 序列化之前强制 DateTime Kind 为 Utc,但我不建议这样做。


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
]