Browse Source

implement IComparable and comparison operators on Timestamp (#4318)

Warren Falk 7 years ago
parent
commit
96833b8f4b

+ 101 - 0
csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs

@@ -111,5 +111,106 @@ namespace Google.Protobuf.WellKnownTypes
             var duration = new Timestamp { Seconds = 1, Nanos = -1 };
             var duration = new Timestamp { Seconds = 1, Nanos = -1 };
             Assert.AreEqual("{ \"@warning\": \"Invalid Timestamp\", \"seconds\": \"1\", \"nanos\": -1 }", duration.ToString());
             Assert.AreEqual("{ \"@warning\": \"Invalid Timestamp\", \"seconds\": \"1\", \"nanos\": -1 }", duration.ToString());
         }
         }
+
+        [Test]
+        public void Comparability()
+        {
+            Timestamp
+                a = null,
+                b = new Timestamp { Seconds = 1, Nanos = 1 },
+                c = new Timestamp { Seconds = 1, Nanos = 10 },
+                d = new Timestamp { Seconds = 10, Nanos = 1 },
+                e = new Timestamp { Seconds = 10, Nanos = 10 };
+
+            Assert.IsTrue(b.CompareTo(a) > 0); // null is always first (according to default behavior of Array.Sort)
+            Assert.IsTrue(b.CompareTo(b) == 0);
+            Assert.IsTrue(b.CompareTo(b.Clone()) == 0);
+            Assert.IsTrue(b.CompareTo(c) < 0);
+            Assert.IsTrue(b.CompareTo(d) < 0);
+            Assert.IsTrue(b.CompareTo(e) < 0);
+
+            Assert.IsTrue(c.CompareTo(a) > 0);
+            Assert.IsTrue(c.CompareTo(b) > 0);
+            Assert.IsTrue(c.CompareTo(c) == 0);
+            Assert.IsTrue(c.CompareTo(c.Clone()) == 0);
+            Assert.IsTrue(c.CompareTo(d) < 0);
+            Assert.IsTrue(c.CompareTo(e) < 0);
+
+            Assert.IsTrue(d.CompareTo(a) > 0);
+            Assert.IsTrue(d.CompareTo(b) > 0);
+            Assert.IsTrue(d.CompareTo(c) > 0);
+            Assert.IsTrue(d.CompareTo(d) == 0);
+            Assert.IsTrue(d.CompareTo(d.Clone()) == 0);
+            Assert.IsTrue(d.CompareTo(e) < 0);
+
+            Assert.IsTrue(e.CompareTo(a) > 0);
+            Assert.IsTrue(e.CompareTo(b) > 0);
+            Assert.IsTrue(e.CompareTo(c) > 0);
+            Assert.IsTrue(e.CompareTo(d) > 0);
+            Assert.IsTrue(e.CompareTo(e) == 0);
+            Assert.IsTrue(e.CompareTo(e.Clone()) == 0);
+        }
+
+
+        [Test]
+        public void ComparabilityOperators()
+        {
+            Timestamp
+                a = null,
+                b = new Timestamp { Seconds = 1, Nanos = 1 },
+                c = new Timestamp { Seconds = 1, Nanos = 10 },
+                d = new Timestamp { Seconds = 10, Nanos = 1 },
+                e = new Timestamp { Seconds = 10, Nanos = 10 };
+
+#pragma warning disable CS1718 // Comparison made to same variable
+            Assert.IsTrue(b > a);
+            Assert.IsTrue(b == b);
+            Assert.IsTrue(b == b.Clone());
+            Assert.IsTrue(b < c);
+            Assert.IsTrue(b < d);
+            Assert.IsTrue(b < e);
+
+            Assert.IsTrue(c > a);
+            Assert.IsTrue(c > b);
+            Assert.IsTrue(c == c);
+            Assert.IsTrue(c == c.Clone());
+            Assert.IsTrue(c < d);
+            Assert.IsTrue(c < e);
+
+            Assert.IsTrue(d > a);
+            Assert.IsTrue(d > b);
+            Assert.IsTrue(d > c);
+            Assert.IsTrue(d == d);
+            Assert.IsTrue(d == d.Clone());
+            Assert.IsTrue(d < e);
+
+            Assert.IsTrue(e > a);
+            Assert.IsTrue(e > b);
+            Assert.IsTrue(e > c);
+            Assert.IsTrue(e > d);
+            Assert.IsTrue(e == e);
+            Assert.IsTrue(e == e.Clone());
+            
+            Assert.IsTrue(b >= a);
+            Assert.IsTrue(b <= c);
+            Assert.IsTrue(b <= d);
+            Assert.IsTrue(b <= e);
+
+            Assert.IsTrue(c >= a);
+            Assert.IsTrue(c >= b);
+            Assert.IsTrue(c <= d);
+            Assert.IsTrue(c <= e);
+
+            Assert.IsTrue(d >= a);
+            Assert.IsTrue(d >= b);
+            Assert.IsTrue(d >= c);
+            Assert.IsTrue(d <= e);
+
+            Assert.IsTrue(e >= a);
+            Assert.IsTrue(e >= b);
+            Assert.IsTrue(e >= c);
+            Assert.IsTrue(e >= d);
+#pragma warning restore CS1718 // Comparison made to same variable
+        }
     }
     }
 }
 }

+ 104 - 1
csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs

@@ -36,7 +36,7 @@ using System.Text;
 
 
 namespace Google.Protobuf.WellKnownTypes
 namespace Google.Protobuf.WellKnownTypes
 {
 {
-    public partial class Timestamp : ICustomDiagnosticMessage
+    public partial class Timestamp : ICustomDiagnosticMessage, IComparable<Timestamp>
     {
     {
         private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
         private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
         // Constants determined programmatically, but then hard-coded so they can be constant expressions.
         // Constants determined programmatically, but then hard-coded so they can be constant expressions.
@@ -222,6 +222,109 @@ namespace Google.Protobuf.WellKnownTypes
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Given another timestamp, returns 0 if the timestamps are equivalent, -1 if this timestamp precedes the other, and 1 otherwise
+        /// </summary>
+        /// <remarks>
+        /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+        /// </remarks>
+        /// <param name="other">Timestamp to compare</param>
+        /// <returns>an integer indicating whether this timestamp precedes or follows the other</returns>
+        public int CompareTo(Timestamp other)
+        {
+            return other == null ? 1
+                : Seconds < other.Seconds ? -1
+                : Seconds > other.Seconds ? 1
+                : Nanos < other.Nanos ? -1
+                : Nanos > other.Nanos ? 1
+                : 0;
+        }
+
+        /// <summary>
+        /// Compares two timestamps and returns whether the first is less than (chronologically precedes) the second
+        /// </summary>
+        /// <remarks>
+        /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+        /// </remarks>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        /// <returns>true if a precedes b</returns>
+        public static bool operator <(Timestamp a, Timestamp b)
+        {
+            return a.CompareTo(b) < 0;
+        }
+
+        /// <summary>
+        /// Compares two timestamps and returns whether the first is greater than (chronologically follows) the second
+        /// </summary>
+        /// <remarks>
+        /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+        /// </remarks>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        /// <returns>true if a follows b</returns>
+        public static bool operator >(Timestamp a, Timestamp b)
+        {
+            return a.CompareTo(b) > 0;
+        }
+
+        /// <summary>
+        /// Compares two timestamps and returns whether the first is less than (chronologically precedes) the second
+        /// </summary>
+        /// <remarks>
+        /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+        /// </remarks>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        /// <returns>true if a precedes b</returns>
+        public static bool operator <=(Timestamp a, Timestamp b)
+        {
+            return a.CompareTo(b) <= 0;
+        }
+
+        /// <summary>
+        /// Compares two timestamps and returns whether the first is greater than (chronologically follows) the second
+        /// </summary>
+        /// <remarks>
+        /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+        /// </remarks>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        /// <returns>true if a follows b</returns>
+        public static bool operator >=(Timestamp a, Timestamp b)
+        {
+            return a.CompareTo(b) >= 0;
+        }
+
+
+        /// <summary>
+        /// Returns whether two timestamps are equivalent
+        /// </summary>
+        /// <remarks>
+        /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+        /// </remarks>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        /// <returns>true if the two timestamps refer to the same nanosecond</returns>
+        public static bool operator ==(Timestamp a, Timestamp b)
+        {
+            return ReferenceEquals(a, b) || (a is null ? (b is null ? true : false) : a.Equals(b));
+        }
+
+        /// <summary>
+        /// Returns whether two timestamps differ
+        /// </summary>
+        /// <remarks>
+        /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+        /// </remarks>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        /// <returns>true if the two timestamps differ</returns>
+        public static bool operator !=(Timestamp a, Timestamp b)
+        {
+            return !(a == b);
+        }
+
         /// <summary>
         /// <summary>
         /// Returns a string representation of this <see cref="Timestamp"/> for diagnostic purposes.
         /// Returns a string representation of this <see cref="Timestamp"/> for diagnostic purposes.
         /// </summary>
         /// </summary>