third_party/protobuf/src/google/protobuf/stubs/time.cc
Go to the documentation of this file.
1 #include <google/protobuf/stubs/time.h>
2 
3 #include <ctime>
4 
5 #include <google/protobuf/stubs/stringprintf.h>
6 #include <google/protobuf/stubs/strutil.h>
7 
8 namespace google {
9 namespace protobuf {
10 namespace internal {
11 
12 namespace {
13 static const int64 kSecondsPerMinute = 60;
14 static const int64 kSecondsPerHour = 3600;
15 static const int64 kSecondsPerDay = kSecondsPerHour * 24;
16 static const int64 kSecondsPer400Years =
17  kSecondsPerDay * (400 * 365 + 400 / 4 - 3);
18 // Seconds from 0001-01-01T00:00:00 to 1970-01-01T:00:00:00
19 static const int64 kSecondsFromEraToEpoch = 62135596800LL;
20 // The range of timestamp values we support.
21 static const int64 kMinTime = -62135596800LL; // 0001-01-01T00:00:00
22 static const int64 kMaxTime = 253402300799LL; // 9999-12-31T23:59:59
23 
24 static const int kNanosPerMillisecond = 1000000;
25 static const int kNanosPerMicrosecond = 1000;
26 
27 // Count the seconds from the given year (start at Jan 1, 00:00) to 100 years
28 // after.
29 int64 SecondsPer100Years(int year) {
30  if (year % 400 == 0 || year % 400 > 300) {
31  return kSecondsPerDay * (100 * 365 + 100 / 4);
32  } else {
33  return kSecondsPerDay * (100 * 365 + 100 / 4 - 1);
34  }
35 }
36 
37 // Count the seconds from the given year (start at Jan 1, 00:00) to 4 years
38 // after.
39 int64 SecondsPer4Years(int year) {
40  if ((year % 100 == 0 || year % 100 > 96) &&
41  !(year % 400 == 0 || year % 400 > 396)) {
42  // No leap years.
43  return kSecondsPerDay * (4 * 365);
44  } else {
45  // One leap years.
46  return kSecondsPerDay * (4 * 365 + 1);
47  }
48 }
49 
50 bool IsLeapYear(int year) {
51  return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
52 }
53 
54 int64 SecondsPerYear(int year) {
55  return kSecondsPerDay * (IsLeapYear(year) ? 366 : 365);
56 }
57 
58 static const int kDaysInMonth[13] = {
59  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
60 };
61 
62 int64 SecondsPerMonth(int month, bool leap) {
63  if (month == 2 && leap) {
64  return kSecondsPerDay * (kDaysInMonth[month] + 1);
65  }
66  return kSecondsPerDay * kDaysInMonth[month];
67 }
68 
69 static const int kDaysSinceJan[13] = {
70  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
71 };
72 
73 bool ValidateDateTime(const DateTime& time) {
74  if (time.year < 1 || time.year > 9999 ||
75  time.month < 1 || time.month > 12 ||
76  time.day < 1 || time.day > 31 ||
77  time.hour < 0 || time.hour > 23 ||
78  time.minute < 0 || time.minute > 59 ||
79  time.second < 0 || time.second > 59) {
80  return false;
81  }
82  if (time.month == 2 && IsLeapYear(time.year)) {
83  return time.day <= kDaysInMonth[time.month] + 1;
84  } else {
85  return time.day <= kDaysInMonth[time.month];
86  }
87 }
88 
89 // Count the number of seconds elapsed from 0001-01-01T00:00:00 to the given
90 // time.
91 int64 SecondsSinceCommonEra(const DateTime& time) {
92  int64 result = 0;
93  // Years should be between 1 and 9999.
94  assert(time.year >= 1 && time.year <= 9999);
95  int year = 1;
96  if ((time.year - year) >= 400) {
97  int count_400years = (time.year - year) / 400;
98  result += kSecondsPer400Years * count_400years;
99  year += count_400years * 400;
100  }
101  while ((time.year - year) >= 100) {
102  result += SecondsPer100Years(year);
103  year += 100;
104  }
105  while ((time.year - year) >= 4) {
106  result += SecondsPer4Years(year);
107  year += 4;
108  }
109  while (time.year > year) {
110  result += SecondsPerYear(year);
111  ++year;
112  }
113  // Months should be between 1 and 12.
114  assert(time.month >= 1 && time.month <= 12);
115  int month = time.month;
116  result += kSecondsPerDay * kDaysSinceJan[month];
117  if (month > 2 && IsLeapYear(year)) {
118  result += kSecondsPerDay;
119  }
120  assert(time.day >= 1 &&
121  time.day <= (month == 2 && IsLeapYear(year)
122  ? kDaysInMonth[month] + 1
123  : kDaysInMonth[month]));
124  result += kSecondsPerDay * (time.day - 1);
125  result += kSecondsPerHour * time.hour +
126  kSecondsPerMinute * time.minute +
127  time.second;
128  return result;
129 }
130 
131 // Format nanoseconds with either 3, 6, or 9 digits depending on the required
132 // precision to represent the exact value.
133 std::string FormatNanos(int32 nanos) {
134  if (nanos % kNanosPerMillisecond == 0) {
135  return StringPrintf("%03d", nanos / kNanosPerMillisecond);
136  } else if (nanos % kNanosPerMicrosecond == 0) {
137  return StringPrintf("%06d", nanos / kNanosPerMicrosecond);
138  } else {
139  return StringPrintf("%09d", nanos);
140  }
141 }
142 
143 // Parses an integer from a null-terminated char sequence. The method
144 // consumes at most "width" chars. Returns a pointer after the consumed
145 // integer, or nullptr if the data does not start with an integer or the
146 // integer value does not fall in the range of [min_value, max_value].
147 const char* ParseInt(const char* data, int width, int min_value,
148  int max_value, int* result) {
149  if (!ascii_isdigit(*data)) {
150  return nullptr;
151  }
152  int value = 0;
153  for (int i = 0; i < width; ++i, ++data) {
154  if (ascii_isdigit(*data)) {
155  value = value * 10 + (*data - '0');
156  } else {
157  break;
158  }
159  }
160  if (value >= min_value && value <= max_value) {
161  *result = value;
162  return data;
163  } else {
164  return nullptr;
165  }
166 }
167 
168 // Consumes the fractional parts of a second into nanos. For example,
169 // "010" will be parsed to 10000000 nanos.
170 const char* ParseNanos(const char* data, int32* nanos) {
171  if (!ascii_isdigit(*data)) {
172  return nullptr;
173  }
174  int value = 0;
175  int len = 0;
176  // Consume as many digits as there are but only take the first 9 into
177  // account.
178  while (ascii_isdigit(*data)) {
179  if (len < 9) {
180  value = value * 10 + *data - '0';
181  }
182  ++len;
183  ++data;
184  }
185  while (len < 9) {
186  value = value * 10;
187  ++len;
188  }
189  *nanos = value;
190  return data;
191 }
192 
193 const char* ParseTimezoneOffset(const char* data, int64* offset) {
194  // Accept format "HH:MM". E.g., "08:00"
195  int hour;
196  if ((data = ParseInt(data, 2, 0, 23, &hour)) == nullptr) {
197  return nullptr;
198  }
199  if (*data++ != ':') {
200  return nullptr;
201  }
202  int minute;
203  if ((data = ParseInt(data, 2, 0, 59, &minute)) == nullptr) {
204  return nullptr;
205  }
206  *offset = (hour * 60 + minute) * 60;
207  return data;
208 }
209 } // namespace
210 
211 bool SecondsToDateTime(int64 seconds, DateTime* time) {
212  if (seconds < kMinTime || seconds > kMaxTime) {
213  return false;
214  }
215  // It's easier to calculate the DateTime starting from 0001-01-01T00:00:00
216  seconds = seconds + kSecondsFromEraToEpoch;
217  int year = 1;
218  if (seconds >= kSecondsPer400Years) {
219  int count_400years = seconds / kSecondsPer400Years;
220  year += 400 * count_400years;
221  seconds %= kSecondsPer400Years;
222  }
223  while (seconds >= SecondsPer100Years(year)) {
224  seconds -= SecondsPer100Years(year);
225  year += 100;
226  }
227  while (seconds >= SecondsPer4Years(year)) {
228  seconds -= SecondsPer4Years(year);
229  year += 4;
230  }
231  while (seconds >= SecondsPerYear(year)) {
232  seconds -= SecondsPerYear(year);
233  year += 1;
234  }
235  bool leap = IsLeapYear(year);
236  int month = 1;
237  while (seconds >= SecondsPerMonth(month, leap)) {
238  seconds -= SecondsPerMonth(month, leap);
239  ++month;
240  }
241  int day = 1 + seconds / kSecondsPerDay;
242  seconds %= kSecondsPerDay;
243  int hour = seconds / kSecondsPerHour;
244  seconds %= kSecondsPerHour;
245  int minute = seconds / kSecondsPerMinute;
246  seconds %= kSecondsPerMinute;
247  time->year = year;
248  time->month = month;
249  time->day = day;
250  time->hour = hour;
251  time->minute = minute;
252  time->second = static_cast<int>(seconds);
253  return true;
254 }
255 
256 bool DateTimeToSeconds(const DateTime& time, int64* seconds) {
257  if (!ValidateDateTime(time)) {
258  return false;
259  }
260  *seconds = SecondsSinceCommonEra(time) - kSecondsFromEraToEpoch;
261  return true;
262 }
263 
264 void GetCurrentTime(int64* seconds, int32* nanos) {
265  // TODO(xiaofeng): Improve the accuracy of this implementation (or just
266  // remove this method from protobuf).
267  *seconds = time(nullptr);
268  *nanos = 0;
269 }
270 
272  DateTime time;
273  if (nanos < 0 || nanos > 999999999 || !SecondsToDateTime(seconds, &time)) {
274  return "InvalidTime";
275  }
277  StringPrintf("%04d-%02d-%02dT%02d:%02d:%02d", time.year, time.month,
278  time.day, time.hour, time.minute, time.second);
279  if (nanos != 0) {
280  result += "." + FormatNanos(nanos);
281  }
282  return result + "Z";
283 }
284 
285 bool ParseTime(const std::string& value, int64* seconds, int32* nanos) {
286  DateTime time;
287  const char* data = value.c_str();
288  // We only accept:
289  // Z-normalized: 2015-05-20T13:29:35.120Z
290  // With UTC offset: 2015-05-20T13:29:35.120-08:00
291 
292  // Parse year
293  if ((data = ParseInt(data, 4, 1, 9999, &time.year)) == nullptr) {
294  return false;
295  }
296  // Expect '-'
297  if (*data++ != '-') return false;
298  // Parse month
299  if ((data = ParseInt(data, 2, 1, 12, &time.month)) == nullptr) {
300  return false;
301  }
302  // Expect '-'
303  if (*data++ != '-') return false;
304  // Parse day
305  if ((data = ParseInt(data, 2, 1, 31, &time.day)) == nullptr) {
306  return false;
307  }
308  // Expect 'T'
309  if (*data++ != 'T') return false;
310  // Parse hour
311  if ((data = ParseInt(data, 2, 0, 23, &time.hour)) == nullptr) {
312  return false;
313  }
314  // Expect ':'
315  if (*data++ != ':') return false;
316  // Parse minute
317  if ((data = ParseInt(data, 2, 0, 59, &time.minute)) == nullptr) {
318  return false;
319  }
320  // Expect ':'
321  if (*data++ != ':') return false;
322  // Parse second
323  if ((data = ParseInt(data, 2, 0, 59, &time.second)) == nullptr) {
324  return false;
325  }
326  if (!DateTimeToSeconds(time, seconds)) {
327  return false;
328  }
329  // Parse nanoseconds.
330  if (*data == '.') {
331  ++data;
332  // Parse nanoseconds.
333  if ((data = ParseNanos(data, nanos)) == nullptr) {
334  return false;
335  }
336  } else {
337  *nanos = 0;
338  }
339  // Parse UTC offsets.
340  if (*data == 'Z') {
341  ++data;
342  } else if (*data == '+') {
343  ++data;
344  int64 offset;
345  if ((data = ParseTimezoneOffset(data, &offset)) == nullptr) {
346  return false;
347  }
348  *seconds -= offset;
349  } else if (*data == '-') {
350  ++data;
351  int64 offset;
352  if ((data = ParseTimezoneOffset(data, &offset)) == nullptr) {
353  return false;
354  }
355  *seconds += offset;
356  } else {
357  return false;
358  }
359  // Done with parsing.
360  return *data == 0;
361 }
362 
363 } // namespace internal
364 } // namespace protobuf
365 } // namespace google
absl::time_internal::cctz::seconds
std::chrono::duration< std::int_fast64_t > seconds
Definition: abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h:40
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
width
int width
Definition: libuv/docs/code/tty-gravity/main.c:10
google::protobuf::value
const Descriptor::ReservedRange value
Definition: bloaty/third_party/protobuf/src/google/protobuf/descriptor.h:1954
google::protobuf.internal::DateTime
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.h:41
google::protobuf::int64
int64_t int64
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/port.h:151
google::protobuf.internal::DateTime::minute
int minute
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.h:46
google::protobuf.internal::DateTime::month
int month
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.h:43
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
google::protobuf.internal::DateTime::day
int day
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.h:44
google::protobuf
Definition: bloaty/third_party/protobuf/benchmarks/util/data_proto2_to_proto3_util.h:12
google::protobuf.internal::DateTime::year
int year
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.h:42
google::protobuf.internal::DateTime::second
int second
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.h:47
google::protobuf.internal::DateTime::hour
int hour
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.h:45
google::protobuf::int32
int32_t int32
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/port.h:150
google::protobuf.internal::GetCurrentTime
void GetCurrentTime(int64 *seconds, int32 *nanos)
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.cc:264
google::protobuf.internal::DateTimeToSeconds
bool DateTimeToSeconds(const DateTime &time, int64 *seconds)
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.cc:256
google::protobuf::StringPrintf
string StringPrintf(const char *format,...)
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc:109
data
char data[kBufferLength]
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1006
google::protobuf.internal::ParseTime
bool ParseTime(const string &value, int64 *seconds, int32 *nanos)
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.cc:285
google::protobuf::ascii_isdigit
bool ascii_isdigit(char c)
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/strutil.h:73
google::protobuf.internal::FormatTime
string FormatTime(int64 seconds, int32 nanos)
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.cc:271
internal
Definition: benchmark/test/output_test_helper.cc:20
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
google
Definition: bloaty/third_party/protobuf/benchmarks/util/data_proto2_to_proto3_util.h:11
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
offset
voidpf uLong offset
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:142
LL
#define LL(x)
google::protobuf.internal::SecondsToDateTime
bool SecondsToDateTime(int64 seconds, DateTime *time)
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/stubs/time.cc:211


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:36