Skip to content

Commit 3ea83b5

Browse files
committed
CSHARP-2440: Add DateTime representation to DateTimeOffsetSerializer.
1 parent 292a56c commit 3ea83b5

File tree

2 files changed

+153
-5
lines changed

2 files changed

+153
-5
lines changed

src/MongoDB.Bson/Serialization/Serializers/DateTimeOffsetSerializer.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414
*/
1515

1616
using System;
17-
using System.IO;
1817
using MongoDB.Bson.IO;
19-
using MongoDB.Bson.Serialization.Attributes;
20-
using MongoDB.Bson.Serialization.Options;
2118

2219
namespace MongoDB.Bson.Serialization.Serializers
2320
{
@@ -58,6 +55,7 @@ public DateTimeOffsetSerializer(BsonType representation)
5855
switch (representation)
5956
{
6057
case BsonType.Array:
58+
case BsonType.DateTime:
6159
case BsonType.Document:
6260
case BsonType.String:
6361
break;
@@ -112,6 +110,10 @@ public override DateTimeOffset Deserialize(BsonDeserializationContext context, B
112110
bsonReader.ReadEndArray();
113111
return new DateTimeOffset(ticks, offset);
114112

113+
case BsonType.DateTime:
114+
var millisecondsSinceEpoch = bsonReader.ReadDateTime();
115+
return DateTimeOffset.FromUnixTimeMilliseconds(millisecondsSinceEpoch);
116+
115117
case BsonType.Document:
116118
ticks = 0;
117119
offset = TimeSpan.Zero;
@@ -144,8 +146,6 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati
144146
{
145147
var bsonWriter = context.Writer;
146148

147-
// note: the DateTime portion cannot be serialized as a BsonType.DateTime because it is NOT in UTC
148-
149149
switch (_representation)
150150
{
151151
case BsonType.Array:
@@ -155,6 +155,11 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati
155155
bsonWriter.WriteEndArray();
156156
break;
157157

158+
case BsonType.DateTime:
159+
var millisecondsSinceEpoch = value.ToUnixTimeMilliseconds();
160+
bsonWriter.WriteDateTime(millisecondsSinceEpoch);
161+
break;
162+
158163
case BsonType.Document:
159164
bsonWriter.WriteStartDocument();
160165
bsonWriter.WriteDateTime("DateTime", BsonUtils.ToMillisecondsSinceEpoch(value.UtcDateTime));
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.IO;
18+
using FluentAssertions;
19+
using MongoDB.Bson.IO;
20+
using MongoDB.Bson.Serialization;
21+
using MongoDB.Bson.Serialization.Serializers;
22+
using MongoDB.Bson.TestHelpers.XunitExtensions;
23+
using Xunit;
24+
25+
namespace MongoDB.Bson.Tests.Serialization.Serializers
26+
{
27+
public class DateTimeOffsetSerializerTests
28+
{
29+
[Fact]
30+
public void constructor_with_no_arguments_should_return_expected_result()
31+
{
32+
var subject = new DateTimeOffsetSerializer();
33+
34+
subject.Representation.Should().Be(BsonType.Array);
35+
}
36+
37+
[Theory]
38+
[ParameterAttributeData]
39+
public void constructor_with_representation_should_return_expected_result(
40+
[Values(BsonType.Array, BsonType.DateTime, BsonType.Document, BsonType.String)] BsonType representation)
41+
{
42+
var subject = new DateTimeOffsetSerializer(representation);
43+
44+
subject.Representation.Should().Be(representation);
45+
}
46+
47+
[Fact]
48+
public void constructor_with_representation_should_throw_when_representation_is_invalid()
49+
{
50+
var exception = Record.Exception(() => new DateTimeOffsetSerializer(BsonType.Null));
51+
52+
exception.Should().BeOfType<ArgumentException>();
53+
}
54+
55+
[Theory]
56+
[InlineData("{ x : [{ $numberLong : '0' }, 0] }", "0001-01-01T00:00:00+00:00")]
57+
[InlineData("{ x : [{ $numberLong : '621355968000000000' }, 0] }", "1970-01-01T00:00:00+00:00")]
58+
[InlineData("{ x : [{ $numberLong : '621355968000000000' }, 60] }", "1970-01-01T00:00:00+01:00")]
59+
[InlineData("{ x : [{ $numberLong : '621355968000000000' }, -60] }", "1970-01-01T00:00:00-01:00")]
60+
[InlineData("{ x : { $date : { $numberLong : '0' } } }", "1970-01-01T00:00:00Z")]
61+
[InlineData("{ x : { $date : { $numberLong : '60000' } } }", "1970-01-01T00:01:00Z")]
62+
[InlineData("{ x : { $date : { $numberLong : '1640995200000' } } }", "2022-01-01T00:00:00Z")]
63+
[InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberLong : 0 }, Offset : 0 } }", "0001-01-01T00:00:00Z")]
64+
[InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberLong : '621355968000000000' }, Offset : 0 } }", "1970-01-01T00:00:00Z")]
65+
[InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberLong : '621355968000000000' }, Offset : 60 } }", "1970-01-01T00:00:00+01:00")]
66+
[InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberLong : '621355968000000000' }, Offset : -60 } }", "1970-01-01T00:00:00-01:00")]
67+
[InlineData("{ x : '0001-01-01T00:00:00+00:00' }", "0001-01-01T00:00:00+00:00")]
68+
[InlineData("{ x : '1970-01-01T00:00:00+00:00' }", "1970-01-01T00:00:00+00:00")]
69+
[InlineData("{ x : '1970-01-01T00:00:00+01:00' }", "1970-01-01T00:00:00+01:00")]
70+
[InlineData("{ x : '1970-01-01T00:00:00-01:00' }", "1970-01-01T00:00:00-01:00")]
71+
public void Deserialize_should_return_expected_result(string json, string expectedResult)
72+
{
73+
var x = DateTimeOffset.Parse(expectedResult);
74+
var m = BsonUtils.ToMillisecondsSinceEpoch(x.UtcDateTime);
75+
var subject = new DateTimeOffsetSerializer();
76+
77+
DateTimeOffset result;
78+
using (var reader = new JsonReader(json))
79+
{
80+
reader.ReadStartDocument();
81+
reader.ReadName("x");
82+
var context = BsonDeserializationContext.CreateRoot(reader);
83+
result = subject.Deserialize(context);
84+
reader.ReadEndDocument();
85+
}
86+
87+
result.Should().Be(DateTimeOffset.Parse(expectedResult));
88+
}
89+
90+
[Theory]
91+
[InlineData(BsonType.Array, "0001-01-01T00:00:00Z", "{ \"x\" : [{ \"$numberLong\" : \"0\" }, { \"$numberInt\" : \"0\" }] }")]
92+
[InlineData(BsonType.Array, "1970-01-01T00:00:00Z", "{ \"x\" : [{ \"$numberLong\" : \"621355968000000000\" }, { \"$numberInt\" : \"0\" }] }")]
93+
[InlineData(BsonType.Array, "1970-01-01T00:00:00+01:00", "{ \"x\" : [{ \"$numberLong\" : \"621355968000000000\" }, { \"$numberInt\" : \"60\" }] }")]
94+
[InlineData(BsonType.Array, "1970-01-01T00:00:00-01:00", "{ \"x\" : [{ \"$numberLong\" : \"621355968000000000\" }, { \"$numberInt\" : \"-60\" }] }")]
95+
[InlineData(BsonType.DateTime, "0001-01-01T00:00:00Z", "{ \"x\" : { \"$date\" : { \"$numberLong\" : \"-62135596800000\" } } }")]
96+
[InlineData(BsonType.DateTime, "1970-01-01T00:00:00Z", "{ \"x\" : { \"$date\" : { \"$numberLong\" : \"0\" } } }")]
97+
[InlineData(BsonType.DateTime, "1970-01-01T00:00:00+01:00", "{ \"x\" : { \"$date\" : { \"$numberLong\" : \"-3600000\" } } }")]
98+
[InlineData(BsonType.DateTime, "1970-01-01T00:00:00-01:00", "{ \"x\" : { \"$date\" : { \"$numberLong\" : \"3600000\" } } }")]
99+
[InlineData(BsonType.Document, "0001-01-01T00:00:00Z", "{ \"x\" : { \"DateTime\" : { \"$date\" : { \"$numberLong\" : \"-62135596800000\" } }, \"Ticks\" : { \"$numberLong\" : \"0\" }, \"Offset\" : { \"$numberInt\" : \"0\" } } }")]
100+
[InlineData(BsonType.Document, "1970-01-01T00:00:00Z", "{ \"x\" : { \"DateTime\" : { \"$date\" : { \"$numberLong\" : \"0\" } }, \"Ticks\" : { \"$numberLong\" : \"621355968000000000\" }, \"Offset\" : { \"$numberInt\" : \"0\" } } }")]
101+
[InlineData(BsonType.Document, "1970-01-01T00:00:00+01:00", "{ \"x\" : { \"DateTime\" : { \"$date\" : { \"$numberLong\" : \"-3600000\" } }, \"Ticks\" : { \"$numberLong\" : \"621355968000000000\" }, \"Offset\" : { \"$numberInt\" : \"60\" } } }")]
102+
[InlineData(BsonType.Document, "1970-01-01T00:00:00-01:00", "{ \"x\" : { \"DateTime\" : { \"$date\" : { \"$numberLong\" : \"3600000\" } }, \"Ticks\" : { \"$numberLong\" : \"621355968000000000\" }, \"Offset\" : { \"$numberInt\" : \"-60\" } } }")]
103+
[InlineData(BsonType.String, "0001-01-01T00:00:00Z", "{ \"x\" : \"0001-01-01T00:00:00+00:00\" }")]
104+
[InlineData(BsonType.String, "1970-01-01T00:00:00Z", "{ \"x\" : \"1970-01-01T00:00:00+00:00\" }")]
105+
[InlineData(BsonType.String, "1970-01-01T00:00:00+01:00", "{ \"x\" : \"1970-01-01T00:00:00+01:00\" }")]
106+
[InlineData(BsonType.String, "1970-01-01T00:00:00-01:00", "{ \"x\" : \"1970-01-01T00:00:00-01:00\" }")]
107+
public void Serialize_should_have_expected_result(BsonType representation, string valueString, string expectedResult)
108+
{
109+
var subject = new DateTimeOffsetSerializer(representation);
110+
var value = DateTimeOffset.Parse(valueString);
111+
112+
string result;
113+
using (var textWriter = new StringWriter())
114+
using (var writer = new JsonWriter(textWriter, new JsonWriterSettings { OutputMode = JsonOutputMode.CanonicalExtendedJson }))
115+
{
116+
var context = BsonSerializationContext.CreateRoot(writer);
117+
writer.WriteStartDocument();
118+
writer.WriteName("x");
119+
subject.Serialize(context, value);
120+
writer.WriteEndDocument();
121+
result = textWriter.ToString();
122+
}
123+
124+
result.Should().Be(expectedResult);
125+
}
126+
[Theory]
127+
[ParameterAttributeData]
128+
public void WithRepresentation_should_return_expected_result(
129+
[Values(BsonType.Array, BsonType.DateTime, BsonType.Document, BsonType.String)] BsonType oldRepresentation,
130+
[Values(BsonType.Array, BsonType.DateTime, BsonType.Document, BsonType.String)] BsonType newRepresentation)
131+
{
132+
var subject = new DateTimeOffsetSerializer(oldRepresentation);
133+
134+
var result = subject.WithRepresentation(newRepresentation);
135+
136+
result.Representation.Should().Be(newRepresentation);
137+
if (newRepresentation == oldRepresentation)
138+
{
139+
result.Should().BeSameAs(subject);
140+
}
141+
}
142+
}
143+
}

0 commit comments

Comments
 (0)