Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] DateTimeOffset? protobuf serialization/deserialization in ASP Core #226

Open
odysseus1973 opened this issue Mar 24, 2017 · 6 comments
Assignees
Labels

Comments

@odysseus1973
Copy link

odysseus1973 commented Mar 24, 2017

Hi!

After some investigation and reading posts on SO I found two ways to serialize DateTimeOffset?

  1. Shim properties (working, but does not fit in my case)
  2. Surrogates. Not working in my case
    In my StartUp file I use
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset),false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset?),false).SetSurrogate(typeof(DateTimeOffsetSurrogate));

My surrogate class

    [ProtoContract]
    public class DateTimeOffsetSurrogate
    {
        [ProtoMember(1)]
        public string Date { get; set; }

        //public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset? value)
        //{
        //    return (value.HasValue) ? new DateTimeOffsetSurrogate { Date = value.Value.ToString("o") } : new DateTimeOffsetSurrogate { Date = null };
        //}

        //public static implicit operator DateTimeOffset? (DateTimeOffsetSurrogate value)
        //{
        //    DateTimeOffset? result = null;
        //    if (DateTimeOffset.TryParse(value.Date, out DateTimeOffset date))
        //    {
        //        result = date;
        //    }
        //    return result;
        //}

        public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
        {
            return (value != null) ? new DateTimeOffsetSurrogate { Date = value.ToString("o") } : new DateTimeOffsetSurrogate { Date = null };
        }

        public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
        {
            // DateTimeOffset? result = null;
            // if (
            DateTimeOffset.TryParse(value.Date, out DateTimeOffset date);
            //{
            //    result = date;
            //}
            return date;
        }
    }

My serialized class

    [ProtoContract]
    public class StocksDto
    {
        [JsonIgnore]
        public Guid? StoreId { get; set; }
        [Required]
        public DateTimeOffset? Date { get; set; }
        [JsonIgnore]
        public DateTime DateUtc { get { return Date.HasValue ? Date.Value.UtcDateTime : DateTime.MinValue; } }
        [JsonIgnore]
        public double TimeZoneOffset { get { return Date.HasValue ? Date.Value.Offset.TotalMinutes : 0; } }
        [ProtoMember(1)]
        [Required]
        public bool? Full { get; set; }
        [HasElements(ErrorMessage = "The field Stocks must contain non-empty array of elements")]
        [ProtoMember(2)]
        public IEnumerable<Stock> Stocks { get; set; }
    }

When I try serialize/deserialize StocksDto, Date property return no value (null). As I can see implicit operator in surrogate does not called.
How to use surrogate for DateTimeOffset? in my case?

@mgravell mgravell self-assigned this Jun 20, 2017
@rushfrisby
Copy link

Your surrogate should be

using System;
using System.Runtime.Serialization;

namespace ProtobufNetTest
{
    [DataContract(Name = nameof(DateTimeOffset))]
    public class DateTimeOffsetSurrogate
    {
        [DataMember(Order = 1)]
        public long? Value { get; set; }

        public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate surrogate)
        {
            return DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
        }

        public static implicit operator DateTimeOffset?(DateTimeOffsetSurrogate surrogate)
        {
            return surrogate != null ? DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value) : (DateTimeOffset?)null;
        }

        public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset source)
        {
            return new DateTimeOffsetSurrogate
            {
                Value = source.ToUnixTimeMilliseconds()
            };
        }

        public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset? source)
        {
            return new DateTimeOffsetSurrogate
            {
                Value = source?.ToUnixTimeMilliseconds()
            };
        }
    }
}

@odysseus1973
Copy link
Author

Thanks! I will try it, but why surrogate can't use string and how to register surrogate correctly? When I was debugging I did not see that the surrogate was called, so i think my problem with surrogate registration.

@rushfrisby
Copy link

The way you registered surrogates is correct however your class has a DateTimeOffset? property and your surrogate has no operators for converting between the two. Your surrogate only has operators for DateTimeOffset which is a different type.

Sure you can serialize the value as a string. I used long and the unix time value because the output is smaller than the string representation and faster than converting to/from a string.

@odysseus1973
Copy link
Author

odysseus1973 commented Aug 2, 2017

Thanks for answer! I specifically did not delete the commented lines in code block in my question, I tried it, but without success. How does converting to UnixTime support the time zone?

@rushfrisby
Copy link

By definition it takes into account the time zone since unix time starts at 1/1/1970 00:00:00 (UTC)

@paraboxx
Copy link

The purpose of DateTimeOffset is to preserve TimeZone information so it's possible to see at white Offset the timestamp was generated. By convertig to unixtime this information is lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants