azos icon indicating copy to clipboard operation
azos copied to clipboard

(Slim) Packed DateTimeOffset is equal to MinValue after deserialization

Open kalteherz opened this issue 5 years ago • 9 comments

static readonly SlimSerializer serializer = new SlimSerializer();

static byte[] Serialize(object obj)
{
    using (var ms = new MemoryStream())
    {
        serializer.Serialize(ms, obj);
        return ms.ToArray();
    }
}
static object Deserialize(byte[] data)
{
    using (var ms = new MemoryStream(data))
    {
        return serializer.Deserialize(ms);
    }
}

static void Main(string[] args)
{
    var dateTimeOffsetNow = DateTimeOffset.Now;
    var packedDateTimeOffset = new {Now = dateTimeOffsetNow};

    var serializedDateTimeOffset = Serialize(dateTimeOffsetNow);
    var serializedPackedDateTimeOffset = Serialize(packedDateTimeOffset);

    var deserializedDateTimeOffsetNow = Deserialize(serializedDateTimeOffset);
    var deserializedPackedDateTimeOffset = (dynamic) Deserialize(serializedPackedDateTimeOffset);
}

// dateTimeOffsetNow:                      6/27/2019 1:02:12 PM +03:00
// deserializedDateTimeOffsetNow:          6/27/2019 1:02:12 PM +03:00

// packedDateTimeOffset.Now:               6/27/2019 1:02:12 PM +03:00
// deserializedPackedDateTimeOffset.Now:   1/1/0001 12:00:00 AM +00:00

kalteherz avatar Jun 27 '19 11:06 kalteherz

@kalteherz can you repeat your test without using the "dynamic"? The test code you have is about serializing an anonymous class, so try it with explicit class (public class A{ public DateTimeOffset Now;}}

itadapter avatar Jun 27 '19 12:06 itadapter

@itadapter Same result.

dateTimeOffsetNow:                      6/27/2019 3:56:28 PM +03:00
deserializedDateTimeOffsetNow:          6/27/2019 3:56:28 PM +03:00

packedDateTimeOffset.Now:               6/27/2019 3:56:28 PM +03:00
deserializedPackedDateTimeOffset.Now:   1/1/0001 12:00:00 AM +00:00

kalteherz avatar Jun 27 '19 12:06 kalteherz

static readonly SlimSerializer serializer = new SlimSerializer();

static byte[] Serialize(object obj)
{
    using (var ms = new MemoryStream())
    {
        serializer.Serialize(ms, obj);
        return ms.ToArray();
    }
}
static object Deserialize(byte[] data)
{
    using (var ms = new MemoryStream(data))
    {
        return serializer.Deserialize(ms);
    }
}

public class A
{
    public DateTimeOffset Now;
}

static void Main(string[] args)
{
    var dateTimeOffsetNow = DateTimeOffset.Now;
    var packedDateTimeOffset = new A(){ Now = dateTimeOffsetNow };

    var serializedDateTimeOffset = Serialize(dateTimeOffsetNow);
    var serializedPackedDateTimeOffset = Serialize(packedDateTimeOffset);

    var deserializedDateTimeOffsetNow = (DateTimeOffset)Deserialize(serializedDateTimeOffset);
    var deserializedPackedDateTimeOffset = Deserialize(serializedPackedDateTimeOffset) as A;

    Console.WriteLine($"{nameof(dateTimeOffsetNow)}:\t\t\t{dateTimeOffsetNow.ToString()}");
    Console.WriteLine($"{nameof(deserializedDateTimeOffsetNow)}:\t\t{deserializedDateTimeOffsetNow.ToString()}");

    Console.WriteLine();

    Console.WriteLine($"{nameof(packedDateTimeOffset)}.{nameof(A.Now)}:\t\t{packedDateTimeOffset.Now.ToString()}");
    Console.WriteLine($"{nameof(deserializedPackedDateTimeOffset)}.{nameof(A.Now)}:\t{deserializedPackedDateTimeOffset.Now.ToString()}");
}

kalteherz avatar Jun 27 '19 13:06 kalteherz

@kalteherz looks like DateTimeOffset uses custom serialization mechanisms internally, so that is the most probable cause. Will require construction of additional unit test to figure this out. We can also serialize DateTimeOffset ourselves without MSFT ISerializable solution

itadapter avatar Jun 27 '19 13:06 itadapter

@itadapter If you solve this problem for the NFX too, it will be great.

kalteherz avatar Jun 27 '19 13:06 kalteherz

@kalteherz NFX is legacy code, you should use .Net Standard Azos instead for current development

itadapter avatar Jun 27 '19 13:06 itadapter

I cloned NFX and similar lines of code solved the problem, at first glance:

public override void Write(DateTimeOffset value)
{
  Write(value.DateTime);
  Write(value.Offset);
}


    public override void Write(DateTimeOffset? value)
    {
      if (value.HasValue)
      {
        Write(true);
        Write(value.Value);
        return;
      }
      Write(false);
    }

public override DateTimeOffset ReadDateTimeOffset()
{
  var dateTime = ReadDateTime();
  var offset = ReadTimeSpan();
  return new DateTimeOffset(dateTime, offset);
}

    public override DateTimeOffset? ReadNullableDateTimeOffset()
    {
      var has = ReadBool();
      if (has) return ReadDateTimeOffset();
      return null;
    }

Also added these methods to interfaces and types to Slim.TypeRegistry.

It works for me.

kalteherz avatar Jun 27 '19 21:06 kalteherz

@itadapter The same problem with IntPtr

kalteherz avatar Sep 04 '19 10:09 kalteherz

Just proposed a PR regarding this issue: https://github.com/azist/azos/pull/611 Thanks @kalteherz for the pointer.

davidbrun avatar Nov 17 '21 08:11 davidbrun