timestamp.cc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /*
  2. * Copyright 2018 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "Firestore/core/include/firebase/firestore/timestamp.h"
  17. #include <ostream>
  18. #if defined(__APPLE__)
  19. #import <CoreFoundation/CoreFoundation.h>
  20. #elif defined(_STLPORT_VERSION)
  21. #include <ctime>
  22. #endif
  23. #include "Firestore/core/src/util/hard_assert.h"
  24. #include "absl/strings/str_cat.h"
  25. namespace firebase {
  26. namespace {
  27. constexpr int32_t kNanosPerSecond = 1000 * 1000 * 1000;
  28. /**
  29. * Creates a `Timestamp` from the given non-normalized inputs.
  30. *
  31. * Timestamp protos require `Timestamp` to always has a positive number of
  32. * nanoseconds that is counting forward. For negative time, we need to adjust
  33. * representations with negative nanoseconds. That is, make (negative seconds s1
  34. * + negative nanoseconds ns1) into (negative seconds s2 + positive nanoseconds
  35. * ns2). Since nanosecond part is always less than 1 second in our
  36. * representation, instead of starting at s1 and going back ns1 nanoseconds,
  37. * start at (s1 minus one second) and go *forward* ns2 = (1 second + ns1, ns1 <
  38. * 0) nanoseconds.
  39. */
  40. Timestamp MakeNormalizedTimestamp(int64_t seconds, int64_t nanos) {
  41. if (nanos < 0) {
  42. // Note: if nanoseconds are negative, it must mean that seconds are
  43. // non-positive, but the formula would still be valid, so no need to check.
  44. seconds = seconds - 1;
  45. nanos = kNanosPerSecond + nanos;
  46. }
  47. HARD_ASSERT(nanos < kNanosPerSecond);
  48. return {seconds, static_cast<int32_t>(nanos)};
  49. }
  50. } // namespace
  51. Timestamp::Timestamp(int64_t seconds, int32_t nanoseconds)
  52. : seconds_(seconds), nanoseconds_(nanoseconds) {
  53. ValidateBounds();
  54. }
  55. Timestamp Timestamp::Now() {
  56. #if defined(__APPLE__)
  57. // Originally, FIRTimestamp used NSDate to get current time. This method
  58. // preserves the lower accuracy of that method.
  59. CFAbsoluteTime now =
  60. CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970;
  61. double seconds_double;
  62. double fraction = modf(now, &seconds_double);
  63. auto seconds = static_cast<int64_t>(seconds_double);
  64. auto nanos = static_cast<int32_t>(fraction * kNanosPerSecond);
  65. return MakeNormalizedTimestamp(seconds, nanos);
  66. #elif !defined(_STLPORT_VERSION)
  67. // Use the standard <chrono> library from C++11 if possible.
  68. return FromTimePoint(std::chrono::system_clock::now());
  69. #else
  70. // If <chrono> is unavailable, use clock_gettime from POSIX, which supports
  71. // up to nanosecond resolution. Note that it's a non-standard function
  72. // contained in <time.h>.
  73. //
  74. // Note: it's possible to check for availability of POSIX clock_gettime using
  75. // macros (see "Availability" at https://linux.die.net/man/3/clock_gettime).
  76. // However, the only platform where <chrono> isn't available is Android with
  77. // STLPort standard library, where clock_gettime is known to be available.
  78. timespec now;
  79. clock_gettime(CLOCK_REALTIME, &now);
  80. return MakeNormalizedTimestamp(now.tv_sec, now.tv_nsec);
  81. #endif // !defined(_STLPORT_VERSION)
  82. }
  83. Timestamp Timestamp::FromTimeT(const time_t seconds_since_unix_epoch) {
  84. return {seconds_since_unix_epoch, 0};
  85. }
  86. #if !defined(_STLPORT_VERSION)
  87. Timestamp Timestamp::FromTimePoint(
  88. const std::chrono::time_point<std::chrono::system_clock> time_point) {
  89. namespace chr = std::chrono;
  90. const auto epoch_time = time_point.time_since_epoch();
  91. auto seconds = chr::duration_cast<chr::duration<int64_t>>(epoch_time);
  92. auto nanos = chr::duration_cast<chr::nanoseconds>(epoch_time - seconds);
  93. Timestamp result = MakeNormalizedTimestamp(seconds.count(), nanos.count());
  94. result.ValidateBounds();
  95. return result;
  96. }
  97. #endif // !defined(_STLPORT_VERSION)
  98. std::string Timestamp::ToString() const {
  99. return absl::StrCat("Timestamp(seconds=", seconds_,
  100. ", nanoseconds=", nanoseconds_, ")");
  101. }
  102. std::ostream& operator<<(std::ostream& out, const Timestamp& timestamp) {
  103. return out << timestamp.ToString();
  104. }
  105. void Timestamp::ValidateBounds() const {
  106. HARD_ASSERT(nanoseconds_ >= 0, "Timestamp nanoseconds out of range: %s",
  107. nanoseconds_);
  108. HARD_ASSERT(nanoseconds_ < kNanosPerSecond,
  109. "Timestamp nanoseconds out of range: %s", nanoseconds_);
  110. // Midnight at the beginning of 1/1/1 is the earliest timestamp Firestore
  111. // supports.
  112. HARD_ASSERT(seconds_ >= -62135596800L, "Timestamp seconds out of range: %s",
  113. seconds_);
  114. // This will break in the year 10,000.
  115. HARD_ASSERT(seconds_ < 253402300800L, "Timestamp seconds out of range: %s",
  116. seconds_);
  117. }
  118. } // namespace firebase