Permalink
Browse files

CSHARP-647: Improvements to TimeSpanUnits and TimeSpanSerializer.

  • Loading branch information...
1 parent 72b74ad commit 7be975692fa2502834cd3a48d81bf8e2c4fd1640 rstam committed Dec 16, 2012
@@ -100,6 +100,7 @@
<Compile Include="Serialization\Attributes\BsonRepresentationAttribute.cs" />
<Compile Include="Serialization\Attributes\BsonSerializationOptionsAttribute.cs" />
<Compile Include="Serialization\Attributes\BsonSerializerAttribute.cs" />
+ <Compile Include="Serialization\Attributes\BsonTimeSpanOptionsAttribute.cs" />
<Compile Include="Serialization\BsonClassMapSerializationProvider.cs" />
<Compile Include="Serialization\BsonDocumentBackedClass.cs" />
<Compile Include="Serialization\IdGenerators\BsonBinaryDataGuidGenerator.cs" />
@@ -0,0 +1,75 @@
+/* Copyright 2010-2012 10gen Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using MongoDB.Bson.Serialization;
+using MongoDB.Bson.Serialization.Options;
+
+namespace MongoDB.Bson.Serialization.Attributes
+{
+ /// <summary>
+ /// Specifies the external representation and related options for this field or property.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public class BsonTimeSpanOptionsAttribute : BsonSerializationOptionsAttribute
+ {
+ // private fields
+ private BsonType _representation;
+ private TimeSpanUnits _units;
+
+ // constructors
+ /// <summary>
+ /// Initializes a new instance of the BsonTimeSpanOptionsAttribute class.
+ /// </summary>
+ /// <param name="representation">The external representation.</param>
+ public BsonTimeSpanOptionsAttribute(BsonType representation)
+ {
+ _representation = representation;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the BsonTimeSpanOptionsAttribute class.
+ /// </summary>
+ /// <param name="representation">The external representation.</param>
+ /// <param name="units">The TimeSpanUnits.</param>
+ public BsonTimeSpanOptionsAttribute(BsonType representation, TimeSpanUnits units)
+ {
+ _representation = representation;
+ _units = units;
+ }
+
+ // public properties
+ /// <summary>
+ /// Gets the external representation.
+ /// </summary>
+ public BsonType Representation
+ {
+ get { return _representation; }
+ }
+
+ /// <summary>
+ /// Gets or sets the TimeSpanUnits.
+ /// </summary>
+ public TimeSpanUnits Units
+ {
+ get { return _units; }
+ set { _units = value; }
+ }
+ }
+}
@@ -95,6 +95,14 @@ public override void ApplyAttribute(IBsonSerializer serializer, Attribute attrib
return;
}
+ var optionsAttribute = attribute as BsonTimeSpanOptionsAttribute;
+ if (optionsAttribute != null)
+ {
+ _representation = optionsAttribute.Representation;
+ _units = optionsAttribute.Units;
+ return;
+ }
+
var message = string.Format("A serialization options attribute of type {0} cannot be applied to serialization options of type {1}.",
BsonUtils.GetFriendlyTypeName(attribute.GetType()), BsonUtils.GetFriendlyTypeName(GetType()));
throw new NotSupportedException(message);
@@ -76,50 +76,19 @@ public static TimeSpanSerializer Instance
var timeSpanSerializationOptions = EnsureSerializationOptions<TimeSpanSerializationOptions>(options);
BsonType bsonType = bsonReader.GetCurrentBsonType();
- if (bsonType == BsonType.String)
+ switch (bsonType)
{
- return TimeSpan.Parse(bsonReader.ReadString()); // not XmlConvert.ToTimeSpan (we're using .NET's format for TimeSpan)
- }
- else if (timeSpanSerializationOptions.Units == TimeSpanUnits.Ticks)
- {
- long ticks;
- switch (bsonType)
- {
- case BsonType.Double: ticks = (long)bsonReader.ReadDouble(); break;
- case BsonType.Int32: ticks = (long)bsonReader.ReadInt32(); break;
- case BsonType.Int64: ticks = bsonReader.ReadInt64(); break;
- default:
- var message = string.Format("Cannot deserialize TimeSpan from BsonType {0}.", bsonType);
- throw new FileFormatException(message);
- }
- return new TimeSpan(ticks);
- }
- else
- {
- double interval;
- switch (bsonType)
- {
- case BsonType.Double: interval = bsonReader.ReadDouble(); break;
- case BsonType.Int32: interval = bsonReader.ReadInt32(); break;
- case BsonType.Int64: interval = bsonReader.ReadInt64(); break;
- default:
- var message = string.Format("Cannot deserialize TimeSpan from BsonType {0}.", bsonType);
- throw new FileFormatException(message);
- }
-
- switch (timeSpanSerializationOptions.Units)
- {
- case TimeSpanUnits.Days: return TimeSpan.FromDays(interval);
- case TimeSpanUnits.Hours: return TimeSpan.FromHours(interval);
- case TimeSpanUnits.Minutes: return TimeSpan.FromMinutes(interval);
- case TimeSpanUnits.Seconds: return TimeSpan.FromSeconds(interval);
- case TimeSpanUnits.Milliseconds: return TimeSpan.FromMilliseconds(interval);
- case TimeSpanUnits.Microseconds: return TimeSpan.FromTicks((long)interval*10L);
- case TimeSpanUnits.Nanoseconds: return TimeSpan.FromMilliseconds(interval / 1000.0);
- default:
- var message = string.Format("'{0}' is not a valid TimeSpanUnits value.", timeSpanSerializationOptions.Units);
- throw new BsonSerializationException(message);
- }
+ case BsonType.Double:
+ return FromDouble(bsonReader.ReadDouble(), timeSpanSerializationOptions.Units);
+ case BsonType.Int32:
+ return FromInt32(bsonReader.ReadInt32(), timeSpanSerializationOptions.Units);
+ case BsonType.Int64:
+ return FromInt64(bsonReader.ReadInt64(), timeSpanSerializationOptions.Units);
+ case BsonType.String:
+ return TimeSpan.Parse(bsonReader.ReadString()); // not XmlConvert.ToTimeSpan (we're using .NET's format for TimeSpan)
+ default:
+ var message = string.Format("Cannot deserialize TimeSpan from BsonType {0}.", bsonType);
+ throw new FileFormatException(message);
}
}
@@ -146,49 +115,113 @@ public static TimeSpanSerializer Instance
}
var timeSpanSerializationOptions = EnsureSerializationOptions<TimeSpanSerializationOptions>(options);
- if (timeSpanSerializationOptions.Representation == BsonType.String)
+ switch (timeSpanSerializationOptions.Representation)
+ {
+ case BsonType.Double:
+ bsonWriter.WriteDouble(ToDouble(timeSpan, timeSpanSerializationOptions.Units));
+ break;
+ case BsonType.Int32:
+ bsonWriter.WriteInt32(ToInt32(timeSpan, timeSpanSerializationOptions.Units));
+ break;
+ case BsonType.Int64:
+ bsonWriter.WriteInt64(ToInt64(timeSpan, timeSpanSerializationOptions.Units));
+ break;
+ case BsonType.String:
+ bsonWriter.WriteString(timeSpan.ToString()); // not XmlConvert.ToString (we're using .NET's format for TimeSpan)
+ break;
+ default:
+ var message = string.Format("'{0}' is not a valid TimeSpan representation.", timeSpanSerializationOptions.Representation);
+ throw new BsonSerializationException(message);
+ }
+ }
+
+ // private methods
+ private TimeSpan FromDouble(double value, TimeSpanUnits units)
+ {
+ if (units == TimeSpanUnits.Nanoseconds)
{
- bsonWriter.WriteString(timeSpan.ToString()); // for TimeSpan use .NET's format instead of XmlConvert.ToString
+ return TimeSpan.FromTicks((long)(value / 100.0)); // divide first then cast to reduce chance of overflow
}
- else if (timeSpanSerializationOptions.Units == TimeSpanUnits.Ticks)
+ else
+ {
+ return TimeSpan.FromTicks((long)(value * TicksPerUnit(units))); // multiply first then cast to preserve fractional part of value
+ }
+ }
+
+ private TimeSpan FromInt32(int value, TimeSpanUnits units)
+ {
+ if (units == TimeSpanUnits.Nanoseconds)
+ {
+ return TimeSpan.FromTicks(value / 100);
+ }
+ else
+ {
+ return TimeSpan.FromTicks(value * TicksPerUnit(units));
+ }
+ }
+
+ private TimeSpan FromInt64(long value, TimeSpanUnits units)
+ {
+ if (units == TimeSpanUnits.Nanoseconds)
+ {
+ return TimeSpan.FromTicks(value / 100);
+ }
+ else
+ {
+ return TimeSpan.FromTicks(value * TicksPerUnit(units));
+ }
+ }
+
+ private long TicksPerUnit(TimeSpanUnits units)
+ {
+ switch (units)
+ {
+ case TimeSpanUnits.Days: return TimeSpan.TicksPerDay;
+ case TimeSpanUnits.Hours: return TimeSpan.TicksPerHour;
+ case TimeSpanUnits.Minutes: return TimeSpan.TicksPerMinute;
+ case TimeSpanUnits.Seconds: return TimeSpan.TicksPerSecond;
+ case TimeSpanUnits.Milliseconds: return TimeSpan.TicksPerMillisecond;
+ case TimeSpanUnits.Microseconds: return TimeSpan.TicksPerMillisecond / 1000;
+ case TimeSpanUnits.Ticks: return 1;
+ default:
+ var message = string.Format("Invalid TimeSpanUnits value: {0}.", units);
+ throw new ArgumentException(message);
+ }
+ }
+
+ private double ToDouble(TimeSpan timeSpan, TimeSpanUnits units)
+ {
+ if (units == TimeSpanUnits.Nanoseconds)
+ {
+ return (double)(timeSpan.Ticks) * 100.0;
+ }
+ else
+ {
+ return (double)timeSpan.Ticks / (double)TicksPerUnit(units); // cast first then divide to preserve fractional part of result
+ }
+ }
+
+ private int ToInt32(TimeSpan timeSpan, TimeSpanUnits units)
+ {
+ if (units == TimeSpanUnits.Nanoseconds)
+ {
+ return (int)(timeSpan.Ticks * 100);
+ }
+ else
+ {
+ return (int)(timeSpan.Ticks / TicksPerUnit(units));
+ }
+ }
+
+ private long ToInt64(TimeSpan timeSpan, TimeSpanUnits units)
+ {
+ if (units == TimeSpanUnits.Nanoseconds)
{
- var ticks = timeSpan.Ticks;
- switch (timeSpanSerializationOptions.Representation)
- {
- case BsonType.Double: bsonWriter.WriteDouble((double)ticks); break;
- case BsonType.Int32: bsonWriter.WriteInt32((int)ticks); break;
- case BsonType.Int64: bsonWriter.WriteInt64(ticks); break;
- default:
- var message = string.Format("'{0}' is not a valid TimeSpan representation.", timeSpanSerializationOptions.Representation);
- throw new BsonSerializationException(message);
- }
+ return timeSpan.Ticks * 100;
}
else
{
- double interval;
- switch (timeSpanSerializationOptions.Units)
- {
- case TimeSpanUnits.Days: interval = timeSpan.TotalDays; break;
- case TimeSpanUnits.Hours: interval = timeSpan.TotalHours; break;
- case TimeSpanUnits.Minutes: interval = timeSpan.TotalMinutes; break;
- case TimeSpanUnits.Seconds: interval = timeSpan.TotalSeconds; break;
- case TimeSpanUnits.Milliseconds: interval = timeSpan.TotalMilliseconds; break;
- case TimeSpanUnits.Microseconds: interval = timeSpan.Ticks / 10d; break;
- case TimeSpanUnits.Nanoseconds: interval = timeSpan.TotalMilliseconds * 1000.0; break;
- default:
- var message = string.Format("'{0}' is not a valid TimeSpanUnits value.", timeSpanSerializationOptions.Units);
- throw new BsonSerializationException(message);
- }
-
- switch (timeSpanSerializationOptions.Representation)
- {
- case BsonType.Double: bsonWriter.WriteDouble(interval); break;
- case BsonType.Int32: bsonWriter.WriteInt32((int)interval); break;
- case BsonType.Int64: bsonWriter.WriteInt64((long)interval); break;
- default:
- var message = string.Format("'{0}' is not a valid TimeSpan representation.", timeSpanSerializationOptions.Representation);
- throw new BsonSerializationException(message);
- }
+ return timeSpan.Ticks / TicksPerUnit(units);
}
}
}
@@ -100,6 +100,7 @@
<Compile Include="Serialization\Serializers\JaggedArraySerializerTests.cs" />
<Compile Include="Serialization\Serializers\ObjectSerializerTests.cs" />
<Compile Include="Serialization\Serializers\ThreeDimensionalArraySerializerTests.cs" />
+ <Compile Include="Serialization\Serializers\TimeSpanSerializerTests.cs" />
<Compile Include="Serialization\Serializers\TwoDimensionalArraySerializerTests.cs" />
<Compile Include="Serialization\Serializers\DateTimeSerializerTests.cs" />
<Compile Include="Serialization\Serializers\CollectionSerializerGenericTests.cs" />
Oops, something went wrong.

0 comments on commit 7be9756

Please sign in to comment.