Home | History | Annotate | Download | only in stubs
      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