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 MongoDB . Bson . Serialization . Options ;
18+
19+ namespace MongoDB . Bson . Serialization . Serializers
20+ {
21+ #if NET6_0_OR_GREATER
22+ /// <summary>
23+ /// Represents a serializer for TimeOnlys
24+ /// </summary>
25+ public sealed class TimeOnlySerializer : StructSerializerBase < TimeOnly > , IRepresentationConfigurable < TimeOnlySerializer >
26+ {
27+ // static
28+ private static readonly TimeOnlySerializer __instance = new ( ) ;
29+
30+ /// <summary>
31+ /// Gets the default TimeOnlySerializer
32+ /// </summary>
33+ public static TimeOnlySerializer Instance => __instance ;
34+
35+ // private fields
36+ private readonly BsonType _representation ;
37+ private readonly TimeOnlyUnits _units ;
38+
39+ // constructors
40+ /// <summary>
41+ /// Initializes a new instance of the <see cref="TimeOnlySerializer"/> class.
42+ /// </summary>
43+ public TimeOnlySerializer ( )
44+ : this ( BsonType . Int64 , TimeOnlyUnits . Ticks )
45+ {
46+ }
47+
48+ /// <summary>
49+ /// Initializes a new instance of the <see cref="TimeOnlySerializer"/> class.
50+ /// </summary>
51+ /// <param name="representation">The representation.</param>
52+ public TimeOnlySerializer ( BsonType representation )
53+ : this ( representation , TimeOnlyUnits . Ticks )
54+ {
55+ }
56+
57+ /// <summary>
58+ /// Initializes a new instance of the <see cref="TimeOnlySerializer"/> class.
59+ /// </summary>
60+ /// <param name="representation">The representation.</param>
61+ /// <param name="units">The units.</param>
62+ public TimeOnlySerializer ( BsonType representation , TimeOnlyUnits units )
63+ {
64+ switch ( representation )
65+ {
66+ case BsonType . Double :
67+ case BsonType . Int32 :
68+ case BsonType . Int64 :
69+ case BsonType . String :
70+ break ;
71+
72+ default :
73+ throw new ArgumentException ( $ "{ representation } is not a valid representation for a TimeOnlySerializer.") ;
74+ }
75+
76+ _representation = representation ;
77+ _units = units ;
78+ }
79+
80+ // public properties
81+ /// <inheritdoc />
82+ public BsonType Representation => _representation ;
83+
84+ /// <summary>
85+ /// Gets the units.
86+ /// </summary>
87+ /// <value>
88+ /// The units.
89+ /// </value>
90+ public TimeOnlyUnits Units => _units ;
91+
92+ // public methods
93+ /// <inheritdoc/>
94+ public override TimeOnly Deserialize ( BsonDeserializationContext context , BsonDeserializationArgs args )
95+ {
96+ var bsonReader = context . Reader ;
97+ var bsonType = bsonReader . GetCurrentBsonType ( ) ;
98+
99+ return bsonType switch
100+ {
101+ BsonType . String => TimeOnly . ParseExact ( bsonReader . ReadString ( ) , "o" ) ,
102+ BsonType . Int64 => FromInt64 ( bsonReader . ReadInt64 ( ) , _units ) ,
103+ BsonType . Int32 => FromInt32 ( bsonReader . ReadInt32 ( ) , _units ) ,
104+ BsonType . Double => FromDouble ( bsonReader . ReadDouble ( ) , _units ) ,
105+ _ => throw CreateCannotDeserializeFromBsonTypeException ( bsonType )
106+ } ;
107+ }
108+
109+ /// <inheritdoc/>
110+ public override bool Equals ( object obj )
111+ {
112+ if ( object . ReferenceEquals ( obj , null ) ) { return false ; }
113+ if ( object . ReferenceEquals ( this , obj ) ) { return true ; }
114+
115+ return
116+ base . Equals ( obj ) &&
117+ obj is TimeOnlySerializer other &&
118+ _representation . Equals ( other . _representation ) &&
119+ _units . Equals ( other . _units ) ;
120+ }
121+
122+ /// <inheritdoc/>
123+ public override int GetHashCode ( ) => 0 ;
124+
125+ /// <inheritdoc />
126+ public override void Serialize ( BsonSerializationContext context , BsonSerializationArgs args , TimeOnly value )
127+ {
128+ var bsonWriter = context . Writer ;
129+
130+ switch ( _representation )
131+ {
132+ case BsonType . Double :
133+ bsonWriter . WriteDouble ( ToDouble ( value , _units ) ) ;
134+ break ;
135+
136+ case BsonType . Int32 :
137+ bsonWriter . WriteInt32 ( ToInt32 ( value , _units ) ) ;
138+ break ;
139+
140+ case BsonType . Int64 :
141+ bsonWriter . WriteInt64 ( ToInt64 ( value , _units ) ) ;
142+ break ;
143+
144+ case BsonType . String :
145+ bsonWriter . WriteString ( value . ToString ( "o" ) ) ;
146+ break ;
147+
148+ default :
149+ throw new BsonSerializationException ( $ "'{ _representation } ' is not a valid TimeOnly representation.") ;
150+ }
151+ }
152+
153+ /// <inheritdoc />
154+ public TimeOnlySerializer WithRepresentation ( BsonType representation )
155+ {
156+ return representation == _representation ? this : new TimeOnlySerializer ( representation ) ;
157+ }
158+
159+ /// <summary>
160+ /// Returns a serializer that has been reconfigured with the specified representation and units.
161+ /// </summary>
162+ /// <param name="representation">The representation.</param>
163+ /// <param name="units">The units.</param>
164+ /// <returns>
165+ /// The reconfigured serializer.
166+ /// </returns>
167+ public TimeOnlySerializer WithRepresentation ( BsonType representation , TimeOnlyUnits units )
168+ {
169+ if ( representation == _representation && units == _units )
170+ {
171+ return this ;
172+ }
173+
174+ return new TimeOnlySerializer ( representation , units ) ;
175+ }
176+
177+ // private methods
178+ private TimeOnly FromDouble ( double value , TimeOnlyUnits units )
179+ {
180+ return units is TimeOnlyUnits . Nanoseconds
181+ ? new TimeOnly ( ( long ) ( value / 100.0 ) )
182+ : new TimeOnly ( ( long ) ( value * TicksPerUnit ( units ) ) ) ;
183+ }
184+
185+ private TimeOnly FromInt32 ( int value , TimeOnlyUnits units )
186+ {
187+ return units is TimeOnlyUnits . Nanoseconds
188+ ? new TimeOnly ( value / 100 )
189+ : new TimeOnly ( value * TicksPerUnit ( units ) ) ;
190+ }
191+
192+ private TimeOnly FromInt64 ( long value , TimeOnlyUnits units )
193+ {
194+ return units is TimeOnlyUnits . Nanoseconds
195+ ? new TimeOnly ( value / 100 )
196+ : new TimeOnly ( value * TicksPerUnit ( units ) ) ;
197+ }
198+
199+ private long TicksPerUnit ( TimeOnlyUnits units )
200+ {
201+ return units switch
202+ {
203+ TimeOnlyUnits . Hours => TimeSpan . TicksPerHour ,
204+ TimeOnlyUnits . Minutes => TimeSpan . TicksPerMinute ,
205+ TimeOnlyUnits . Seconds => TimeSpan . TicksPerSecond ,
206+ TimeOnlyUnits . Milliseconds => TimeSpan . TicksPerMillisecond ,
207+ TimeOnlyUnits . Microseconds => TimeSpan . TicksPerMillisecond / 1000 ,
208+ TimeOnlyUnits . Ticks => 1 ,
209+ _ => throw new ArgumentException ( $ "Invalid TimeOnlyUnits value: { units } .")
210+ } ;
211+ }
212+
213+ private double ToDouble ( TimeOnly timeOnly , TimeOnlyUnits units )
214+ {
215+ return units is TimeOnlyUnits . Nanoseconds
216+ ? timeOnly . Ticks * 100
217+ : timeOnly . Ticks / ( double ) TicksPerUnit ( units ) ;
218+ }
219+
220+ private int ToInt32 ( TimeOnly timeOnly , TimeOnlyUnits units )
221+ {
222+ return units is TimeOnlyUnits . Nanoseconds
223+ ? ( int ) ( timeOnly . Ticks * 100 )
224+ : ( int ) ( timeOnly . Ticks / TicksPerUnit ( units ) ) ;
225+ }
226+
227+ private long ToInt64 ( TimeOnly timeOnly , TimeOnlyUnits units )
228+ {
229+ return units is TimeOnlyUnits . Nanoseconds
230+ ? timeOnly . Ticks * 100
231+ : timeOnly . Ticks / TicksPerUnit ( units ) ;
232+ }
233+
234+ // explicit interface implementations
235+ IBsonSerializer IRepresentationConfigurable . WithRepresentation ( BsonType representation )
236+ {
237+ return WithRepresentation ( representation ) ;
238+ }
239+ }
240+ #endif
241+ }
0 commit comments