// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_TIMESTAMP_H #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_TIMESTAMP_H #include "google/cloud/spanner/version.h" #include "google/cloud/status_or.h" #include "absl/time/time.h" #include #include #include #include #include #include namespace google { namespace cloud { namespace spanner { inline namespace SPANNER_CLIENT_NS { /** * Convenience alias. `std::chrono::sys_time` since C++20. */ template using sys_time = std::chrono::time_point; /** * A representation of the Spanner TIMESTAMP type: An instant in time. * * A `Timestamp` represents an absolute point in time (i.e., is independent of * any time zone), with at least nanosecond precision, and with a range of * 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z, inclusive. * * The `MakeTimestamp(src)` factory function(s) should be used to construct * `Timestamp` values from standard representations of absolute time. * * A `Timestamp` can be converted back to a standard representation using * `ts.get()`. * * @see https://cloud.google.com/spanner/docs/data-types#timestamp_type */ class Timestamp { public: /// Default construction yields 1970-01-01T00:00:00Z. Timestamp() : Timestamp(absl::UnixEpoch()) {} /// @name Regular value type, supporting copy, assign, move. ///@{ Timestamp(Timestamp&&) = default; Timestamp& operator=(Timestamp&&) = default; Timestamp(Timestamp const&) = default; Timestamp& operator=(Timestamp const&) = default; ///@} /// @name Relational operators ///@{ friend bool operator==(Timestamp const& a, Timestamp const& b) { return a.t_ == b.t_; } friend bool operator!=(Timestamp const& a, Timestamp const& b) { return !(a == b); } friend bool operator<(Timestamp const& a, Timestamp const& b) { return a.t_ < b.t_; } friend bool operator<=(Timestamp const& a, Timestamp const& b) { return !(b < a); } friend bool operator>=(Timestamp const& a, Timestamp const& b) { return !(a < b); } friend bool operator>(Timestamp const& a, Timestamp const& b) { return b < a; } ///@} /// @name Output streaming friend std::ostream& operator<<(std::ostream& os, Timestamp ts); /** * Convert the `Timestamp` to the user-specified template type. Fails if * `*this` cannot be represented as a `T`. * * Supported destination types are: * - `google::cloud::spanner::sys_time` - `Duration::rep` may not * be wider than `std::int64_t`, and `Duration::period` may be no more * precise than `std::nano`. * - `absl::Time` - Since `absl::Time` can represent all possible * `Timestamp` values, `get()` never returns an error. * * @par Example * * @code * sys_time tp = ...; * Timestamp ts = MakeTimestamp(tp).value(); * assert(tp == ts.get>().value()); * @endcode */ template StatusOr get() const { return ConvertTo(T{}); } private: friend StatusOr MakeTimestamp(absl::Time); StatusOr ToRatio(std::int64_t min, std::int64_t max, std::int64_t num, std::int64_t den) const; // Conversion to a `std::chrono::time_point` on the system clock. May // produce out-of-range errors, depending on the properties of `Duration` // and the `std::chrono::system_clock` epoch. template StatusOr> ConvertTo(sys_time const&) const { using Rep = typename Duration::rep; using Period = typename Duration::period; static_assert(std::ratio_greater_equal::value, "Duration must be no more precise than std::nano"); auto count = ToRatio(std::numeric_limits::min(), std::numeric_limits::max(), Period::num, Period::den); if (!count) return std::move(count).status(); auto const unix_epoch = std::chrono::time_point_cast( sys_time::clock::from_time_t(0)); return unix_epoch + Duration(static_cast(*count)); } // Conversion to an `absl::Time`. Can never fail. StatusOr ConvertTo(absl::Time) const { return t_; } explicit Timestamp(absl::Time t) : t_(t) {} absl::Time t_; }; /** * Construct a `Timestamp` from an `absl::Time`. May produce out-of-range * errors if the given time is beyond the range supported by `Timestamp` (see * class comments above). */ StatusOr MakeTimestamp(absl::Time); /** * Construct a `Timestamp` from a `std::chrono::time_point` on the system * clock. May produce out-of-range errors, depending on the properties of * `Duration` and the `std::chrono::system_clock` epoch. `Duration::rep` may * not be wider than `std::int64_t`. Requires that `Duration::period` is no * more precise than `std::nano`. */ template StatusOr MakeTimestamp(sys_time const& tp) { using Period = typename Duration::period; static_assert(std::ratio_greater_equal::value, "Duration must be no more precise than std::nano"); auto const unix_epoch = std::chrono::time_point_cast( sys_time::clock::from_time_t(0)); auto const period = absl::Seconds(Period::num) / Period::den; auto const count = (tp - unix_epoch).count(); return MakeTimestamp(absl::UnixEpoch() + count * period); } /** * A sentinel type used to update a commit timestamp column. * * @see https://cloud.google.com/spanner/docs/commit-timestamp */ struct CommitTimestamp { friend bool operator==(CommitTimestamp, CommitTimestamp) { return true; } friend bool operator!=(CommitTimestamp, CommitTimestamp) { return false; } }; namespace internal { StatusOr TimestampFromRFC3339(std::string const&); std::string TimestampToRFC3339(Timestamp); StatusOr TimestampFromProto(protobuf::Timestamp const&); protobuf::Timestamp TimestampToProto(Timestamp); } // namespace internal } // namespace SPANNER_CLIENT_NS } // namespace spanner } // namespace cloud } // namespace google #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_TIMESTAMP_H