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
8namespace google {
9namespace protobuf {
10namespace internal {
11
12namespace {
13static const int64_t kSecondsPerMinute = 60;
14static const int64_t kSecondsPerHour = 3600;
15static const int64_t kSecondsPerDay = kSecondsPerHour * 24;
16static 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
19static const int64_t kSecondsFromEraToEpoch = 62135596800LL;
20// The range of timestamp values we support.
21static const int64_t kMinTime = -62135596800LL; // 0001-01-01T00:00:00
22static const int64_t kMaxTime = 253402300799LL; // 9999-12-31T23:59:59
23
24static const int kNanosPerMillisecond = 1000000;
25static const int kNanosPerMicrosecond = 1000;
26
27// Count the seconds from the given year (start at Jan 1, 00:00) to 100 years
28// after.
29int64_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.
39int64_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
50bool IsLeapYear(int year) {
51 return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
52}
53
54int64_t SecondsPerYear(int year) {
55 return kSecondsPerDay * (IsLeapYear(year) ? 366 : 365);
56}
57
58static const int kDaysInMonth[13] = {
59 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
60};
61
62int64_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
69static const int kDaysSinceJan[13] = {
70 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
71};
72
73bool 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.
91int64_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.
133std::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].
147const 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.
170const 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
193const 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
211bool 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
256bool 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
264void 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
271std::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
285bool 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