1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/util/time_util.h"
9
10 #include <cstdint>
11 #include <cstdlib>
12
13 #include "google/protobuf/duration.pb.h"
14 #include "google/protobuf/timestamp.pb.h"
15 #include "absl/log/absl_check.h"
16 #include "absl/numeric/int128.h"
17 #include "absl/strings/str_cat.h"
18 #include "absl/strings/str_format.h"
19 #include "absl/strings/string_view.h"
20 #include "absl/time/clock.h"
21 #include "absl/time/time.h"
22
23 // Must go after other includes.
24 #include "google/protobuf/port_def.inc"
25
26 namespace google {
27 namespace protobuf {
28 namespace util {
29
30 using google::protobuf::Duration;
31 using google::protobuf::Timestamp;
32
33 namespace {
34 static constexpr int32_t kNanosPerSecond = 1000000000;
35 static constexpr int32_t kMicrosPerSecond = 1000000;
36 static constexpr int32_t kMillisPerSecond = 1000;
37 static constexpr int32_t kNanosPerMillisecond = 1000000;
38 static constexpr int32_t kNanosPerMicrosecond = 1000;
39 static constexpr int32_t kSecondsPerMinute =
40 60; // Note that we ignore leap seconds.
41 static constexpr int32_t kSecondsPerHour = 3600;
42
43 template <typename T>
44 T CreateNormalized(int64_t seconds, int32_t nanos);
45
46 template <>
CreateNormalized(int64_t seconds,int32_t nanos)47 Timestamp CreateNormalized(int64_t seconds, int32_t nanos) {
48 ABSL_DCHECK(seconds >= TimeUtil::kTimestampMinSeconds &&
49 seconds <= TimeUtil::kTimestampMaxSeconds)
50 << "Timestamp seconds are outside of the valid range";
51
52 // Make sure nanos is in the range.
53 if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
54 seconds += nanos / kNanosPerSecond;
55 nanos = nanos % kNanosPerSecond;
56 }
57 // For Timestamp nanos should be in the range [0, 999999999]
58 if (nanos < 0) {
59 seconds -= 1;
60 nanos += kNanosPerSecond;
61 }
62
63 ABSL_DCHECK(seconds >= TimeUtil::kTimestampMinSeconds &&
64 seconds <= TimeUtil::kTimestampMaxSeconds &&
65 nanos >= TimeUtil::kTimestampMinNanoseconds &&
66 nanos <= TimeUtil::kTimestampMaxNanoseconds)
67 << "Timestamp is outside of the valid range";
68 Timestamp result;
69 result.set_seconds(seconds);
70 result.set_nanos(static_cast<int32_t>(nanos));
71 return result;
72 }
73
74 template <>
CreateNormalized(int64_t seconds,int32_t nanos)75 Duration CreateNormalized(int64_t seconds, int32_t nanos) {
76 ABSL_DCHECK(seconds >= TimeUtil::kDurationMinSeconds &&
77 seconds <= TimeUtil::kDurationMaxSeconds)
78 << "Duration seconds are outside of the valid range";
79
80 // Make sure nanos is in the range.
81 if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
82 seconds += nanos / kNanosPerSecond;
83 nanos = nanos % kNanosPerSecond;
84 }
85 // nanos should have the same sign as seconds.
86 if (seconds < 0 && nanos > 0) {
87 seconds += 1;
88 nanos -= kNanosPerSecond;
89 } else if (seconds > 0 && nanos < 0) {
90 seconds -= 1;
91 nanos += kNanosPerSecond;
92 }
93
94 ABSL_DCHECK(seconds >= TimeUtil::kDurationMinSeconds &&
95 seconds <= TimeUtil::kDurationMaxSeconds &&
96 nanos >= TimeUtil::kDurationMinNanoseconds &&
97 nanos <= TimeUtil::kDurationMaxNanoseconds)
98 << "Duration is outside of the valid range";
99 Duration result;
100 result.set_seconds(seconds);
101 result.set_nanos(static_cast<int32_t>(nanos));
102 return result;
103 }
104
105 // Format nanoseconds with either 3, 6, or 9 digits depending on the required
106 // precision to represent the exact value.
FormatNanos(int32_t nanos)107 std::string FormatNanos(int32_t nanos) {
108 if (nanos % kNanosPerMillisecond == 0) {
109 return absl::StrFormat("%03d", nanos / kNanosPerMillisecond);
110 } else if (nanos % kNanosPerMicrosecond == 0) {
111 return absl::StrFormat("%06d", nanos / kNanosPerMicrosecond);
112 } else {
113 return absl::StrFormat("%09d", nanos);
114 }
115 }
116
FormatTime(int64_t seconds,int32_t nanos)117 std::string FormatTime(int64_t seconds, int32_t nanos) {
118 static constexpr absl::string_view kTimestampFormat = "%E4Y-%m-%dT%H:%M:%S";
119
120 timespec spec;
121 spec.tv_sec = seconds;
122 // We only use absl::FormatTime to format the seconds part because we need
123 // finer control over the precision of nanoseconds.
124 spec.tv_nsec = 0;
125 std::string result = absl::FormatTime(
126 kTimestampFormat, absl::TimeFromTimespec(spec), absl::UTCTimeZone());
127 // We format the nanoseconds part separately to meet the precision
128 // requirement.
129 if (nanos != 0) {
130 absl::StrAppend(&result, ".", FormatNanos(nanos));
131 }
132 absl::StrAppend(&result, "Z");
133 return result;
134 }
135
ParseTime(absl::string_view value,int64_t * seconds,int32_t * nanos)136 bool ParseTime(absl::string_view value, int64_t* seconds, int32_t* nanos) {
137 absl::Time result;
138 if (!absl::ParseTime(absl::RFC3339_full, value, &result, nullptr)) {
139 return false;
140 }
141 timespec spec = absl::ToTimespec(result);
142 *seconds = spec.tv_sec;
143 *nanos = spec.tv_nsec;
144 return true;
145 }
146
CurrentTime(int64_t * seconds,int32_t * nanos)147 void CurrentTime(int64_t* seconds, int32_t* nanos) {
148 absl::Time now = absl::Now();
149 timespec spec = absl::ToTimespec(now);
150 *seconds = spec.tv_sec;
151 *nanos = spec.tv_nsec;
152 }
153
154 // Truncates the remainder part after division.
RoundTowardZero(int64_t value,int64_t divider)155 int64_t RoundTowardZero(int64_t value, int64_t divider) {
156 int64_t result = value / divider;
157 int64_t remainder = value % divider;
158 // Before C++11, the sign of the remainder is implementation dependent if
159 // any of the operands is negative. Here we try to enforce C++11's "rounded
160 // toward zero" semantics. For example, for (-5) / 2 an implementation may
161 // give -3 as the result with the remainder being 1. This function ensures
162 // we always return -2 (closer to zero) regardless of the implementation.
163 if (result < 0 && remainder > 0) {
164 return result + 1;
165 } else {
166 return result;
167 }
168 }
169 } // namespace
170
171 // Actually define these static const integers. Required by C++ standard (but
172 // some compilers don't like it).
173 #ifndef _MSC_VER
174 constexpr int64_t TimeUtil::kTimestampMinSeconds;
175 constexpr int64_t TimeUtil::kTimestampMaxSeconds;
176 constexpr int32_t TimeUtil::kTimestampMinNanoseconds;
177 constexpr int32_t TimeUtil::kTimestampMaxNanoseconds;
178 constexpr int64_t TimeUtil::kDurationMaxSeconds;
179 constexpr int64_t TimeUtil::kDurationMinSeconds;
180 constexpr int32_t TimeUtil::kDurationMaxNanoseconds;
181 constexpr int32_t TimeUtil::kDurationMinNanoseconds;
182 #endif // !_MSC_VER
183
ToString(const Timestamp & timestamp)184 std::string TimeUtil::ToString(const Timestamp& timestamp) {
185 return FormatTime(timestamp.seconds(), timestamp.nanos());
186 }
187
FromString(absl::string_view value,Timestamp * timestamp)188 bool TimeUtil::FromString(absl::string_view value, Timestamp* timestamp) {
189 int64_t seconds;
190 int32_t nanos;
191 if (!ParseTime(value, &seconds, &nanos)) {
192 return false;
193 }
194 *timestamp = CreateNormalized<Timestamp>(seconds, nanos);
195 return true;
196 }
197
GetCurrentTime()198 Timestamp TimeUtil::GetCurrentTime() {
199 int64_t seconds;
200 int32_t nanos;
201 CurrentTime(&seconds, &nanos);
202 return CreateNormalized<Timestamp>(seconds, nanos);
203 }
204
GetEpoch()205 Timestamp TimeUtil::GetEpoch() { return Timestamp(); }
206
ToString(const Duration & duration)207 std::string TimeUtil::ToString(const Duration& duration) {
208 std::string result;
209 int64_t seconds = duration.seconds();
210 int32_t nanos = duration.nanos();
211 if (seconds < 0 || nanos < 0) {
212 result = "-";
213 seconds = -seconds;
214 nanos = -nanos;
215 }
216 absl::StrAppend(&result, seconds);
217 if (nanos != 0) {
218 absl::StrAppend(&result, ".", FormatNanos(nanos));
219 }
220 absl::StrAppend(&result, "s");
221 return result;
222 }
223
Pow(int64_t x,int y)224 static int64_t Pow(int64_t x, int y) {
225 int64_t result = 1;
226 for (int i = 0; i < y; ++i) {
227 result *= x;
228 }
229 return result;
230 }
231
FromString(absl::string_view value,Duration * duration)232 bool TimeUtil::FromString(absl::string_view value, Duration* duration) {
233 if (value.length() <= 1 || value[value.length() - 1] != 's') {
234 return false;
235 }
236 bool negative = (value[0] == '-');
237 size_t sign_length = (negative ? 1 : 0);
238 // Parse the duration value as two integers rather than a float value
239 // to avoid precision loss.
240 std::string seconds_part, nanos_part;
241 size_t pos = value.find_last_of('.');
242 if (pos == std::string::npos) {
243 seconds_part = std::string(
244 value.substr(sign_length, value.length() - 1 - sign_length));
245 nanos_part = "0";
246 } else {
247 seconds_part = std::string(value.substr(sign_length, pos - sign_length));
248 nanos_part = std::string(value.substr(pos + 1, value.length() - pos - 2));
249 }
250 char* end;
251 static_assert(sizeof(int64_t) == sizeof(long long),
252 "sizeof int64_t is not sizeof long long");
253 int64_t seconds = std::strtoll(seconds_part.c_str(), &end, 10);
254 if (end != seconds_part.c_str() + seconds_part.length()) {
255 return false;
256 }
257 int64_t nanos = std::strtoll(nanos_part.c_str(), &end, 10);
258 if (end != nanos_part.c_str() + nanos_part.length()) {
259 return false;
260 }
261 nanos = nanos * Pow(10, static_cast<int>(9 - nanos_part.length()));
262 if (negative) {
263 // If a Duration is negative, both seconds and nanos should be negative.
264 seconds = -seconds;
265 nanos = -nanos;
266 }
267 duration->set_seconds(seconds);
268 duration->set_nanos(static_cast<int32_t>(nanos));
269 return true;
270 }
271
NanosecondsToDuration(int64_t nanos)272 Duration TimeUtil::NanosecondsToDuration(int64_t nanos) {
273 return CreateNormalized<Duration>(nanos / kNanosPerSecond,
274 nanos % kNanosPerSecond);
275 }
276
MicrosecondsToDuration(int64_t micros)277 Duration TimeUtil::MicrosecondsToDuration(int64_t micros) {
278 return CreateNormalized<Duration>(
279 micros / kMicrosPerSecond,
280 (micros % kMicrosPerSecond) * kNanosPerMicrosecond);
281 }
282
MillisecondsToDuration(int64_t millis)283 Duration TimeUtil::MillisecondsToDuration(int64_t millis) {
284 return CreateNormalized<Duration>(
285 millis / kMillisPerSecond,
286 (millis % kMillisPerSecond) * kNanosPerMillisecond);
287 }
288
SecondsToDuration(int64_t seconds)289 Duration TimeUtil::SecondsToDuration(int64_t seconds) {
290 return CreateNormalized<Duration>(seconds, 0);
291 }
292
MinutesToDuration(int64_t minutes)293 Duration TimeUtil::MinutesToDuration(int64_t minutes) {
294 ABSL_DCHECK(minutes >= TimeUtil::kDurationMinSeconds / kSecondsPerMinute &&
295 minutes <= TimeUtil::kDurationMaxSeconds / kSecondsPerMinute)
296 << "Duration minutes are outside of the valid range";
297 return SecondsToDuration(minutes * kSecondsPerMinute);
298 }
299
HoursToDuration(int64_t hours)300 Duration TimeUtil::HoursToDuration(int64_t hours) {
301 ABSL_DCHECK(hours >= TimeUtil::kDurationMinSeconds / kSecondsPerHour &&
302 hours <= TimeUtil::kDurationMaxSeconds / kSecondsPerHour)
303 << "Duration hours are outside of the valid range";
304 return SecondsToDuration(hours * kSecondsPerHour);
305 }
306
DurationToNanoseconds(const Duration & duration)307 int64_t TimeUtil::DurationToNanoseconds(const Duration& duration) {
308 ABSL_DCHECK(IsDurationValid(duration))
309 << "Duration is outside of the valid range";
310 return duration.seconds() * kNanosPerSecond + duration.nanos();
311 }
312
DurationToMicroseconds(const Duration & duration)313 int64_t TimeUtil::DurationToMicroseconds(const Duration& duration) {
314 return DurationToSeconds(duration) * kMicrosPerSecond +
315 RoundTowardZero(duration.nanos(), kNanosPerMicrosecond);
316 }
317
DurationToMilliseconds(const Duration & duration)318 int64_t TimeUtil::DurationToMilliseconds(const Duration& duration) {
319 return DurationToSeconds(duration) * kMillisPerSecond +
320 RoundTowardZero(duration.nanos(), kNanosPerMillisecond);
321 }
322
DurationToSeconds(const Duration & duration)323 int64_t TimeUtil::DurationToSeconds(const Duration& duration) {
324 ABSL_DCHECK(IsDurationValid(duration))
325 << "Duration is outside of the valid range";
326 return duration.seconds();
327 }
328
DurationToMinutes(const Duration & duration)329 int64_t TimeUtil::DurationToMinutes(const Duration& duration) {
330 return RoundTowardZero(DurationToSeconds(duration), kSecondsPerMinute);
331 }
332
DurationToHours(const Duration & duration)333 int64_t TimeUtil::DurationToHours(const Duration& duration) {
334 return RoundTowardZero(DurationToSeconds(duration), kSecondsPerHour);
335 }
336
NanosecondsToTimestamp(int64_t nanos)337 Timestamp TimeUtil::NanosecondsToTimestamp(int64_t nanos) {
338 return CreateNormalized<Timestamp>(nanos / kNanosPerSecond,
339 nanos % kNanosPerSecond);
340 }
341
MicrosecondsToTimestamp(int64_t micros)342 Timestamp TimeUtil::MicrosecondsToTimestamp(int64_t micros) {
343 return CreateNormalized<Timestamp>(
344 micros / kMicrosPerSecond,
345 micros % kMicrosPerSecond * kNanosPerMicrosecond);
346 }
347
MillisecondsToTimestamp(int64_t millis)348 Timestamp TimeUtil::MillisecondsToTimestamp(int64_t millis) {
349 return CreateNormalized<Timestamp>(
350 millis / kMillisPerSecond,
351 millis % kMillisPerSecond * kNanosPerMillisecond);
352 }
353
SecondsToTimestamp(int64_t seconds)354 Timestamp TimeUtil::SecondsToTimestamp(int64_t seconds) {
355 return CreateNormalized<Timestamp>(seconds, 0);
356 }
357
TimestampToNanoseconds(const Timestamp & timestamp)358 int64_t TimeUtil::TimestampToNanoseconds(const Timestamp& timestamp) {
359 ABSL_DCHECK(IsTimestampValid(timestamp))
360 << "Timestamp is outside of the valid range";
361 return timestamp.seconds() * kNanosPerSecond + timestamp.nanos();
362 }
363
TimestampToMicroseconds(const Timestamp & timestamp)364 int64_t TimeUtil::TimestampToMicroseconds(const Timestamp& timestamp) {
365 ABSL_DCHECK(IsTimestampValid(timestamp))
366 << "Timestamp is outside of the valid range";
367 return timestamp.seconds() * kMicrosPerSecond +
368 RoundTowardZero(timestamp.nanos(), kNanosPerMicrosecond);
369 }
370
TimestampToMilliseconds(const Timestamp & timestamp)371 int64_t TimeUtil::TimestampToMilliseconds(const Timestamp& timestamp) {
372 ABSL_DCHECK(IsTimestampValid(timestamp))
373 << "Timestamp is outside of the valid range";
374 return timestamp.seconds() * kMillisPerSecond +
375 RoundTowardZero(timestamp.nanos(), kNanosPerMillisecond);
376 }
377
TimestampToSeconds(const Timestamp & timestamp)378 int64_t TimeUtil::TimestampToSeconds(const Timestamp& timestamp) {
379 ABSL_DCHECK(IsTimestampValid(timestamp))
380 << "Timestamp is outside of the valid range";
381 return timestamp.seconds();
382 }
383
TimeTToTimestamp(time_t value)384 Timestamp TimeUtil::TimeTToTimestamp(time_t value) {
385 return CreateNormalized<Timestamp>(static_cast<int64_t>(value), 0);
386 }
387
TimestampToTimeT(const Timestamp & value)388 time_t TimeUtil::TimestampToTimeT(const Timestamp& value) {
389 return static_cast<time_t>(value.seconds());
390 }
391
TimevalToTimestamp(const timeval & value)392 Timestamp TimeUtil::TimevalToTimestamp(const timeval& value) {
393 return CreateNormalized<Timestamp>(value.tv_sec,
394 value.tv_usec * kNanosPerMicrosecond);
395 }
396
TimestampToTimeval(const Timestamp & value)397 timeval TimeUtil::TimestampToTimeval(const Timestamp& value) {
398 timeval result;
399 result.tv_sec = value.seconds();
400 result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond);
401 return result;
402 }
403
TimevalToDuration(const timeval & value)404 Duration TimeUtil::TimevalToDuration(const timeval& value) {
405 return CreateNormalized<Duration>(value.tv_sec,
406 value.tv_usec * kNanosPerMicrosecond);
407 }
408
DurationToTimeval(const Duration & value)409 timeval TimeUtil::DurationToTimeval(const Duration& value) {
410 timeval result;
411 result.tv_sec = value.seconds();
412 result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond);
413 // timeval.tv_usec's range is [0, 1000000)
414 if (result.tv_usec < 0) {
415 result.tv_sec -= 1;
416 result.tv_usec += kMicrosPerSecond;
417 }
418 return result;
419 }
420
421 } // namespace util
422 } // namespace protobuf
423 } // namespace google
424
425 namespace google {
426 namespace protobuf {
427 namespace {
428 using ::google::protobuf::util::CreateNormalized;
429 using ::google::protobuf::util::kNanosPerSecond;
430
431 // Convert a Duration to uint128.
ToUint128(const Duration & value,absl::uint128 * result,bool * negative)432 void ToUint128(const Duration& value, absl::uint128* result, bool* negative) {
433 if (value.seconds() < 0 || value.nanos() < 0) {
434 *negative = true;
435 *result = static_cast<uint64_t>(-value.seconds());
436 *result = *result * kNanosPerSecond + static_cast<uint32_t>(-value.nanos());
437 } else {
438 *negative = false;
439 *result = static_cast<uint64_t>(value.seconds());
440 *result = *result * kNanosPerSecond + static_cast<uint32_t>(value.nanos());
441 }
442 }
443
ToDuration(const absl::uint128 & value,bool negative,Duration * duration)444 void ToDuration(const absl::uint128& value, bool negative, Duration* duration) {
445 int64_t seconds =
446 static_cast<int64_t>(absl::Uint128Low64(value / kNanosPerSecond));
447 int32_t nanos =
448 static_cast<int32_t>(absl::Uint128Low64(value % kNanosPerSecond));
449 if (negative) {
450 seconds = -seconds;
451 nanos = -nanos;
452 }
453 duration->set_seconds(seconds);
454 duration->set_nanos(nanos);
455 }
456 } // namespace
457
operator +=(Duration & d1,const Duration & d2)458 Duration& operator+=(Duration& d1, const Duration& d2) {
459 d1 = CreateNormalized<Duration>(d1.seconds() + d2.seconds(),
460 d1.nanos() + d2.nanos());
461 return d1;
462 }
463
operator -=(Duration & d1,const Duration & d2)464 Duration& operator-=(Duration& d1, const Duration& d2) { // NOLINT
465 d1 = CreateNormalized<Duration>(d1.seconds() - d2.seconds(),
466 d1.nanos() - d2.nanos());
467 return d1;
468 }
469
operator *=(Duration & d,int64_t r)470 Duration& operator*=(Duration& d, int64_t r) { // NOLINT
471 bool negative;
472 absl::uint128 value;
473 ToUint128(d, &value, &negative);
474 if (r > 0) {
475 value *= static_cast<uint64_t>(r);
476 } else {
477 negative = !negative;
478 value *= static_cast<uint64_t>(-r);
479 }
480 ToDuration(value, negative, &d);
481 return d;
482 }
483
operator *=(Duration & d,double r)484 Duration& operator*=(Duration& d, double r) { // NOLINT
485 double result =
486 (static_cast<double>(d.seconds()) + d.nanos() * (1.0 / kNanosPerSecond)) *
487 r;
488 int64_t seconds = static_cast<int64_t>(result);
489 int32_t nanos = static_cast<int32_t>((result - static_cast<double>(seconds)) *
490 kNanosPerSecond);
491 // Note that we normalize here not just because nanos can have a different
492 // sign from seconds but also that nanos can be any arbitrary value when
493 // overflow happens (i.e., the result is a much larger value than what
494 // int64 can represent).
495 d = CreateNormalized<Duration>(seconds, nanos);
496 return d;
497 }
498
operator /=(Duration & d,int64_t r)499 Duration& operator/=(Duration& d, int64_t r) { // NOLINT
500 bool negative;
501 absl::uint128 value;
502 ToUint128(d, &value, &negative);
503 if (r > 0) {
504 value /= static_cast<uint64_t>(r);
505 } else {
506 negative = !negative;
507 value /= static_cast<uint64_t>(-r);
508 }
509 ToDuration(value, negative, &d);
510 return d;
511 }
512
operator /=(Duration & d,double r)513 Duration& operator/=(Duration& d, double r) { // NOLINT
514 return d *= 1.0 / r;
515 }
516
operator %=(Duration & d1,const Duration & d2)517 Duration& operator%=(Duration& d1, const Duration& d2) { // NOLINT
518 bool negative1, negative2;
519 absl::uint128 value1, value2;
520 ToUint128(d1, &value1, &negative1);
521 ToUint128(d2, &value2, &negative2);
522 absl::uint128 result = value1 % value2;
523 // When negative values are involved in division, we round the division
524 // result towards zero. With this semantics, sign of the remainder is the
525 // same as the dividend. For example:
526 // -5 / 10 = 0, -5 % 10 = -5
527 // -5 / (-10) = 0, -5 % (-10) = -5
528 // 5 / (-10) = 0, 5 % (-10) = 5
529 ToDuration(result, negative1, &d1);
530 return d1;
531 }
532
operator /(const Duration & d1,const Duration & d2)533 int64_t operator/(const Duration& d1, const Duration& d2) {
534 bool negative1, negative2;
535 absl::uint128 value1, value2;
536 ToUint128(d1, &value1, &negative1);
537 ToUint128(d2, &value2, &negative2);
538 int64_t result = absl::Uint128Low64(value1 / value2);
539 if (negative1 != negative2) {
540 result = -result;
541 }
542 return result;
543 }
544
operator +=(Timestamp & t,const Duration & d)545 Timestamp& operator+=(Timestamp& t, const Duration& d) { // NOLINT
546 t = CreateNormalized<Timestamp>(t.seconds() + d.seconds(),
547 t.nanos() + d.nanos());
548 return t;
549 }
550
operator -=(Timestamp & t,const Duration & d)551 Timestamp& operator-=(Timestamp& t, const Duration& d) { // NOLINT
552 t = CreateNormalized<Timestamp>(t.seconds() - d.seconds(),
553 t.nanos() - d.nanos());
554 return t;
555 }
556
operator -(const Timestamp & t1,const Timestamp & t2)557 Duration operator-(const Timestamp& t1, const Timestamp& t2) {
558 return CreateNormalized<Duration>(t1.seconds() - t2.seconds(),
559 t1.nanos() - t2.nanos());
560 }
561 } // namespace protobuf
562 } // namespace google
563
564 #include "google/protobuf/port_undef.inc"
565