TimeSpanSerializer.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. using System;
  16. using System.IO;
  17. using MongoDB.Bson.IO;
  18. using MongoDB.Bson.Serialization.Attributes;
  19. using MongoDB.Bson.Serialization.Options;
  20. namespace MongoDB.Bson.Serialization.Serializers
  21. {
  22. /// <summary>
  23. /// Represents a serializer for Timespans.
  24. /// </summary>
  25. public class TimeSpanSerializer : StructSerializerBase<TimeSpan>, IRepresentationConfigurable<TimeSpanSerializer>
  26. {
  27. // private fields
  28. private readonly BsonType _representation;
  29. private readonly TimeSpanUnits _units;
  30. // constructors
  31. /// <summary>
  32. /// Initializes a new instance of the <see cref="TimeSpanSerializer"/> class.
  33. /// </summary>
  34. public TimeSpanSerializer()
  35. : this(BsonType.String)
  36. {
  37. }
  38. /// <summary>
  39. /// Initializes a new instance of the <see cref="TimeSpanSerializer"/> class.
  40. /// </summary>
  41. /// <param name="representation">The representation.</param>
  42. public TimeSpanSerializer(BsonType representation)
  43. : this(representation, TimeSpanUnits.Ticks)
  44. {
  45. }
  46. /// <summary>
  47. /// Initializes a new instance of the <see cref="TimeSpanSerializer"/> class.
  48. /// </summary>
  49. /// <param name="representation">The representation.</param>
  50. /// <param name="units">The units.</param>
  51. public TimeSpanSerializer(BsonType representation, TimeSpanUnits units)
  52. {
  53. switch (representation)
  54. {
  55. case BsonType.Double:
  56. case BsonType.Int32:
  57. case BsonType.Int64:
  58. case BsonType.String:
  59. break;
  60. default:
  61. var message = string.Format("{0} is not a valid representation for a TimeSpanSerializer.", representation);
  62. throw new ArgumentException(message);
  63. }
  64. _representation = representation;
  65. _units = units;
  66. }
  67. // public properties
  68. /// <summary>
  69. /// Gets the representation.
  70. /// </summary>
  71. /// <value>
  72. /// The representation.
  73. /// </value>
  74. public BsonType Representation
  75. {
  76. get { return _representation; }
  77. }
  78. /// <summary>
  79. /// Gets the units.
  80. /// </summary>
  81. /// <value>
  82. /// The units.
  83. /// </value>
  84. public TimeSpanUnits Units
  85. {
  86. get { return _units; }
  87. }
  88. // public methods
  89. /// <summary>
  90. /// Deserializes a value.
  91. /// </summary>
  92. /// <param name="context">The deserialization context.</param>
  93. /// <param name="args">The deserialization args.</param>
  94. /// <returns>A deserialized value.</returns>
  95. public override TimeSpan Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
  96. {
  97. var bsonReader = context.Reader;
  98. BsonType bsonType = bsonReader.GetCurrentBsonType();
  99. switch (bsonType)
  100. {
  101. case BsonType.Double:
  102. return FromDouble(bsonReader.ReadDouble(), _units);
  103. case BsonType.Int32:
  104. return FromInt32(bsonReader.ReadInt32(), _units);
  105. case BsonType.Int64:
  106. return FromInt64(bsonReader.ReadInt64(), _units);
  107. case BsonType.String:
  108. return TimeSpan.Parse(bsonReader.ReadString()); // not XmlConvert.ToTimeSpan (we're using .NET's format for TimeSpan)
  109. default:
  110. throw CreateCannotDeserializeFromBsonTypeException(bsonType);
  111. }
  112. }
  113. /// <summary>
  114. /// Serializes a value.
  115. /// </summary>
  116. /// <param name="context">The serialization context.</param>
  117. /// <param name="args">The serialization args.</param>
  118. /// <param name="value">The object.</param>
  119. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TimeSpan value)
  120. {
  121. var bsonWriter = context.Writer;
  122. switch (_representation)
  123. {
  124. case BsonType.Double:
  125. bsonWriter.WriteDouble(ToDouble(value, _units));
  126. break;
  127. case BsonType.Int32:
  128. bsonWriter.WriteInt32(ToInt32(value, _units));
  129. break;
  130. case BsonType.Int64:
  131. bsonWriter.WriteInt64(ToInt64(value, _units));
  132. break;
  133. case BsonType.String:
  134. bsonWriter.WriteString(value.ToString()); // not XmlConvert.ToString (we're using .NET's format for TimeSpan)
  135. break;
  136. default:
  137. var message = string.Format("'{0}' is not a valid TimeSpan representation.", _representation);
  138. throw new BsonSerializationException(message);
  139. }
  140. }
  141. /// <summary>
  142. /// Returns a serializer that has been reconfigured with the specified representation.
  143. /// </summary>
  144. /// <param name="representation">The representation.</param>
  145. /// <returns>The reconfigured serializer.</returns>
  146. public TimeSpanSerializer WithRepresentation(BsonType representation)
  147. {
  148. if (representation == _representation)
  149. {
  150. return this;
  151. }
  152. else
  153. {
  154. return new TimeSpanSerializer(representation, _units);
  155. }
  156. }
  157. /// <summary>
  158. /// Returns a serializer that has been reconfigured with the specified representation and units.
  159. /// </summary>
  160. /// <param name="representation">The representation.</param>
  161. /// <param name="units">The units.</param>
  162. /// <returns>
  163. /// The reconfigured serializer.
  164. /// </returns>
  165. public TimeSpanSerializer WithRepresentation(BsonType representation, TimeSpanUnits units)
  166. {
  167. if (representation == _representation && units == _units)
  168. {
  169. return this;
  170. }
  171. else
  172. {
  173. return new TimeSpanSerializer(representation, units);
  174. }
  175. }
  176. // private methods
  177. private TimeSpan FromDouble(double value, TimeSpanUnits units)
  178. {
  179. if (units == TimeSpanUnits.Nanoseconds)
  180. {
  181. return TimeSpan.FromTicks((long)(value / 100.0)); // divide first then cast to reduce chance of overflow
  182. }
  183. else
  184. {
  185. return TimeSpan.FromTicks((long)(value * TicksPerUnit(units))); // multiply first then cast to preserve fractional part of value
  186. }
  187. }
  188. private TimeSpan FromInt32(int value, TimeSpanUnits units)
  189. {
  190. if (units == TimeSpanUnits.Nanoseconds)
  191. {
  192. return TimeSpan.FromTicks(value / 100);
  193. }
  194. else
  195. {
  196. return TimeSpan.FromTicks(value * TicksPerUnit(units));
  197. }
  198. }
  199. private TimeSpan FromInt64(long value, TimeSpanUnits units)
  200. {
  201. if (units == TimeSpanUnits.Nanoseconds)
  202. {
  203. return TimeSpan.FromTicks(value / 100);
  204. }
  205. else
  206. {
  207. return TimeSpan.FromTicks(value * TicksPerUnit(units));
  208. }
  209. }
  210. private long TicksPerUnit(TimeSpanUnits units)
  211. {
  212. switch (units)
  213. {
  214. case TimeSpanUnits.Days: return TimeSpan.TicksPerDay;
  215. case TimeSpanUnits.Hours: return TimeSpan.TicksPerHour;
  216. case TimeSpanUnits.Minutes: return TimeSpan.TicksPerMinute;
  217. case TimeSpanUnits.Seconds: return TimeSpan.TicksPerSecond;
  218. case TimeSpanUnits.Milliseconds: return TimeSpan.TicksPerMillisecond;
  219. case TimeSpanUnits.Microseconds: return TimeSpan.TicksPerMillisecond / 1000;
  220. case TimeSpanUnits.Ticks: return 1;
  221. default:
  222. var message = string.Format("Invalid TimeSpanUnits value: {0}.", units);
  223. throw new ArgumentException(message);
  224. }
  225. }
  226. private double ToDouble(TimeSpan timeSpan, TimeSpanUnits units)
  227. {
  228. if (units == TimeSpanUnits.Nanoseconds)
  229. {
  230. return (double)(timeSpan.Ticks) * 100.0;
  231. }
  232. else
  233. {
  234. return (double)timeSpan.Ticks / (double)TicksPerUnit(units); // cast first then divide to preserve fractional part of result
  235. }
  236. }
  237. private int ToInt32(TimeSpan timeSpan, TimeSpanUnits units)
  238. {
  239. if (units == TimeSpanUnits.Nanoseconds)
  240. {
  241. return (int)(timeSpan.Ticks * 100);
  242. }
  243. else
  244. {
  245. return (int)(timeSpan.Ticks / TicksPerUnit(units));
  246. }
  247. }
  248. private long ToInt64(TimeSpan timeSpan, TimeSpanUnits units)
  249. {
  250. if (units == TimeSpanUnits.Nanoseconds)
  251. {
  252. return timeSpan.Ticks * 100;
  253. }
  254. else
  255. {
  256. return timeSpan.Ticks / TicksPerUnit(units);
  257. }
  258. }
  259. // explicit interface implementations
  260. IBsonSerializer IRepresentationConfigurable.WithRepresentation(BsonType representation)
  261. {
  262. return WithRepresentation(representation);
  263. }
  264. }
  265. }