Index: third_party/protobuf/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs |
diff --git a/third_party/protobuf/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs b/third_party/protobuf/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs |
index 324f48fc05a3557f5e31d63987191f0d2b3c0b5f..f164bfd19d2e792297c835b529b10d6aae0b1409 100644 |
--- a/third_party/protobuf/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs |
+++ b/third_party/protobuf/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs |
@@ -31,12 +31,14 @@ |
#endregion |
using System; |
+using System.Globalization; |
+using System.Text; |
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 partial class Duration : ICustomDiagnosticMessage |
{ |
/// <summary> |
/// The number of nanoseconds in a second. |
@@ -57,15 +59,37 @@ namespace Google.Protobuf.WellKnownTypes |
/// </summary> |
public const long MinSeconds = -315576000000L; |
+ internal const int MaxNanoseconds = NanosecondsPerSecond - 1; |
+ internal const int MinNanoseconds = -NanosecondsPerSecond + 1; |
+ |
+ internal static bool IsNormalized(long seconds, int nanoseconds) |
+ { |
+ // Simple boundaries |
+ if (seconds < MinSeconds || seconds > MaxSeconds || |
+ nanoseconds < MinNanoseconds || nanoseconds > MaxNanoseconds) |
+ { |
+ return false; |
+ } |
+ // We only have a problem is one is strictly negative and the other is |
+ // strictly positive. |
+ return Math.Sign(seconds) * Math.Sign(nanoseconds) != -1; |
+ } |
+ |
/// <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> |
+ /// <exception cref="InvalidOperationException">This value isn't a valid normalized duration, as |
+ /// described in the documentation.</exception> |
public TimeSpan ToTimeSpan() |
{ |
checked |
{ |
+ if (!IsNormalized(Seconds, Nanos)) |
+ { |
+ throw new InvalidOperationException("Duration was not a valid normalized duration"); |
+ } |
long ticks = Seconds * TimeSpan.TicksPerSecond + Nanos / NanosecondsPerTick; |
return TimeSpan.FromTicks(ticks); |
} |
@@ -94,7 +118,7 @@ namespace Google.Protobuf.WellKnownTypes |
/// <returns>The negated value of this duration.</returns> |
public static Duration operator -(Duration value) |
{ |
- Preconditions.CheckNotNull(value, "value"); |
+ ProtoPreconditions.CheckNotNull(value, "value"); |
checked |
{ |
return Normalize(-value.Seconds, -value.Nanos); |
@@ -109,8 +133,8 @@ namespace Google.Protobuf.WellKnownTypes |
/// <returns></returns> |
public static Duration operator +(Duration lhs, Duration rhs) |
{ |
- Preconditions.CheckNotNull(lhs, "lhs"); |
- Preconditions.CheckNotNull(rhs, "rhs"); |
+ ProtoPreconditions.CheckNotNull(lhs, "lhs"); |
+ ProtoPreconditions.CheckNotNull(rhs, "rhs"); |
checked |
{ |
return Normalize(lhs.Seconds + rhs.Seconds, lhs.Nanos + rhs.Nanos); |
@@ -125,8 +149,8 @@ namespace Google.Protobuf.WellKnownTypes |
/// <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"); |
+ ProtoPreconditions.CheckNotNull(lhs, "lhs"); |
+ ProtoPreconditions.CheckNotNull(rhs, "rhs"); |
checked |
{ |
return Normalize(lhs.Seconds - rhs.Seconds, lhs.Nanos - rhs.Nanos); |
@@ -157,5 +181,90 @@ namespace Google.Protobuf.WellKnownTypes |
} |
return new Duration { Seconds = seconds, Nanos = nanoseconds }; |
} |
+ |
+ /// <summary> |
+ /// Converts a duration specified in seconds/nanoseconds to a string. |
+ /// </summary> |
+ /// <remarks> |
+ /// If the value is a normalized duration in the range described in <c>duration.proto</c>, |
+ /// <paramref name="diagnosticOnly"/> is ignored. Otherwise, if the parameter is <c>true</c>, |
+ /// a JSON object with a warning is returned; if it is <c>false</c>, an <see cref="InvalidOperationException"/> is thrown. |
+ /// </remarks> |
+ /// <param name="seconds">Seconds portion of the duration.</param> |
+ /// <param name="nanoseconds">Nanoseconds portion of the duration.</param> |
+ /// <param name="diagnosticOnly">Determines the handling of non-normalized values</param> |
+ /// <exception cref="InvalidOperationException">The represented duration is invalid, and <paramref name="diagnosticOnly"/> is <c>false</c>.</exception> |
+ internal static string ToJson(long seconds, int nanoseconds, bool diagnosticOnly) |
+ { |
+ if (IsNormalized(seconds, nanoseconds)) |
+ { |
+ var builder = new StringBuilder(); |
+ builder.Append('"'); |
+ // The seconds part will normally provide the minus sign if we need it, but not if it's 0... |
+ if (seconds == 0 && nanoseconds < 0) |
+ { |
+ builder.Append('-'); |
+ } |
+ |
+ builder.Append(seconds.ToString("d", CultureInfo.InvariantCulture)); |
+ AppendNanoseconds(builder, Math.Abs(nanoseconds)); |
+ builder.Append("s\""); |
+ return builder.ToString(); |
+ } |
+ if (diagnosticOnly) |
+ { |
+ // Note: the double braces here are escaping for braces in format strings. |
+ return string.Format(CultureInfo.InvariantCulture, |
+ "{{ \"@warning\": \"Invalid Duration\", \"seconds\": \"{0}\", \"nanos\": {1} }}", |
+ seconds, |
+ nanoseconds); |
+ } |
+ else |
+ { |
+ throw new InvalidOperationException("Non-normalized duration value"); |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Returns a string representation of this <see cref="Duration"/> for diagnostic purposes. |
+ /// </summary> |
+ /// <remarks> |
+ /// Normally the returned value will be a JSON string value (including leading and trailing quotes) but |
+ /// when the value is non-normalized or out of range, a JSON object representation will be returned |
+ /// instead, including a warning. This is to avoid exceptions being thrown when trying to |
+ /// diagnose problems - the regular JSON formatter will still throw an exception for non-normalized |
+ /// values. |
+ /// </remarks> |
+ /// <returns>A string representation of this value.</returns> |
+ public string ToDiagnosticString() |
+ { |
+ return ToJson(Seconds, Nanos, true); |
+ } |
+ |
+ /// <summary> |
+ /// Appends a number of nanoseconds to a StringBuilder. Either 0 digits are added (in which |
+ /// case no "." is appended), or 3 6 or 9 digits. This is internal for use in Timestamp as well |
+ /// as Duration. |
+ /// </summary> |
+ internal static void AppendNanoseconds(StringBuilder builder, int nanos) |
+ { |
+ if (nanos != 0) |
+ { |
+ builder.Append('.'); |
+ // Output to 3, 6 or 9 digits. |
+ if (nanos % 1000000 == 0) |
+ { |
+ builder.Append((nanos / 1000000).ToString("d3", CultureInfo.InvariantCulture)); |
+ } |
+ else if (nanos % 1000 == 0) |
+ { |
+ builder.Append((nanos / 1000).ToString("d6", CultureInfo.InvariantCulture)); |
+ } |
+ else |
+ { |
+ builder.Append(nanos.ToString("d9", CultureInfo.InvariantCulture)); |
+ } |
+ } |
+ } |
} |
} |