Thank you for reaching out to us about the issue with
DateTimeOffset and the .NET/C# driver.
This is expected behaviour from
BsonSerializer. When an unregistered type is looked up via
BsonSerializer.LookupSerializer<T>() we walk the list of registered
IBsonSerializationProvider instances looking for one that implements a serializer for the requested type.
DateTimeOffsetSerializer is returned from
PrimitiveSerializationProvider. So the first time that you lookup a serializer for
DateTimeOffset, we don’t find one registered and register the one from
If you want to override this behaviour and provide your own serializer for a type, you can register one at startup either via
BsonSerializer.RegisterSerializer<T>(IBsonSerializer<T> serializer) or you can include it in a custom
IBsonSerializationProvider registered via
BsonSerializer.RegisterSerializationProvider(IBsonSerializationProvider provider). Note that
IBsonSerializationProvider instances are consulted in reverse order of registration - e.g. last one first - so that users are able to customize serialization behaviour as needed.
Hopefully that explains why #1 is required. In theory we could provide a way to determine if any
IBsonSerializationProvider has a serializer for a particular type by adding additional query methods to the interface, but this would be a breaking change. As well there are catch-all
IBsonSerializationProvider implementations that allow us to return a generic serializer if a custom one hasn’t been provided. So in practice it is very uncommon to not find any serializer for a particular type. The intent of the design is to override/customize serialization during application startup.
Regarding question #2, we don’t provide a mechanism to swap serializers as this can lead to race conditions where different serializers could be used to serializer/deserialize instances. The intent of the design is to register all serializers or serialization providers during application startup and that the mapping of type to serializer instance remains stable throughout the application lifetime.
Moving onto question #3, yes, you can implement your own
IBsonSerializationProvider to override the built-in
DateTimeOffsetSerializer. Since you will register the provider last during your application initialization, your custom provider will be used to find a serializer for
Switching gears a bit for question #4, this is challenging due to the representation mismatch between
BsonDateTime. I agree with you that .NET should have used
DateTimeOffset for its date-time representation from the start, but Microsoft didn’t.
DateTime originally stored the date and time without any timezone information.
DateTimeKind was retrofitted later to differentiate between local and UTC. But
DateTime is used extensively in .NET code for better or worse.
DateTimeOffset includes timezone information, which removes a lot of ambiguity and is more nuanced than the
DateTimeKind fix. So I understand your desire to use
DateTimeOffset in your applications.
Now how does this relate to MongoDB? MongoDB stores date-time instances as
BsonDateTime, which is a 64-bit integer representing the number of milliseconds since the Unix epoch (January 1, 1970) in UTC. (See the BSON spec for details.) There is no timezone information as all date-times are converted to UTC for storage. Although you can use
BsonDateTime in your applications, it is more natural to use
DateTime. If the
DateTime.Kind is local, then we convert to UTC based on the current timezone of the client before storing it to the database. If it is UTC already, we do not perform the conversion.
How does this relate to
DateTimeOffset? If we were to store
BsonDateTime, we would lose the timezone information as we store
BsonDateTime as a simple int64 in UTC. The
DateTimeOffsetSerializer serializes values as an array (default), document, or string, which allows us to store the timezone information along with the date-time value itself. You could implement your own custom serializer for
DateTimeOffset values and make whatever assumptions about the timezone is appropriate for your application and thus only store the
DateTime portion of
DateTimeOffset, but we cannot make those simplifying assumptions in a generic way that would work for all users of our driver.
Hopefully this provides some clarity on why serialization behaves the way it does and why we cannot automatically serialize
DateTimeOffset values into simple
BsonDateTime values in the database. Please let us know if you have any additional questions.