programing

Date Time을 사용할 수 있는 이유MinValue가 UTC보다 이전 시간대로 일련화되지 않았습니까?

newsource 2023. 2. 15. 22:06

Date Time을 사용할 수 있는 이유MinValue가 UTC보다 이전 시간대로 일련화되지 않았습니까?

WCF REST 서비스에 문제가 있습니다.반환하려는 와이어 오브젝트에 특정 속성이 설정되어 있지 않아 Date Time이 됩니다.DateTime 유형의 속성에 대한 최소값입니다.서비스가 빈 문서(HTTP 상태 200 ?)를 반환합니다.?) JSON 시리얼라이제이션에 직접 콜을 걸려고 하면 다음과 같은 예외가 발생합니다.

시리얼화예외:Date Time 값보다 큰 Date Time 값.최대값 또는 DateTime보다 작습니다.UTC로 변환된 MinValue는 JSON으로 직렬화할 수 없습니다.

콘솔 앱에서 다음 코드를 실행하여 이를 재현할 수 있습니다.

DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(DateTime));
MemoryStream m = new MemoryStream();
DateTime dt = DateTime.MinValue;

// throws SerializationException in my timezone
ser.WriteObject(m, dt);
string json = Encoding.ASCII.GetString(m.GetBuffer());
Console.WriteLine(json);

왜 이런 행동일까요?제 시간대(GMT+1)와 관련이 있다고 생각합니다.Date Time(날짜 시간)으로.MinValue는 디폴트(DateTime)이므로 문제없이 시리얼화할 수 있을 것으로 예상됩니다.

REST 서비스를 어떻게 동작시킬지 조언해 주시겠어요?데이터 계약을 변경하고 싶지 않습니다.

가장 큰 문제는DateTime.MinValue가지다DateTimeKind.Unspecified다음과 같이 정의됩니다.

MinValue = new DateTime(0L, DateTimeKind.Unspecified);

그러나 이것은 실제 문제가 아닙니다.이 정의는 시리얼라이제이션 중 문제로 이어집니다.JSON Date Time 시리얼화는 다음 방법으로 이루어집니다.

System.Runtime.Serialization.Json.JsonWriterDelegator.WriteDateTime(DateTime value)

유감스럽게도 다음과 같이 정의됩니다.

...

if (value.Kind != DateTimeKind.Utc)
{
    long num = value.Ticks - TimeZone.CurrentTimeZone.GetUtcOffset(value).Ticks;
    if ((num > DateTime.MaxValue.Ticks) || (num < DateTime.MinValue.Ticks))
    {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString("JsonDateTimeOutOfRange"), new ArgumentOutOfRangeException("value")));
    }
}

...

그래서 그것은 고려되지 않는다.Unspecified라고 취급합니다.Local. 이 상황을 피하기 위해 자체 상수를 정의할 수 있습니다.

MinValueUtc = new DateTime(0L, DateTimeKind.Utc);

또는

MinValueUtc = DateTime.MinValue.ToUniversalTime();

물론 이상하게 보이긴 하지만 도움이 될 거야

임의의 DateTime 멤버에 이 추가 시도

[DataMember(IsRequired = false, EmitDefaultValue = false)]

이러한 에러의 대부분은 디폴트값이datetimeDateTime.MinValueJSON의 연재는 1970년부터입니다.

표준시가 GMT+1인 경우 UTC 값은DateTime.MinValue당신의 타임존에서 당신의 타임존은DateTime.MinValue.

이 생성자를 사용합니다.

public DataContractJsonSerializer(Type type, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, IDataContractSurrogate dataContractSurrogate, bool alwaysEmitTypeInformation)

코드 예:

DataContractJsonSerializer serializer = new DataContractJsonSerializer(o.GetType(), null, int.MaxValue, false, new DateTimeSurrogate(), false);

 public class DateTimeSurrogate : IDataContractSurrogate
    {

        #region IDataContractSurrogate 成员

        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            return null;
        }

        public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
        {
            return null;
        }

        public Type GetDataContractType(Type type)
        {
            return type;
        }

        public object GetDeserializedObject(object obj, Type targetType)
        {
                   return obj;
        }

        public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
        {

        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            if (obj.GetType() == typeof(DateTime))
            {
                DateTime dt = (DateTime)obj;
                if (dt == DateTime.MinValue)
                {
                    dt = DateTime.MinValue.ToUniversalTime();
                    return dt;
                }
                return dt;
            }
            if (obj == null)
            {
                return null;
            }
            var q = from p in obj.GetType().GetProperties()
                    where (p.PropertyType == typeof(DateTime)) && (DateTime)p.GetValue(obj, null) == DateTime.MinValue
                    select p;
            q.ToList().ForEach(p =>
            {
                p.SetValue(obj, DateTime.MinValue.ToUniversalTime(), null);
            });
            return obj;
        }

        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            return null;
        }

        public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
        {
            return typeDeclaration;
        }

        #endregion
    }

보다 우아한 방법은 Date Time 필드의 기본값을 내보내지 않도록 시리얼라이저에 지시하는 것이라고 생각합니다.이렇게 하면 전송 중에 바이트를 절약하고 값이 없는 필드를 직렬화할 때 바이트를 절약할 수 있습니다.예:

[DataContract]
public class Document {
    [DataMember] 
    public string Title { get; set; }
    [DataMember(IsRequired = false, EmitDefaultValue = false)] 
    public DateTime Modified { get; set; } 
}

또는 Nullables를 사용할 수 있습니다.예:

[DataContract]
public class Document {
    [DataMember] 
    public string Title { get; set; }
    [DataMember] 
    public DateTime? Modified { get; set; } 
}

이 모든 것은 프로젝트에서 사용할 수 있는 요건과 제한에 따라 달라집니다.데이터 유형만 변경할 수 없는 경우도 있습니다.이 경우에도 여전히 이 서비스를 이용할 수 있습니다.DataMember속성을 지정하고 데이터 유형을 그대로 유지합니다.

에서는 in음음음음음음이 있는 new Document() { Title = "Test Document" } 에 , 「JSON」이 .{"Title": "Test Document"}따라서 자바스크립트나 다른 클라이언트에서도 쉽게 처리할 수 있습니다.JSON(JSON)입니다.을 하고 돌아오게 됩니다.undefined. 으로 예상되는에 따라 입력된 언어에서는 유형(일반적으로 예상되는 동작)에 따라 해당 속성의 기본값이 지정됩니다.

library.GetDocument(id).success(function(raw){ 
    var document = JSON.Parse(raw);
    var date = document.date; // date will be *undefined*
    ...
}

OnSerializing Atribut과 리플렉션을 통해 시리얼라이제이션 중에 수정할 수 있습니다.

[OnSerializing]
public void OnSerializing(StreamingContext context)
{
  var properties = this.GetType().GetProperties();
  foreach (PropertyInfo property in properties)
  {
    if (property.PropertyType == typeof(DateTime) && property.GetValue(this).Equals(DateTime.MinValue))
    {
      property.SetValue(this, DateTime.MinValue.ToUniversalTime());
    }
  }
}

사용자 지정 DateTimeFormat을 지정하면(WriteDateTimeInDefaultFormat 메서드를 사용하여) 문제를 방지할 수 있습니다.

DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings();
settings.DateTimeFormat = new DateTimeFormat("yyyy-MM-ddThh:mm:ss.fffZ");
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(MyDataContract), settings);

DateTime 값이 실제로 UTC 내에 있는지 확인해야 하지만 DateTime과 함께 사용해도 안전합니다.최소값

사용하고 있는 DateTimeFormat 의 상세한 것에 대하여는, 이것을 참조해 주세요.

언급URL : https://stackoverflow.com/questions/4025851/why-can-datetime-minvalue-not-be-serialized-in-timezones-ahead-of-utc