Why can't DateTime.MinValue be serialized in time zones before UTC?

I am having problems with the WCF REST service. The posting object that I am trying to return has certain properties that are not set, which results in DateTime.MinValue for properties of the DateTime type. The service returns an empty document (with an HTTP status of 200 ???). When I try to call JSON serialization myself, the exception is:

SerializationException: DateTime values โ€‹โ€‹that are greater than DateTime.MaxValue or less than DateTime.MinValue when converted to UTC cannot be serialized to JSON.

This can be reproduced by running the following code in a console application:

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); 

Why is this behavior? I think this is due to my time zone (GMT + 1). Since DateTime.MinValue is by default (DateTime), I would expect this to be serializable without problems.

Any tips on how to get my REST service to behave? I do not want to change my DataContract.

+47
json c # datetime serialization wcf
Oct 26 2018-10-26
source share
6 answers

The main problem with DateTime.MinValue is DateTimeKind.Unspecified . It is defined as:

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

But this is not a real problem, this definition leads to a problem during serialization. JSON DateTime serialization is done through:

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

Unfortunately, it is defined as:

 ... 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"))); } } ... 

Therefore, it does not account for Unspecified and treats it as Local . To avoid this situation, you can define your own constant:

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

or

 MinValueUtc = DateTime.MinValue.ToUniversalTime(); 

It looks weird, but it helps.

+59
Oct 26 '10 at 17:27
source share

Try adding this to any DateTime member.

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

Most of these erros occur because the default datetime is DateTime.MinValue , which is year 1, and JSON serialization has been since 1970.

+13
Sep 06
source share

If your time zone is GMT + 1, then the UTC DateTime.MinValue in your time zone will be one hour less than DateTime.MinValue .

+6
Oct 26 2018-10-26
source share

using this constructor:

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

code example:

 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 } 
+5
May 22 '11 at 10:20
source share

I find that a more elegant way is to tell the serializer not to give a default value for DateTime fields. This will save some byte during transmission and some serialization processing for fields for which you have no value. Example:

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

or you can use nullables. Example:

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

It all depends on the requirements and limitations that may arise in your project. Sometimes you cannot just change data types. In this case, you can still use the DataMember and keep the data types intact.

In the above example, if you have new Document() { Title = "Test Document" } on the server side, when it is serialized in JSON, it will give you {"Title": "Test Document"} , so it will be easier with it handle in JavaScript or with any other client on the other side of the wire, In JavaScript, if you are JSON.Parse () and try to read it, you will return undefined . In typed languages, you will have a default value for this property depending on the type (usually this is the expected behavior).

 library.GetDocument(id).success(function(raw){ var document = JSON.Parse(raw); var date = document.date; // date will be *undefined* ... } 
+2
Nov 06 '15 at 8:07
source share

You can fix this during serialization through the OnSerializing attribute and some reflection:

 [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()); } } } 
0
Mar 24 '15 at 9:05
source share



All Articles