Google Cloud Spanner C++ Client  1.32.0
A C++ Client Library for Google Cloud Spanner
timestamp.cc
Go to the documentation of this file.
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/spanner/timestamp.h"
16 #include "google/cloud/status.h"
17 #include <google/protobuf/util/time_util.h>
18 #include <string>
19 
20 namespace google {
21 namespace cloud {
22 namespace spanner {
23 inline namespace SPANNER_CLIENT_NS {
24 
25 namespace {
26 
27 Status OutOfRange(std::string message) {
28  return Status(StatusCode::kOutOfRange, std::move(message));
29 }
30 
31 Status PositiveOverflow(std::string const& type) {
32  return OutOfRange(type + " positive overflow");
33 }
34 
35 Status NegativeOverflow(std::string const& type) {
36  return OutOfRange(type + " negative overflow");
37 }
38 
39 } // namespace
40 
41 StatusOr<protobuf::Timestamp> Timestamp::ConvertTo(
42  protobuf::Timestamp const&) const {
43  auto constexpr kDestType = "google::protobuf::Timestamp";
44  auto const s = absl::ToUnixSeconds(t_);
45  if (s > google::protobuf::util::TimeUtil::kTimestampMaxSeconds)
46  return PositiveOverflow(kDestType);
47  if (s < google::protobuf::util::TimeUtil::kTimestampMinSeconds)
48  return NegativeOverflow(kDestType);
49  auto const ns = absl::ToInt64Nanoseconds(t_ - absl::FromUnixSeconds(s));
50  google::protobuf::Timestamp proto;
51  proto.set_seconds(s);
52  proto.set_nanos(static_cast<std::int32_t>(ns));
53  return proto;
54 }
55 
56 StatusOr<std::int64_t> Timestamp::ToRatio(std::int64_t min, std::int64_t max,
57  std::int64_t num,
58  std::int64_t den) const {
59  auto constexpr kDestType = "std::chrono::time_point";
60  auto const period = absl::Seconds(num) / den;
61  auto const duration = absl::Floor(t_ - absl::UnixEpoch(), period);
62  if (duration > max * period) return PositiveOverflow(kDestType);
63  if (duration < min * period) return NegativeOverflow(kDestType);
64  return duration / period;
65 }
66 
67 StatusOr<Timestamp> MakeTimestamp(absl::Time t) {
68  auto constexpr kDestType = "google::cloud::spanner::Timestamp";
69  auto constexpr kMinTime = absl::FromUnixSeconds(
70  google::protobuf::util::TimeUtil::kTimestampMinSeconds);
71  auto constexpr kMaxTime = absl::FromUnixSeconds(
72  google::protobuf::util::TimeUtil::kTimestampMaxSeconds + 1);
73  if (t >= kMaxTime) return PositiveOverflow(kDestType);
74  if (t < kMinTime) return NegativeOverflow(kDestType);
75  return Timestamp(t);
76 }
77 
78 StatusOr<Timestamp> MakeTimestamp(protobuf::Timestamp const& proto) {
79  return MakeTimestamp(absl::FromUnixSeconds(proto.seconds()) +
80  absl::Nanoseconds(proto.nanos()));
81 }
82 
83 std::ostream& operator<<(std::ostream& os, Timestamp ts) {
84  return os << spanner_internal::TimestampToRFC3339(ts);
85 }
86 
87 } // namespace SPANNER_CLIENT_NS
88 } // namespace spanner
89 
90 namespace spanner_internal {
91 inline namespace SPANNER_CLIENT_NS {
92 
93 namespace {
94 
95 // Timestamp objects are always formatted in UTC, and we always format them
96 // with a trailing 'Z'. However, we're a bit more liberal in the UTC offsets we
97 // accept, thus the use of '%Ez' in kParseSpec.
98 auto constexpr kFormatSpec = "%E4Y-%m-%d%ET%H:%M:%E*SZ";
99 auto constexpr kParseSpec = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
100 
101 Status InvalidArgument(std::string message) {
102  return Status(StatusCode::kInvalidArgument, std::move(message));
103 }
104 
105 } // namespace
106 
107 StatusOr<spanner::Timestamp> TimestampFromRFC3339(std::string const& s) {
108  absl::Time t;
109  std::string err;
110  if (absl::ParseTime(kParseSpec, s, &t, &err)) {
111  return spanner::MakeTimestamp(t);
112  }
113  return InvalidArgument(s + ": " + err);
114 }
115 
116 std::string TimestampToRFC3339(spanner::Timestamp ts) {
117  auto const t = ts.get<absl::Time>().value(); // Cannot fail.
118  return absl::FormatTime(kFormatSpec, t, absl::UTCTimeZone());
119 }
120 
121 } // namespace SPANNER_CLIENT_NS
122 } // namespace spanner_internal
123 } // namespace cloud
124 } // namespace google