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.
SecondsPer100Years(int year)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.
SecondsPer4Years(int year)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
IsLeapYear(int year)50 bool IsLeapYear(int year) {
51 return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
52 }
53
SecondsPerYear(int year)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
SecondsPerMonth(int month,bool leap)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
ValidateDateTime(const DateTime & time)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.
SecondsSinceCommonEra(const DateTime & 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.
FormatNanos(int32 nanos)133 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].
ParseInt(const char * data,int width,int min_value,int max_value,int * result)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.
ParseNanos(const char * data,int32 * 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
ParseTimezoneOffset(const char * data,int64 * offset)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
SecondsToDateTime(int64 seconds,DateTime * time)211 bool SecondsToDateTime(int64 seconds, DateTime* time) {
212 if (seconds < kMinTime || seconds > kMaxTime) {
213 return false;
214 }
215 // It's easier to calcuate 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
DateTimeToSeconds(const DateTime & time,int64 * seconds)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
GetCurrentTime(int64 * seconds,int32 * nanos)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
FormatTime(int64 seconds,int32 nanos)271 string FormatTime(int64 seconds, int32 nanos) {
272 DateTime time;
273 if (nanos < 0 || nanos > 999999999 || !SecondsToDateTime(seconds, &time)) {
274 return "InvalidTime";
275 }
276 string result = StringPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
277 time.year, time.month, time.day,
278 time.hour, time.minute, time.second);
279 if (nanos != 0) {
280 result += "." + FormatNanos(nanos);
281 }
282 return result + "Z";
283 }
284
ParseTime(const string & value,int64 * seconds,int32 * nanos)285 bool ParseTime(const 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
366