Десериализация JavaScriptSerializer с помощью DateTime или DateTimeOffset

VB2012, скомпилированный с .NET4.5: Последние пару дней это не давало мне покоя. У меня есть данные json, которые я анализирую с помощью класса JavaScriptSerializer.Deserialize. Одно из полей представлено следующим образом:

depart 2017-09-22T00:45:00-07:00

Какое местное время отправления (это в Лос-Анджелесе).

В моем классе-оболочке я написал

Public Class JsonLeg
    Public depart As DateTime
    Public blah...many more fields
End Class

Я принимаю данные json и использую JavaScriptSerializer.Deserialize для их анализа. После этого я отлаживаю вывод, и он выглядит так

depart                 [Kind=Local] 2017-09-22T02:45:00
depart.ToLocalTime     [Kind=Local] 2017-09-22T02:45:00
depart.ToUniversalTime [Kind=Utc  ] 2017-09-22T07:45:00

Все выглядит хорошо, но ToLocalTime просто преобразует его в часовой пояс моего компьютера, который находится в Далласе. Что мне нужно, так это местное время в Лос-Анджелесе 2017-09-22T00:45:00 для сравнения со списком дат, у которых нет вида. Как мне вытащить местное время исходного местоположения, а не местное время моего компьютера?

Я протестировал использование DateTimeOffset в качестве переменной, но боюсь, что не понимаю, что делает десериализатор, когда он анализирует данные json.

Отладка для этого выглядит так:

depart.DateTime      [Kind=Unspecified] 2017-09-22T00:45:00
depart.LocalDateTime [Kind=Local      ] 2017-09-22T02:45:00
depart.UtcDateTime   [Kind=Utc        ] 2017-09-22T07:45:00

В этом случае DateTimeOffset.DateTime дает мне то, что мне нужно. Но почему? Я пытаюсь понять, в чем различия и как каждый из них анализируется десериализатором.


person sinDizzy    schedule 21.09.2017    source источник


Ответы (1)


Ваши данные представлены в расширенном формате ISO 8601 со смещением. Это также рассматривается в RFC3339.

Когда вы десериализуете строку в этом формате в DateTime с помощью JavaScriptSerializer, делается довольно серьезное предположение, что она была сгенерирована из другого DateTime с Kind=Local. Поскольку смещения могут совпадать или не совпадать между вашим компьютером и тем, который сгенерировал данные, он сначала применяет предоставленное смещение для перехода к UTC, а затем преобразуется обратно в ваш местный часовой пояс. Вероятно, это не то поведение, которое вам нужно, и нет хорошего способа заставить JavaScriptSerializer вести себя по-другому. Разбор в DateTimeOffset имеет гораздо больше смысла. Полученные части даты, времени и смещения точно совпадают с тем, что было предоставлено.

Когда у вас есть DateTimeOffset, вызов .DateTime возвращает вам только часть даты и времени с результирующим типом Unspecified. Это стандартное поведение для всех DateTimeOffset экземпляров, а не только для тех, которые проходят синтаксический анализ. Причина в том, что смещение может быть любым, и сопоставление его либо с Local, либо с Utc будет ошибкой.

Точно так же .LocalDateTime преобразуется в местное время с учетом смещения (аналогично тому, как если бы вы анализировали непосредственно DateTime). .UtcDateTime учитывает смещение и преобразуется в UTC, аналогично тому, как если бы вы вызывали .ToUniversalTime() в версии DateTime.

Кроме того, вам следует рассмотреть возможность использования Json.NET вместо JavaScriptSerializer. Большинство участников сообщества .NET, включая Microsoft, теперь используют эту библиотеку для сериализации JSON. Он гораздо более гибкий и имеет тенденцию делать правильные вещи из коробки, когда дело доходит до дат и времени.

person Matt Johnson-Pint    schedule 12.10.2017
comment
Спасибо за объяснение. Теперь это имеет смысл для меня. Я хотел легкий способ преобразования данных, и хотя, если бы у MS уже был парсер, я бы использовал его. Я думаю, что рассмотрю возможность перехода на json.net, если мой синтаксический анализ станет более сложным. Огромное спасибо. - person sinDizzy; 13.10.2017