diff options
Diffstat (limited to 'csharp/src/Google.Protobuf')
4 files changed, 373 insertions, 0 deletions
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index 7de4f51b..c7038eb4 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -100,11 +100,14 @@ <Compile Include="WellKnownTypes\Any.cs" />
<Compile Include="WellKnownTypes\Api.cs" />
<Compile Include="WellKnownTypes\Duration.cs" />
+ <Compile Include="WellKnownTypes\DurationPartial.cs" />
<Compile Include="WellKnownTypes\Empty.cs" />
<Compile Include="WellKnownTypes\FieldMask.cs" />
<Compile Include="WellKnownTypes\SourceContext.cs" />
<Compile Include="WellKnownTypes\Struct.cs" />
+ <Compile Include="WellKnownTypes\TimeExtensions.cs" />
<Compile Include="WellKnownTypes\Timestamp.cs" />
+ <Compile Include="WellKnownTypes\TimestampPartial.cs" />
<Compile Include="WellKnownTypes\Type.cs" />
<Compile Include="WellKnownTypes\Wrappers.cs" />
<Compile Include="WireFormat.cs" />
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs new file mode 100644 index 00000000..c69d1b23 --- /dev/null +++ b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs @@ -0,0 +1,145 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; + +namespace Google.Protobuf.WellKnownTypes +{ + // Manually-written partial class for the Duration well-known type, + // providing a conversion to TimeSpan and convenience operators. + public partial class Duration + { + public const int NanosecondsPerSecond = 1000000000; + public const int NanosecondsPerTick = 100; + + /// <summary> + /// Converts this <see cref="Duration"/> to a <see cref="TimeSpan"/>. + /// </summary> + /// <remarks>If the duration is not a precise number of ticks, it is truncated towards 0.</remarks> + /// <returns>The value of this duration, as a <c>TimeSpan</c>.</returns> + public TimeSpan ToTimeSpan() + { + checked + { + long ticks = Seconds * TimeSpan.TicksPerSecond + Nanos / NanosecondsPerTick; + return TimeSpan.FromTicks(ticks); + } + } + + /// <summary> + /// Converts the given <see cref="TimeSpan"/> to a <see cref="Duration"/>. + /// </summary> + /// <param name="timeSpan">The <c>TimeSpan</c> to convert.</param> + /// <returns>The value of the given <c>TimeSpan</c>, as a <c>Duration</c>.</returns> + public static Duration FromTimeSpan(TimeSpan timeSpan) + { + checked + { + long ticks = timeSpan.Ticks; + long seconds = ticks / TimeSpan.TicksPerSecond; + int nanos = (int) (ticks % TimeSpan.TicksPerSecond) * NanosecondsPerTick; + return new Duration { Seconds = seconds, Nanos = nanos }; + } + } + + /// <summary> + /// Returns the result of negating the duration. For example, the negation of 5 minutes is -5 minutes. + /// </summary> + /// <param name="value">The duration to negate. Must not be null.</param> + /// <returns>The negated value of this duration.</returns> + public static Duration operator -(Duration value) + { + Preconditions.CheckNotNull(value, "value"); + checked + { + return Normalize(-value.Seconds, -value.Nanos); + } + } + + /// <summary> + /// Adds the two specified <see cref="Duration"/> values together. + /// </summary> + /// <param name="lhs">The first value to add. Must not be null.</param> + /// <param name="rhs">The second value to add. Must not be null.</param> + /// <returns></returns> + public static Duration operator +(Duration lhs, Duration rhs) + { + Preconditions.CheckNotNull(lhs, "lhs"); + Preconditions.CheckNotNull(rhs, "rhs"); + checked + { + return Normalize(lhs.Seconds + rhs.Seconds, lhs.Nanos + rhs.Nanos); + } + } + + /// <summary> + /// Subtracts one <see cref="Duration"/> from another. + /// </summary> + /// <param name="lhs">The duration to subtract from. Must not be null.</param> + /// <param name="rhs">The duration to subtract. Must not be null.</param> + /// <returns>The difference between the two specified durations.</returns> + public static Duration operator -(Duration lhs, Duration rhs) + { + Preconditions.CheckNotNull(lhs, "lhs"); + Preconditions.CheckNotNull(rhs, "rhs"); + checked + { + return Normalize(lhs.Seconds - rhs.Seconds, lhs.Nanos - rhs.Nanos); + } + } + + /// <summary> + /// Creates a duration with the normalized values from the given number of seconds and + /// nanoseconds, conforming with the description in the proto file. + /// </summary> + internal static Duration Normalize(long seconds, int nanoseconds) + { + // Ensure that nanoseconds is in the range (-1,000,000,000, +1,000,000,000) + int extraSeconds = nanoseconds / NanosecondsPerSecond; + seconds += extraSeconds; + nanoseconds -= extraSeconds * NanosecondsPerSecond; + + // Now make sure that Sign(seconds) == Sign(nanoseconds) if Sign(seconds) != 0. + if (seconds < 0 && nanoseconds > 0) + { + seconds += 1; + nanoseconds -= NanosecondsPerSecond; + } + else if (seconds > 0 && nanoseconds < 0) + { + seconds -= 1; + nanoseconds += NanosecondsPerSecond; + } + return new Duration { Seconds = seconds, Nanos = nanoseconds }; + } + } +} diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/TimeExtensions.cs b/csharp/src/Google.Protobuf/WellKnownTypes/TimeExtensions.cs new file mode 100644 index 00000000..a3806b5c --- /dev/null +++ b/csharp/src/Google.Protobuf/WellKnownTypes/TimeExtensions.cs @@ -0,0 +1,61 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Google.Protobuf.WellKnownTypes +{ + /// <summary> + /// Extension methods on BCL time-related types, converting to protobuf types. + /// </summary> + public static class TimeExtensions + { + public static Timestamp ToTimestamp(this DateTime dateTime) + { + return Timestamp.FromDateTime(dateTime); + } + + public static Timestamp ToTimestamp(this DateTimeOffset dateTimeOffset) + { + return Timestamp.FromDateTimeOffset(dateTimeOffset); + } + + public static Duration ToDuration(this TimeSpan timeSpan) + { + return Duration.FromTimeSpan(timeSpan); + } + } +} diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs new file mode 100644 index 00000000..f682d093 --- /dev/null +++ b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs @@ -0,0 +1,164 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; + +namespace Google.Protobuf.WellKnownTypes +{ + public partial class Timestamp + { + private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private static readonly long BclSecondsAtUnixEpoch = UnixEpoch.Ticks / TimeSpan.TicksPerSecond; + + /// <summary> + /// Returns the difference between one <see cref="Timestamp"/> and another, as a <see cref="Duration"/>. + /// </summary> + /// <param name="lhs">The timestamp to subtract from. Must not be null.</param> + /// <param name="rhs">The timestamp to subtract. Must not be null.</param> + /// <returns>The difference between the two specified timestamps.</returns> + public static Duration operator -(Timestamp lhs, Timestamp rhs) + { + Preconditions.CheckNotNull(lhs, "lhs"); + Preconditions.CheckNotNull(rhs, "rhs"); + checked + { + return Duration.Normalize(lhs.Seconds - rhs.Seconds, lhs.Nanos - rhs.Nanos); + } + } + + /// <summary> + /// Adds a <see cref="Duration"/> to a <see cref="Timestamp"/>, to obtain another <c>Timestamp</c>. + /// </summary> + /// <param name="lhs">The timestamp to add the duration to. Must not be null.</param> + /// <param name="rhs">The duration to add. Must not be null.</param> + /// <returns>The result of adding the duration to the timestamp.</returns> + public static Timestamp operator +(Timestamp lhs, Duration rhs) + { + Preconditions.CheckNotNull(lhs, "lhs"); + Preconditions.CheckNotNull(rhs, "rhs"); + checked + { + return Normalize(lhs.Seconds + rhs.Seconds, lhs.Nanos + rhs.Nanos); + } + } + + /// <summary> + /// Subtracts a <see cref="Duration"/> from a <see cref="Timestamp"/>, to obtain another <c>Timestamp</c>. + /// </summary> + /// <param name="lhs">The timestamp to subtract the duration from. Must not be null.</param> + /// <param name="rhs">The duration to subtract.</param> + /// <returns>The result of subtracting the duration from the timestamp.</returns> + public static Timestamp operator -(Timestamp lhs, Duration rhs) + { + Preconditions.CheckNotNull(lhs, "lhs"); + Preconditions.CheckNotNull(rhs, "rhs"); + checked + { + return Normalize(lhs.Seconds - rhs.Seconds, lhs.Nanos - rhs.Nanos); + } + } + + /// <summary> + /// Converts this timestamp into a <see cref="DateTime"/>. + /// </summary> + /// <remarks> + /// The resulting <c>DateTime</c> will always have a <c>Kind</c> of <c>Utc</c>. + /// If the timestamp is not a precise number of ticks, it will be truncated towards the start + /// of time. For example, a timestamp with a <see cref="Nanos"/> value of 99 will result in a + /// <see cref="DateTime"/> value precisely on a second. + /// </remarks> + /// <returns>This timestamp as a <c>DateTime</c>.</returns> + public DateTime ToDateTime() + { + return UnixEpoch.AddSeconds(Seconds).AddTicks(Nanos / Duration.NanosecondsPerTick); + } + + /// <summary> + /// Converts this timestamp into a <see cref="DateTimeOffset"/>. + /// </summary> + /// <remarks> + /// The resulting <c>DateTimeOffset</c> will always have an <c>Offset</c> of zero. + /// If the timestamp is not a precise number of ticks, it will be truncated towards the start + /// of time. For example, a timestamp with a <see cref="Nanos"/> value of 99 will result in a + /// <see cref="DateTimeOffset"/> value precisely on a second. + /// </remarks> + /// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns> + public DateTimeOffset ToDateTimeOffset() + { + return new DateTimeOffset(ToDateTime(), TimeSpan.Zero); + } + + /// <summary> + /// Converts the specified <see cref="DateTime"/> to a <see cref="Timestamp"/>. + /// </summary> + /// <param name="dateTime"></param> + /// <exception cref="ArgumentException">The <c>Kind</c> of <paramref name="dateTime"/> is not <c>DateTimeKind.Utc</c>.</exception> + /// <returns>The converted timestamp.</returns> + public static Timestamp FromDateTime(DateTime dateTime) + { + if (dateTime.Kind != DateTimeKind.Utc) + { + throw new ArgumentException("Conversion from DateTime to Timestamp requires the DateTime kind to be Utc", "dateTime"); + } + // Do the arithmetic using DateTime.Ticks, which is always non-negative, making things simpler. + long secondsSinceBclEpoch = dateTime.Ticks / TimeSpan.TicksPerSecond; + int nanoseconds = (int) (dateTime.Ticks % TimeSpan.TicksPerSecond) * Duration.NanosecondsPerTick; + return new Timestamp { Seconds = secondsSinceBclEpoch - BclSecondsAtUnixEpoch, Nanos = nanoseconds }; + } + + /// <summary> + /// Converts the specified <see cref="DateTimeOffset"/> to a <see cref="Timestamp"/>. + /// </summary> + /// <param name="dateTime"></param> + /// <returns>The converted timestamp.</returns> + public static Timestamp FromDateTimeOffset(DateTimeOffset dateTimeOffset) + { + // We don't need to worry about this having negative ticks: DateTimeOffset is constrained to handle + // values whose *UTC* value is in the range of DateTime. + return FromDateTime(dateTimeOffset.UtcDateTime); + } + + private static Timestamp Normalize(long seconds, int nanoseconds) + { + int extraSeconds = nanoseconds / Duration.NanosecondsPerSecond; + seconds += extraSeconds; + nanoseconds -= extraSeconds * Duration.NanosecondsPerSecond; + + if (nanoseconds < 0) + { + nanoseconds += Duration.NanosecondsPerSecond; + seconds--; + } + return new Timestamp { Seconds = seconds, Nanos = nanoseconds }; + } + } +} |