diff options
author | Jon Skeet <jonskeet@google.com> | 2016-01-15 11:39:27 +0000 |
---|---|---|
committer | Jon Skeet <jonskeet@google.com> | 2016-01-15 11:39:27 +0000 |
commit | 1fc485928fc7a6483b700867f1a6cb2acfa8da5d (patch) | |
tree | 7175437a631dcefc48d1bf91f0bbe81c632295e2 /csharp/src/Google.Protobuf/WellKnownTypes | |
parent | c74676f07037acca34e9df0fb868b29afae15ac9 (diff) | |
download | protobuf-1fc485928fc7a6483b700867f1a6cb2acfa8da5d.tar.gz protobuf-1fc485928fc7a6483b700867f1a6cb2acfa8da5d.tar.bz2 protobuf-1fc485928fc7a6483b700867f1a6cb2acfa8da5d.zip |
Fixes to JSON timestamp/duration representations
Diffstat (limited to 'csharp/src/Google.Protobuf/WellKnownTypes')
-rw-r--r-- | csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs | 23 | ||||
-rw-r--r-- | csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs | 22 |
2 files changed, 42 insertions, 3 deletions
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs index 324f48fc..b8eba9d3 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs @@ -57,15 +57,38 @@ 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); } diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs index d284acd6..7c50a3d7 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs @@ -37,9 +37,17 @@ 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; - internal static readonly long UnixSecondsAtBclMinValue = -BclSecondsAtUnixEpoch; - internal static readonly long UnixSecondsAtBclMaxValue = (DateTime.MaxValue.Ticks / TimeSpan.TicksPerSecond) - BclSecondsAtUnixEpoch; + // Constants determined programmatically, but then hard-coded so they can be constant expressions. + private const long BclSecondsAtUnixEpoch = 62135596800; + internal const long UnixSecondsAtBclMaxValue = 253402300799; + internal const long UnixSecondsAtBclMinValue = -BclSecondsAtUnixEpoch; + internal const int MaxNanos = Duration.NanosecondsPerSecond - 1; + + private bool IsNormalized => + Nanos >= 0 && + Nanos <= MaxNanos && + Seconds >= UnixSecondsAtBclMinValue && + Seconds <= UnixSecondsAtBclMaxValue; /// <summary> /// Returns the difference between one <see cref="Timestamp"/> and another, as a <see cref="Duration"/>. @@ -99,8 +107,14 @@ namespace Google.Protobuf.WellKnownTypes /// <see cref="DateTime"/> value precisely on a second. /// </remarks> /// <returns>This timestamp as a <c>DateTime</c>.</returns> + /// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is + /// incorrectly normalized or is outside the valid range.</exception> public DateTime ToDateTime() { + if (!IsNormalized) + { + throw new InvalidOperationException(@"Timestamp contains invalid values: Seconds={Seconds}; Nanos={Nanos}"); + } return UnixEpoch.AddSeconds(Seconds).AddTicks(Nanos / Duration.NanosecondsPerTick); } @@ -114,6 +128,8 @@ namespace Google.Protobuf.WellKnownTypes /// <see cref="DateTimeOffset"/> value precisely on a second. /// </remarks> /// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns> + /// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is + /// incorrectly normalized or is outside the valid range.</exception> public DateTimeOffset ToDateTimeOffset() { return new DateTimeOffset(ToDateTime(), TimeSpan.Zero); |