Blame view

Pods/Realm/include/core/realm/timestamp.hpp 7.34 KB
75d24c15   yangbin   123
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  /*************************************************************************
   *
   * Copyright 2016 Realm Inc.
   *
   * 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 REALM_TIMESTAMP_HPP
  #define REALM_TIMESTAMP_HPP
  
  #include <cstdint>
  #include <ostream>
  #include <chrono>
  #include <ctime>
  #include <realm/util/assert.hpp>
  #include <realm/null.hpp>
  
  namespace realm {
  
  class Timestamp {
  public:
      // Construct from the number of seconds and nanoseconds since the UNIX epoch: 00:00:00 UTC on 1 January 1970
      //
      // To split a native nanosecond representation, only division and modulo are necessary:
      //
      //     s = native_nano / nanoseconds_per_second
      //     n = native_nano % nanoseconds_per_second
      //     Timestamp ts(s, n);
      //
      // To convert back into native nanosecond representation, simple multiply and add:
      //
      //     native_nano = ts.s * nanoseconds_per_second + ts.n
      //
      // Specifically this allows the nanosecond part to become negative (only) for Timestamps before the UNIX epoch.
      // Usually this will not need special attention, but for reference, valid Timestamps will have one of the
      // following sign combinations:
      //
      //     s | n
      //     -----
      //     + | +
      //     + | 0
      //     0 | +
      //     0 | 0
      //     0 | -
      //     - | 0
      //     - | -
      //
      // Examples:
      //     The UNIX epoch is constructed by Timestamp(0, 0)
      //     Relative times are constructed as follows:
      //       +1 second is constructed by Timestamp(1, 0)
      //       +1 nanosecond is constructed by Timestamp(0, 1)
      //       +1.1 seconds (1100 milliseconds after the epoch) is constructed by Timestamp(1, 100000000)
      //       -1.1 seconds (1100 milliseconds before the epoch) is constructed by Timestamp(-1, -100000000)
      //
      Timestamp(int64_t seconds, int32_t nanoseconds)
          : m_seconds(seconds)
          , m_nanoseconds(nanoseconds)
          , m_is_null(false)
      {
          REALM_ASSERT_EX(-nanoseconds_per_second < nanoseconds && nanoseconds < nanoseconds_per_second, nanoseconds);
          const bool both_non_negative = seconds >= 0 && nanoseconds >= 0;
          const bool both_non_positive = seconds <= 0 && nanoseconds <= 0;
          REALM_ASSERT_EX(both_non_negative || both_non_positive, both_non_negative, both_non_positive);
      }
      Timestamp(realm::null)
          : m_is_null(true)
      {
      }
      template <typename C = std::chrono::system_clock, typename D = typename C::duration>
      Timestamp(std::chrono::time_point<C, D> tp)
          : m_is_null(false)
      {
          int64_t native_nano = std::chrono::duration_cast<std::chrono::nanoseconds>(tp.time_since_epoch()).count();
          m_seconds = native_nano / nanoseconds_per_second;
          m_nanoseconds = static_cast<int32_t>(native_nano % nanoseconds_per_second);
      }
      Timestamp()
          : Timestamp(null{})
      {
      }
  
      bool is_null() const
      {
          return m_is_null;
      }
  
      int64_t get_seconds() const noexcept
      {
          REALM_ASSERT(!m_is_null);
          return m_seconds;
      }
  
      int32_t get_nanoseconds() const noexcept
      {
          REALM_ASSERT(!m_is_null);
          return m_nanoseconds;
      }
  
      template <typename C = std::chrono::system_clock, typename D = typename C::duration>
      std::chrono::time_point<C, D> get_time_point() const
      {
          REALM_ASSERT(!m_is_null);
  
          int64_t native_nano = m_seconds * nanoseconds_per_second + m_nanoseconds;
          auto duration = std::chrono::duration_cast<D>(std::chrono::duration<int64_t, std::nano>{native_nano});
  
          return std::chrono::time_point<C, D>(duration);
      }
  
      template <typename C = std::chrono::system_clock, typename D = typename C::duration>
      explicit operator std::chrono::time_point<C, D>() const
      {
          return get_time_point();
      }
  
      bool operator==(const Timestamp& rhs) const
      {
          if (is_null() && rhs.is_null())
              return true;
  
          if (is_null() != rhs.is_null())
              return false;
  
          return m_seconds == rhs.m_seconds && m_nanoseconds == rhs.m_nanoseconds;
      }
      bool operator!=(const Timestamp& rhs) const
      {
          return !(*this == rhs);
      }
      bool operator>(const Timestamp& rhs) const
      {
          if (is_null()) {
              return false;
          }
          if (rhs.is_null()) {
              return true;
          }
          return (m_seconds > rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds > rhs.m_nanoseconds);
      }
      bool operator<(const Timestamp& rhs) const
      {
          if (rhs.is_null()) {
              return false;
          }
          if (is_null()) {
              return true;
          }
          return (m_seconds < rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds < rhs.m_nanoseconds);
      }
      bool operator<=(const Timestamp& rhs) const
      {
          if (is_null()) {
              return true;
          }
          if (rhs.is_null()) {
              return false;
          }
          return *this < rhs || *this == rhs;
      }
      bool operator>=(const Timestamp& rhs) const
      {
          if (rhs.is_null()) {
              return true;
          }
          if (is_null()) {
              return false;
          }
          return *this > rhs || *this == rhs;
      }
      Timestamp& operator=(const Timestamp& rhs) = default;
  
      size_t hash() const noexcept;
  
      template <class Ch, class Tr>
      friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& out, const Timestamp&);
      static constexpr int32_t nanoseconds_per_second = 1000000000;
  
  private:
      int64_t m_seconds;
      int32_t m_nanoseconds;
      bool m_is_null;
  };
  
  // LCOV_EXCL_START
  template <class C, class T>
  inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const Timestamp& d)
  {
      auto seconds = time_t(d.get_seconds());
      struct tm buf;
  #ifdef _MSC_VER
      bool success = gmtime_s(&buf, &seconds) == 0;
  #else
      bool success = gmtime_r(&seconds, &buf) != nullptr;
  #endif
      if (success) {
          // We need a buffer for formatting dates.
          // Max size is 20 bytes (incl terminating zero) "YYYY-MM-DD HH:MM:SS"\0
          char buffer[30];
          if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &buf)) {
              out << buffer;
          }
      }
  
      return out;
  }
  // LCOV_EXCL_STOP
  
  inline size_t Timestamp::hash() const noexcept
  {
      return size_t(m_seconds) ^ size_t(m_nanoseconds);
  }
  
  } // namespace realm
  
  namespace std {
  template <>
  struct numeric_limits<realm::Timestamp> {
      static constexpr bool is_integer = false;
      static realm::Timestamp min()
      {
          return realm::Timestamp(numeric_limits<int64_t>::min(), 0);
      }
      static realm::Timestamp lowest()
      {
          return realm::Timestamp(numeric_limits<int64_t>::lowest(), 0);
      }
      static realm::Timestamp max()
      {
          return realm::Timestamp(numeric_limits<int64_t>::max(), 0);
      }
  };
  }
  
  #endif // REALM_TIMESTAMP_HPP