Chromium Code Reviews| Index: base/time/time_posix.cc |
| diff --git a/base/time/time_posix.cc b/base/time/time_posix.cc |
| index 32614bc086d2e015782470f851e76573c07e5065..cb3f9cde4ddd5b7d0ffc2a62e2ba077a9f8f0279 100644 |
| --- a/base/time/time_posix.cc |
| +++ b/base/time/time_posix.cc |
| @@ -301,8 +301,131 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) { |
| } |
| // Adjust from Unix (1970) to Windows (1601) epoch. |
| - return Time((milliseconds * kMicrosecondsPerMillisecond) + |
| - kWindowsEpochDeltaMicroseconds); |
| + base::Time t_time = Time((milliseconds * kMicrosecondsPerMillisecond) + |
| + kWindowsEpochDeltaMicroseconds); |
|
mmenke
2016/05/20 19:44:57
Again, suggest just calling this |time|, unless th
maksims (do not use this acc)
2016/05/24 09:14:21
Done.
|
| + // If |exploded.day_of_month| is set to 31 |
| + // on a 28-30 day month, it will return the first day of the next month. |
| + // Thus round-trip the time and compare the initial |exploded| with |
| + // |utc_to_exploded| time. |
| + base::Time::Exploded to_exploded; |
| + if (!is_local) |
| + t_time.UTCExplode(&to_exploded); |
| + else |
| + t_time.LocalExplode(&to_exploded); |
| + |
| + // If time is null, return false |
| + // which means time conversion failed |
| + return to_exploded != exploded ? Time(0) : t_time; |
|
mmenke
2016/05/20 19:44:57
nit: Fix indent. You can automatically format ev
maksims (do not use this acc)
2016/05/24 09:14:21
Done.
|
| +} |
| + |
| +// static |
| +bool Time::FromExploded(bool is_local, const Exploded& exploded, Time& time) { |
| + struct tm timestruct; |
| + timestruct.tm_sec = exploded.second; |
| + timestruct.tm_min = exploded.minute; |
| + timestruct.tm_hour = exploded.hour; |
| + timestruct.tm_mday = exploded.day_of_month; |
| + timestruct.tm_mon = exploded.month - 1; |
| + timestruct.tm_year = exploded.year - 1900; |
| + timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this |
| + timestruct.tm_yday = 0; // mktime/timegm ignore this |
| + timestruct.tm_isdst = -1; // attempt to figure it out |
| +#if !defined(OS_NACL) && !defined(OS_SOLARIS) |
| + timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore |
| + timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore |
| +#endif |
| + |
| + int64_t milliseconds; |
| + SysTime seconds; |
| + |
| + // Certain exploded dates do not really exist due to daylight saving times, |
| + // and this causes mktime() to return implementation-defined values when |
| + // tm_isdst is set to -1. On Android, the function will return -1, while the |
| + // C libraries of other platforms typically return a liberally-chosen value. |
| + // Handling this requires the special code below. |
| + |
| + // SysTimeFromTimeStruct() modifies the input structure, save current value. |
| + struct tm timestruct0 = timestruct; |
| + |
| + seconds = SysTimeFromTimeStruct(×truct, is_local); |
| + if (seconds == -1) { |
| + // Get the time values with tm_isdst == 0 and 1, then select the closest one |
| + // to UTC 00:00:00 that isn't -1. |
| + timestruct = timestruct0; |
| + timestruct.tm_isdst = 0; |
| + int64_t seconds_isdst0 = SysTimeFromTimeStruct(×truct, is_local); |
| + |
| + timestruct = timestruct0; |
| + timestruct.tm_isdst = 1; |
| + int64_t seconds_isdst1 = SysTimeFromTimeStruct(×truct, is_local); |
| + |
| + // seconds_isdst0 or seconds_isdst1 can be -1 for some timezones. |
| + // E.g. "CLST" (Chile Summer Time) returns -1 for 'tm_isdt == 1'. |
| + if (seconds_isdst0 < 0) |
| + seconds = seconds_isdst1; |
| + else if (seconds_isdst1 < 0) |
| + seconds = seconds_isdst0; |
| + else |
| + seconds = std::min(seconds_isdst0, seconds_isdst1); |
| + } |
| + |
| + // Handle overflow. Clamping the range to what mktime and timegm might |
| + // return is the best that can be done here. It's not ideal, but it's better |
| + // than failing here or ignoring the overflow case and treating each time |
| + // overflow as one second prior to the epoch. |
| + if (seconds == -1 && |
| + (exploded.year < 1969 || exploded.year > 1970)) { |
| + // If exploded.year is 1969 or 1970, take -1 as correct, with the |
| + // time indicating 1 second prior to the epoch. (1970 is allowed to handle |
| + // time zone and DST offsets.) Otherwise, return the most future or past |
| + // time representable. Assumes the time_t epoch is 1970-01-01 00:00:00 UTC. |
| + // |
| + // The minimum and maximum representible times that mktime and timegm could |
| + // return are used here instead of values outside that range to allow for |
| + // proper round-tripping between exploded and counter-type time |
| + // representations in the presence of possible truncation to time_t by |
| + // division and use with other functions that accept time_t. |
| + // |
| + // When representing the most distant time in the future, add in an extra |
| + // 999ms to avoid the time being less than any other possible value that |
| + // this function can return. |
| + |
| + // On Android, SysTime is int64_t, special care must be taken to avoid |
| + // overflows. |
| + const int64_t min_seconds = (sizeof(SysTime) < sizeof(int64_t)) |
| + ? std::numeric_limits<SysTime>::min() |
| + : std::numeric_limits<int32_t>::min(); |
| + const int64_t max_seconds = (sizeof(SysTime) < sizeof(int64_t)) |
| + ? std::numeric_limits<SysTime>::max() |
| + : std::numeric_limits<int32_t>::max(); |
| + if (exploded.year < 1969) { |
| + milliseconds = min_seconds * kMillisecondsPerSecond; |
| + } else { |
| + milliseconds = max_seconds * kMillisecondsPerSecond; |
| + milliseconds += (kMillisecondsPerSecond - 1); |
| + } |
| + } else { |
| + milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond; |
| + } |
| + |
| + // Adjust from Unix (1970) to Windows (1601) epoch. |
| + base::Time t_time = Time((milliseconds * kMicrosecondsPerMillisecond) + |
| + kWindowsEpochDeltaMicroseconds); |
| + // If |exploded.day_of_month| is set to 31 |
| + // on a 28-30 day month, it will return the first day of the next month. |
| + // Thus round-trip the time and compare the initial |exploded| with |
| + // |utc_to_exploded| time. |
| + base::Time::Exploded to_exploded; |
| + if (!is_local) |
| + t_time.UTCExplode(&to_exploded); |
| + else |
| + t_time.LocalExplode(&to_exploded); |
| + |
| + time = to_exploded != exploded ? Time(0) : t_time; |
| + |
| + // If time is null, return false |
| + // which means time conversion failed |
| + return time.is_null() ? false : true; |
| } |
| // TimeTicks ------------------------------------------------------------------ |