DataContractJsonSerializer DateTime неявное преобразование часового пояса - PullRequest
7 голосов
/ 15 апреля 2011

У меня есть дата в базе данных, и я извлекаю ее из базы данных с помощью Entity Framework, затем я передаю данные через JSON API через DataContractJsonSerializer.

Время в поле даты и времени, по-видимому, было скорректировано в соответствии с местным часовым поясом сервера во время обработки в DataContractJsonSerializer. Время, выраженное в эпоху, на 1 час опережает ожидаемое время. Тип DateTime - UTC, но ранее он не был указан, и у меня была та же проблема.

В моем приложении я хочу явно конвертировать часовые пояса на стороне клиента, а не на сервере, поскольку это имеет больше смысла. Я удивлен этой неявной функциональностью, так как мои значения datetime должны быть простыми значениями, как целое число.

спасибо

1 Ответ

5 голосов
/ 19 апреля 2011

DataContractJsonSerializer выведет часть часового пояса (+ zzzz), если ваш DateTime.Kind равен Local ИЛИ Unspecified.Это поведение отличается от XmlSerializer, который выводит только часть часового пояса, если Kind равен Unspecified.

Если любопытно, проверьте источник для JsonWriterDelegator, который содержит следующий метод:

 internal override void WriteDateTime(DateTime value) 
    {
        // ToUniversalTime() truncates dates to DateTime.MaxValue or DateTime.MinValue instead of throwing 
        // This will break round-tripping of these dates (see bug 9690 in CSD Developer Framework)
        if (value.Kind != DateTimeKind.Utc)
        {
            long tickCount = value.Ticks - TimeZone.CurrentTimeZone.GetUtcOffset(value).Ticks; 
            if ((tickCount > DateTime.MaxValue.Ticks) || (tickCount < DateTime.MinValue.Ticks))
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 
                    XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.JsonDateTimeOutOfRange), new ArgumentOutOfRangeException("value")));
            } 
        }

        writer.WriteString(JsonGlobals.DateTimeStartGuardReader);
        writer.WriteValue((value.ToUniversalTime().Ticks - JsonGlobals.unixEpochTicks) / 10000); 

        switch (value.Kind) 
        { 
            case DateTimeKind.Unspecified:
            case DateTimeKind.Local: 
                // +"zzzz";
                TimeSpan ts = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime());
                if (ts.Ticks < 0)
                { 
                    writer.WriteString("-");
                } 
                else 
                {
                    writer.WriteString("+"); 
                }
                int hours = Math.Abs(ts.Hours);
                writer.WriteString((hours < 10) ? "0" + hours : hours.ToString(CultureInfo.InvariantCulture));
                int minutes = Math.Abs(ts.Minutes); 
                writer.WriteString((minutes < 10) ? "0" + minutes : minutes.ToString(CultureInfo.InvariantCulture));
                break; 
            case DateTimeKind.Utc: 
                break;
        } 
        writer.WriteString(JsonGlobals.DateTimeEndGuardReader);
    }

I 'мы выполнили следующий тест на моей машине

var jsonSerializer = new DataContractJsonSerializer(typeof(DateTime));
var date = DateTime.UtcNow;
        Console.WriteLine("original date = " + date.ToString("s"));
        using (var stream = new MemoryStream())
        {
            jsonSerializer.WriteObject(stream, date);

            stream.Position = 0;
            var deserializedDate = (DateTime)jsonSerializer.ReadObject(stream);
            Console.WriteLine("deserialized date = " + deserializedDate.ToString("s"));

        }

, который выдает ожидаемый результат:

original date = 2011-04-19T10:24:39
deserialized date = 2011-04-19T10:24:39

Таким образом, в какой-то момент ваша Дата должна быть Не указано или Локальная.После извлечения его из БД преобразуйте вид из Unspecified в Utc, вызвав

 entity.Date = DateTime.SpecifyKind(entity.Date, DateTimeKind.Utc);

и не забудьте присвоить возвращаемое значение SpecifyKind обратно в ваш объект, как у меня

...