Skip to content

Commit

Permalink
Implement IComparable<Duration> for the Duration type (C#)
Browse files Browse the repository at this point in the history
Note that unlike Timestamp, this does *not* also overload comparison
operators. Adding a `==` overload now would be a breaking change (as
it would change the meaning of existing code from a reference
comparison to a value comparison), and implementing `<`, `<=`, `>=`
and `>` without implementing `==` would be odd.

Implementing `IComparable<T>` makes sorting much easier, however.

Fixes protocolbuffers#7628
  • Loading branch information
jskeet committed Aug 23, 2022
1 parent 3c32c05 commit 8fd097b
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 1 deletion.
33 changes: 33 additions & 0 deletions csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,38 @@ public void ToString_NonNormalized()
var duration = new Duration { Seconds = 1, Nanos = -1 };
Assert.AreEqual("{ \"@warning\": \"Invalid Duration\", \"seconds\": \"1\", \"nanos\": -1 }", duration.ToString());
}

[Test]
public void Comparability()
{
Duration[] durationsInExpectedSortOrder =
{
null,
new Duration { Seconds = -10, Nanos = -10 },
new Duration { Seconds = -10, Nanos = -1 },
new Duration { Seconds = -1, Nanos = -10 },
new Duration { Seconds = -1, Nanos = -1 },
new Duration(),
new Duration { Seconds = 1, Nanos = 1 },
new Duration { Seconds = 1, Nanos = 10 },
new Duration { Seconds = 10, Nanos = 1 },
new Duration { Seconds = 10, Nanos = 10 }
};

for (int i = 0; i < durationsInExpectedSortOrder.Length; i++)
{
var target = durationsInExpectedSortOrder[i];
if (target is null)
{
continue;
}
for (int j = 0; j < durationsInExpectedSortOrder.Length; j++)
{
var expectedResult = Math.Sign(i - j);
var actualResult = target.CompareTo(durationsInExpectedSortOrder[j]);
Assert.AreEqual(expectedResult, actualResult);
}
}
}
}
}
23 changes: 22 additions & 1 deletion csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ 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 : ICustomDiagnosticMessage
public partial class Duration : ICustomDiagnosticMessage, IComparable<Duration>
{
/// <summary>
/// The number of nanoseconds in a second.
Expand Down Expand Up @@ -266,5 +266,26 @@ internal static void AppendNanoseconds(StringBuilder builder, int nanos)
}
}
}


/// <summary>
/// Given another duration, returns 0 if the durations are equivalent, -1 if this duration is shorter than the other, and 1 otherwise.
/// </summary>
/// <remarks>
/// This method expects that both durations are normalized; that is, that the values of <see cref="Seconds"/>
/// and <see cref="Nanos"/> are within the documented bounds.
/// If either value is not normalized, the results of this method are unspecified.
/// </remarks>
/// <param name="other">The duration to compare with this object.</param>
/// <returns>An integer indicating whether this duration is shorter or longer than <paramref name="other"/>.</returns>
public int CompareTo(Duration other)
{
return other == null ? 1
: Seconds < other.Seconds ? -1
: Seconds > other.Seconds ? 1
: Nanos < other.Nanos ? -1
: Nanos > other.Nanos ? 1
: 0;
}
}
}

0 comments on commit 8fd097b

Please sign in to comment.