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_t kSecondsPerMinute = 60; |
14 | static const int64_t kSecondsPerHour = 3600; |
15 | static const int64_t kSecondsPerDay = kSecondsPerHour * 24; |
16 | static const int64_t 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_t kSecondsFromEraToEpoch = 62135596800LL; |
20 | // The range of timestamp values we support. |
21 | static const int64_t kMinTime = -62135596800LL; // 0001-01-01T00:00:00 |
22 | static const int64_t 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_t 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_t 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_t 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_t 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(year: 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_t SecondsSinceCommonEra(const DateTime& time) { |
92 | int64_t 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_t nanos) { |
134 | if (nanos % kNanosPerMillisecond == 0) { |
135 | return StringPrintf(format: "%03d" , nanos / kNanosPerMillisecond); |
136 | } else if (nanos % kNanosPerMicrosecond == 0) { |
137 | return StringPrintf(format: "%06d" , nanos / kNanosPerMicrosecond); |
138 | } else { |
139 | return StringPrintf(format: "%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(c: *data)) { |
150 | return nullptr; |
151 | } |
152 | int value = 0; |
153 | for (int i = 0; i < width; ++i, ++data) { |
154 | if (ascii_isdigit(c: *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_t* nanos) { |
171 | if (!ascii_isdigit(c: *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(c: *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_t* offset) { |
194 | // Accept format "HH:MM". E.g., "08:00" |
195 | int hour; |
196 | if ((data = ParseInt(data, width: 2, min_value: 0, max_value: 23, result: &hour)) == nullptr) { |
197 | return nullptr; |
198 | } |
199 | if (*data++ != ':') { |
200 | return nullptr; |
201 | } |
202 | int minute; |
203 | if ((data = ParseInt(data, width: 2, min_value: 0, max_value: 59, result: &minute)) == nullptr) { |
204 | return nullptr; |
205 | } |
206 | *offset = (hour * 60 + minute) * 60; |
207 | return data; |
208 | } |
209 | } // namespace |
210 | |
211 | bool SecondsToDateTime(int64_t 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_t* seconds) { |
257 | if (!ValidateDateTime(time)) { |
258 | return false; |
259 | } |
260 | *seconds = SecondsSinceCommonEra(time) - kSecondsFromEraToEpoch; |
261 | return true; |
262 | } |
263 | |
264 | void GetCurrentTime(int64_t* seconds, int32_t* nanos) { |
265 | // TODO(xiaofeng): Improve the accuracy of this implementation (or just |
266 | // remove this method from protobuf). |
267 | *seconds = time(timer: nullptr); |
268 | *nanos = 0; |
269 | } |
270 | |
271 | std::string FormatTime(int64_t seconds, int32_t nanos) { |
272 | DateTime time; |
273 | if (nanos < 0 || nanos > 999999999 || !SecondsToDateTime(seconds, time: &time)) { |
274 | return "InvalidTime" ; |
275 | } |
276 | std::string result = |
277 | StringPrintf(format: "%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_t* seconds, int32_t* 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, width: 4, min_value: 1, max_value: 9999, result: &time.year)) == nullptr) { |
294 | return false; |
295 | } |
296 | // Expect '-' |
297 | if (*data++ != '-') return false; |
298 | // Parse month |
299 | if ((data = ParseInt(data, width: 2, min_value: 1, max_value: 12, result: &time.month)) == nullptr) { |
300 | return false; |
301 | } |
302 | // Expect '-' |
303 | if (*data++ != '-') return false; |
304 | // Parse day |
305 | if ((data = ParseInt(data, width: 2, min_value: 1, max_value: 31, result: &time.day)) == nullptr) { |
306 | return false; |
307 | } |
308 | // Expect 'T' |
309 | if (*data++ != 'T') return false; |
310 | // Parse hour |
311 | if ((data = ParseInt(data, width: 2, min_value: 0, max_value: 23, result: &time.hour)) == nullptr) { |
312 | return false; |
313 | } |
314 | // Expect ':' |
315 | if (*data++ != ':') return false; |
316 | // Parse minute |
317 | if ((data = ParseInt(data, width: 2, min_value: 0, max_value: 59, result: &time.minute)) == nullptr) { |
318 | return false; |
319 | } |
320 | // Expect ':' |
321 | if (*data++ != ':') return false; |
322 | // Parse second |
323 | if ((data = ParseInt(data, width: 2, min_value: 0, max_value: 59, result: &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_t offset; |
345 | if ((data = ParseTimezoneOffset(data, offset: &offset)) == nullptr) { |
346 | return false; |
347 | } |
348 | *seconds -= offset; |
349 | } else if (*data == '-') { |
350 | ++data; |
351 | int64_t offset; |
352 | if ((data = ParseTimezoneOffset(data, offset: &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 |
366 | |