• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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