nodatime / nodatime.serialization

Serialization projects for Noda Time

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add Protobuf support for NodaTime.DateTimeZone

SeppPenner opened this issue · comments

If I understand http://protobuf-net.github.io/protobuf-net/nodatime.html correctly, the type NodaTime.DateTimeZone is not supported. It would be great if that support could be added.

I have already asked Marc Gravell in the issue under protobuf-net/protobuf-net#787 and shipped the feature request now to here.

I really would appreciate a short workaround via a surrogate or something as a short term fix if this is a more complex change.
Something like I did for DateTimeOffset maybe:

Class Startup.cs:

RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset?), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));

Class DateTimeOffsetSurrogate.cs:

using System;

using ProtoBuf;

[ProtoContract(Name = nameof(DateTimeOffset))]
public class DateTimeOffsetSurrogate
{
    [ProtoMember(1)]
    public long? Value { get; set; }

    public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate surrogate)
    {
        if (surrogate?.Value is null)
        {
            throw new ArgumentNullException(nameof(surrogate));
        }

        var dateTime = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
        dateTime = dateTime.ToLocalTime();
        return dateTime;
    }

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

        var dateTime = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
        dateTime = dateTime.ToLocalTime();
        return dateTime;
    }

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

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

All your code seems to be about DateTimeOffset, which is very different to a DateTimeZone. I'm honestly not clear about what you're proposing here - or how it relates to this repository, which has a separate integration library for Google.Protobuf, but doesn't use protobuf-net at all. I don't know anything about what surrogates are with respect to protobuf-net.

It wouldn't be hard to add conversion code for the Protobuf TimeZone message, although it's not clear to me what we'd expect it to do when converting from the protobuf representation, if the version were specified but not available.

I don't believe any NodaTime change is required. If you're looking for support in protobuf-net, then you'd want to look at that TimeZone proto, but be aware that it's not part of the "core" protobuf set of protos.

We could add the same support into NodaTime.Serialization.Protobuf, but I wouldn't expect it to be available any time soon.

All your code seems to be about DateTimeOffset, which is very different to a DateTimeZone.

Sure, that was just an example on how I'm using the surrogates right now.

or how it relates to this repository, which has a separate integration library for Google.Protobuf, but doesn't use protobuf-net at all. I don't know anything about what surrogates are with respect to protobuf-net.

Well, I'm not really familiar on how these implementations work together or which library is used where to be honest.

I don't believe any NodaTime change is required.

I agree with that.

If you're looking for support in protobuf-net, then you'd want to look at that TimeZone proto, but be aware that it's not part of the "core" protobuf set of protos.

Ok, I will have a look at this, thanks.

We could add the same support into NodaTime.Serialization.Protobuf, but I wouldn't expect it to be available any time soon.

If I understood it correctly, that would be my desired solution (For some future use case).

For anyone interested, we now use a surrogate workaround like this:

namespace Surrogates
{
    using NodaTime;

    using ProtoBuf;

    [ProtoContract(Name = nameof(DateTimeZone))]
    public class DateTimeZoneSurrogate
    {
        [ProtoMember(1)]
        public string? Value { get; set; }

        public static implicit operator DateTimeZone(DateTimeZoneSurrogate surrogate)
        {
            if (surrogate.Value == null)
            {
                return DateTimeZoneProviders.Tzdb["Europe/Berlin"];
            }

            var dateTimeZone = DateTimeZoneProviders.Tzdb[surrogate.Value];
            return dateTimeZone;
        }

        public static implicit operator DateTimeZoneSurrogate(DateTimeZone? source)
        {
            return new ()
            {
                Value = source?.Id
            };
        }
    }
}

with RuntimeTypeModel.Default.Add(typeof(DateTimeZone), false).SetSurrogate(typeof(DateTimeZoneSurrogate)); in the Startup.cs file.

Closing as I don't think we want to do anything in this repo.