timestamp.hpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*************************************************************************
  2. *
  3. * Copyright 2016 Realm Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. **************************************************************************/
  18. #ifndef REALM_TIMESTAMP_HPP
  19. #define REALM_TIMESTAMP_HPP
  20. #include <array>
  21. #include <cstdint>
  22. #include <ostream>
  23. #include <chrono>
  24. #include <ctime>
  25. #include <realm/util/assert.hpp>
  26. #include <realm/null.hpp>
  27. namespace realm {
  28. class Timestamp {
  29. public:
  30. // Construct from the number of seconds and nanoseconds since the UNIX epoch: 00:00:00 UTC on 1 January 1970
  31. //
  32. // To split a native nanosecond representation, only division and modulo are necessary:
  33. //
  34. // s = native_nano / nanoseconds_per_second
  35. // n = native_nano % nanoseconds_per_second
  36. // Timestamp ts(s, n);
  37. //
  38. // To convert back into native nanosecond representation, simple multiply and add:
  39. //
  40. // native_nano = ts.s * nanoseconds_per_second + ts.n
  41. //
  42. // Specifically this allows the nanosecond part to become negative (only) for Timestamps before the UNIX epoch.
  43. // Usually this will not need special attention, but for reference, valid Timestamps will have one of the
  44. // following sign combinations:
  45. //
  46. // s | n
  47. // -----
  48. // + | +
  49. // + | 0
  50. // 0 | +
  51. // 0 | 0
  52. // 0 | -
  53. // - | 0
  54. // - | -
  55. //
  56. // Examples:
  57. // The UNIX epoch is constructed by Timestamp(0, 0)
  58. // Relative times are constructed as follows:
  59. // +1 second is constructed by Timestamp(1, 0)
  60. // +1 nanosecond is constructed by Timestamp(0, 1)
  61. // +1.1 seconds (1100 milliseconds after the epoch) is constructed by Timestamp(1, 100000000)
  62. // -1.1 seconds (1100 milliseconds before the epoch) is constructed by Timestamp(-1, -100000000)
  63. //
  64. constexpr Timestamp(int64_t seconds, int32_t nanoseconds)
  65. : m_seconds(seconds)
  66. , m_nanoseconds(nanoseconds)
  67. , m_is_null(false)
  68. {
  69. REALM_ASSERT_EX(-nanoseconds_per_second < nanoseconds && nanoseconds < nanoseconds_per_second, nanoseconds);
  70. const bool both_non_negative = seconds >= 0 && nanoseconds >= 0;
  71. const bool both_non_positive = seconds <= 0 && nanoseconds <= 0;
  72. REALM_ASSERT_EX(both_non_negative || both_non_positive, both_non_negative, both_non_positive);
  73. }
  74. constexpr Timestamp() = default;
  75. constexpr Timestamp(realm::null) {}
  76. constexpr Timestamp(const Timestamp&) = default;
  77. constexpr Timestamp& operator=(const Timestamp&) = default;
  78. constexpr Timestamp(std::chrono::time_point<std::chrono::system_clock, std::chrono::system_clock::duration> tp)
  79. : m_is_null(false)
  80. {
  81. int64_t native_nano = std::chrono::duration_cast<std::chrono::nanoseconds>(tp.time_since_epoch()).count();
  82. m_seconds = native_nano / nanoseconds_per_second;
  83. m_nanoseconds = static_cast<int32_t>(native_nano % nanoseconds_per_second);
  84. }
  85. constexpr bool is_null() const
  86. {
  87. return m_is_null;
  88. }
  89. constexpr int64_t get_seconds() const noexcept
  90. {
  91. REALM_ASSERT(!m_is_null);
  92. return m_seconds;
  93. }
  94. constexpr int32_t get_nanoseconds() const noexcept
  95. {
  96. REALM_ASSERT(!m_is_null);
  97. return m_nanoseconds;
  98. }
  99. template <typename C = std::chrono::system_clock, typename D = typename C::duration>
  100. constexpr std::chrono::time_point<C, D> get_time_point() const
  101. {
  102. REALM_ASSERT(!m_is_null);
  103. int64_t native_nano = m_seconds * nanoseconds_per_second + m_nanoseconds;
  104. auto duration = std::chrono::duration_cast<D>(std::chrono::duration<int64_t, std::nano>{native_nano});
  105. return std::chrono::time_point<C, D>(duration);
  106. }
  107. template <typename C = std::chrono::system_clock, typename D = typename C::duration>
  108. constexpr explicit operator std::chrono::time_point<C, D>() const
  109. {
  110. return get_time_point();
  111. }
  112. constexpr bool operator==(const Timestamp& rhs) const
  113. {
  114. if (is_null() && rhs.is_null())
  115. return true;
  116. if (is_null() != rhs.is_null())
  117. return false;
  118. return m_seconds == rhs.m_seconds && m_nanoseconds == rhs.m_nanoseconds;
  119. }
  120. constexpr bool operator!=(const Timestamp& rhs) const
  121. {
  122. return !(*this == rhs);
  123. }
  124. constexpr bool operator>(const Timestamp& rhs) const
  125. {
  126. if (is_null()) {
  127. return false;
  128. }
  129. if (rhs.is_null()) {
  130. return true;
  131. }
  132. return (m_seconds > rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds > rhs.m_nanoseconds);
  133. }
  134. constexpr bool operator<(const Timestamp& rhs) const
  135. {
  136. if (rhs.is_null()) {
  137. return false;
  138. }
  139. if (is_null()) {
  140. return true;
  141. }
  142. return (m_seconds < rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds < rhs.m_nanoseconds);
  143. }
  144. constexpr bool operator<=(const Timestamp& rhs) const
  145. {
  146. if (is_null()) {
  147. return true;
  148. }
  149. if (rhs.is_null()) {
  150. return false;
  151. }
  152. return *this < rhs || *this == rhs;
  153. }
  154. constexpr bool operator>=(const Timestamp& rhs) const
  155. {
  156. if (rhs.is_null()) {
  157. return true;
  158. }
  159. if (is_null()) {
  160. return false;
  161. }
  162. return *this > rhs || *this == rhs;
  163. }
  164. constexpr size_t hash() const noexcept
  165. {
  166. return size_t(m_seconds) ^ size_t(m_nanoseconds);
  167. }
  168. const char* to_string(std::array<char, 32>& buffer) const;
  169. template <class Ch, class Tr>
  170. friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& out, const Timestamp&);
  171. static constexpr int32_t nanoseconds_per_second = 1000000000;
  172. private:
  173. int64_t m_seconds = 0;
  174. int32_t m_nanoseconds = 0;
  175. bool m_is_null = true;
  176. };
  177. // LCOV_EXCL_START
  178. template <class C, class T>
  179. inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const Timestamp& d)
  180. {
  181. std::array<char, 32> buffer{};
  182. out << d.to_string(buffer);
  183. return out;
  184. }
  185. // LCOV_EXCL_STOP
  186. } // namespace realm
  187. namespace std {
  188. template <>
  189. struct numeric_limits<realm::Timestamp> {
  190. static constexpr bool is_integer = false;
  191. static constexpr realm::Timestamp min()
  192. {
  193. return realm::Timestamp(numeric_limits<int64_t>::min(), 0);
  194. }
  195. static constexpr realm::Timestamp lowest()
  196. {
  197. return realm::Timestamp(numeric_limits<int64_t>::lowest(), 0);
  198. }
  199. static constexpr realm::Timestamp max()
  200. {
  201. return realm::Timestamp(numeric_limits<int64_t>::max(), 0);
  202. }
  203. };
  204. }
  205. #endif // REALM_TIMESTAMP_HPP