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.month <= kDaysInMonth[time.month] + 1; 84 } else { 85 return time.month <= 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 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 NULL 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 NULL; 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 NULL; 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 NULL; 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)) == NULL) { 197 return NULL; 198 } 199 if (*data++ != ':') { 200 return NULL; 201 } 202 int minute; 203 if ((data = ParseInt(data, 2, 0, 59, &minute)) == NULL) { 204 return NULL; 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 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 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(NULL); 268 *nanos = 0; 269 } 270 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 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)) == NULL) { 294 return false; 295 } 296 // Expect '-' 297 if (*data++ != '-') return false; 298 // Parse month 299 if ((data = ParseInt(data, 2, 1, 12, &time.month)) == NULL) { 300 return false; 301 } 302 // Expect '-' 303 if (*data++ != '-') return false; 304 // Parse day 305 if ((data = ParseInt(data, 2, 1, 31, &time.day)) == NULL) { 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)) == NULL) { 312 return false; 313 } 314 // Expect ':' 315 if (*data++ != ':') return false; 316 // Parse minute 317 if ((data = ParseInt(data, 2, 0, 59, &time.minute)) == NULL) { 318 return false; 319 } 320 // Expect ':' 321 if (*data++ != ':') return false; 322 // Parse second 323 if ((data = ParseInt(data, 2, 0, 59, &time.second)) == NULL) { 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)) == NULL) { 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)) == NULL) { 346 return false; 347 } 348 *seconds -= offset; 349 } else if (*data == '-') { 350 ++data; 351 int64 offset; 352 if ((data = ParseTimezoneOffset(data, &offset)) == NULL) { 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