#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; internal static readonly long UnixSecondsAtBclMinValue = -BclSecondsAtUnixEpoch; internal static readonly long UnixSecondsAtBclMaxValue = (DateTime.MaxValue.Ticks / TimeSpan.TicksPerSecond) - BclSecondsAtUnixEpoch; /// /// Returns the difference between one and another, as a . /// /// The timestamp to subtract from. Must not be null. /// The timestamp to subtract. Must not be null. /// The difference between the two specified timestamps. 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); } } /// /// Adds a to a , to obtain another Timestamp. /// /// The timestamp to add the duration to. Must not be null. /// The duration to add. Must not be null. /// The result of adding the duration to the timestamp. 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); } } /// /// Subtracts a from a , to obtain another Timestamp. /// /// The timestamp to subtract the duration from. Must not be null. /// The duration to subtract. /// The result of subtracting the duration from the timestamp. 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); } } /// /// Converts this timestamp into a . /// /// /// The resulting DateTime will always have a Kind of Utc. /// 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 value of 99 will result in a /// value precisely on a second. /// /// This timestamp as a DateTime. public DateTime ToDateTime() { return UnixEpoch.AddSeconds(Seconds).AddTicks(Nanos / Duration.NanosecondsPerTick); } /// /// Converts this timestamp into a . /// /// /// The resulting DateTimeOffset will always have an Offset 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 value of 99 will result in a /// value precisely on a second. /// /// This timestamp as a DateTimeOffset. public DateTimeOffset ToDateTimeOffset() { return new DateTimeOffset(ToDateTime(), TimeSpan.Zero); } /// /// Converts the specified to a . /// /// /// The Kind of is not DateTimeKind.Utc. /// The converted timestamp. 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 }; } /// /// Converts the given to a /// /// The offset is taken into consideration when converting the value (so the same instant in time /// is represented) but is not a separate part of the resulting value. In other words, there is no /// roundtrip operation to retrieve the original DateTimeOffset. /// The date and time (with UTC offset) to convert to a timestamp. /// The converted timestamp. 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); } internal 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 }; } } }