[CSHARP-2440] Add DateTime representation to DateTimeOffsetSerializer Created: 24/Nov/18  Updated: 28/Oct/23  Resolved: 28/Jan/22

Status: Closed
Project: C# Driver
Component/s: Serialization
Affects Version/s: 2.7.2
Fix Version/s: 2.15.0

Type: New Feature Priority: Trivial - P5
Reporter: Lukas Vosyka Assignee: Robert Stam
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Backwards Compatibility: Fully Compatible

 Description   

Hi all,

I was wondering why the decision was made to not allow a BsonType.DateTime to be (de)serialized as DateTimeOffset? I know you loose the Offset, etc. but I think this is not a big deal. Converting DateTimeOffset to and from DateTimes is generally a valid operation, so why not allowing it in MongoDB C#?

Just curios, because it could help, when someone has to handle with DateTimeOffset in models but wants to be persisted as BsonType.DateTime.



 Comments   
Comment by James Kovacs [ 31/Jan/22 ]

Reverted because this shouldn't have been cherrypicked into a patch release. Will be released in 2.15.0.

Comment by Githook User [ 31/Jan/22 ]

Author:

{'name': 'James Kovacs', 'email': 'jkovacs@post.harvard.edu', 'username': 'JamesKovacs'}

Message: Revert "CSHARP-2440: Add DateTime representation to DateTimeOffsetSerializer."

This reverts commit 29b312a734f7ccea8174a268128b1f6fd23c4ec1.
Branch: v2.14.x
https://github.com/mongodb/mongo-csharp-driver/commit/b5f1a2a8e5ee90303cf68b75937954dc13b294af

Comment by Githook User [ 31/Jan/22 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-2440: Add DateTime representation to DateTimeOffsetSerializer.
Branch: v2.14.x
https://github.com/mongodb/mongo-csharp-driver/commit/29b312a734f7ccea8174a268128b1f6fd23c4ec1

Comment by Githook User [ 28/Jan/22 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-2440: Add DateTime representation to DateTimeOffsetSerializer.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/3ea83b5d9ad88590686f85616507ed24c4e36fc9

Comment by Lukas Vosyka [ 03/Dec/18 ]

Hi Ian,

thanks for the response. Ok, I understand that. You could leave the default as is, just adding more serializer by default that cover such scenarios? 

By definition MongoDB saves BsonType.DateTime as being UTC. Hence anyone who needs to save a DateTimeOffset into MongoDB should already be aware of the fact, that it will and should be stored as a conversion to UTC datetime. Deserialization is then natural to result also into a DateTimeOffset with zero offset. That is my reasoning of why it should not be a big deal.

As you suggested, I already created an own serializer. I will leave the code here for reference, if someone is interested:

public class DateTimeOffsetSupportingBsonDateTimeSerializer : StructSerializerBase<DateTimeOffset>, 
                 IRepresentationConfigurable<DateTimeOffsetSupportingBsonDateTimeSerializer>
{
    private BsonType _representation;
    private string StringSerializationFormat = "YYYY-MM-ddTHH:mm:ss.FFFFFFK";
 
    public DateTimeOffsetSupportingBsonDateTimeSerializer() : this(BsonType.DateTime)
    {
    }
 
    public DateTimeOffsetSupportingBsonDateTimeSerializer(BsonType representation)
    {
        switch (representation)
        {
            case BsonType.String:
            case BsonType.DateTime:
                break;
            default:
                throw new ArgumentException(string.Format("{0} is 
                not a valid representation for {1}", representation, this.GetType().Name));
        }
 
        _representation = representation;
    }
 
    public BsonType Representation => _representation;
 
    public override DateTimeOffset Deserialize(BsonDeserializationContext context, 
                                               BsonDeserializationArgs args)
    {
        var bsonReader = context.Reader;
        long ticks;
        TimeSpan offset;
 
        BsonType bsonType = bsonReader.GetCurrentBsonType();
        switch (bsonType)
        {
            case BsonType.String:
                var stringValue = bsonReader.ReadString();
                return DateTimeOffset.Parse(stringValue, DateTimeFormatInfo.InvariantInfo);
 
            case BsonType.DateTime:
                var dateTimeValue = bsonReader.ReadDateTime();
                return DateTimeOffset.FromUnixTimeMilliseconds(dateTimeValue);
 
            default:
                throw CreateCannotDeserializeFromBsonTypeException(bsonType);
        }
    }
 
    public override void Serialize
       (BsonSerializationContext context, BsonSerializationArgs args, DateTimeOffset value)
    {
        var bsonWriter = context.Writer;
 
        switch (_representation)
        {
            case BsonType.String:
                bsonWriter.WriteString(value.ToString
                      (StringSerializationFormat, DateTimeFormatInfo.InvariantInfo));
                break;
 
            case BsonType.DateTime:
                bsonWriter.WriteDateTime(value.ToUnixTimeMilliseconds());
                break;
 
            default:
                var message = string.Format("'{0}' is not a valid 
                              DateTimeOffset representation.", _representation);
                throw new BsonSerializationException(message);
        }
    }
 
    public DateTimeOffsetSupportingBsonDateTimeSerializer WithRepresentation(BsonType representation)
    {
        if(representation == _representation)
        {
            return this;
        }
        return new DateTimeOffsetSupportingBsonDateTimeSerializer(representation);
    }
 
    IBsonSerializer IRepresentationConfigurable.WithRepresentation(BsonType representation)
    {
        return WithRepresentation(representation);
    }
 
    protected Exception CreateCannotDeserializeFromBsonTypeException(BsonType bsonType)
    {
        var message = string.Format("Cannot deserialize a '{0}' from BsonType '{1}'.",
            BsonUtils.GetFriendlyTypeName(ValueType),
            bsonType);
        return new FormatException(message);
    }
}

Comment by Ian Whalen (Inactive) [ 03/Dec/18 ]

Hey lukster - Although losing the offset after serializing might not be a big deal to all users, we believe that there are definitely users for whom this would be extremely surprising, and so we've chosen an API that tries to minimize any such surprises.

We will consider for the future adding non-default functionality to do this for those who really know what they're doing.

In the meantime you could write and register your own serializer.

Generated at Wed Feb 07 21:42:33 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.