// Copyright (c) 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/time/time.h" #include #include #include #include "base/mac/scoped_cftyperef.h" #include "base/numerics/safe_conversions.h" #if __LP64__ #error Use posix implementation on 64-bit platforms. #endif // __LP64__ namespace base { // Note: These implementations of Time::FromExploded() and Time::Explode() are // only used on iOS now. Since Mac is now always 64-bit, we can use the POSIX // versions of these functions as time_t is not capped at year 2038 on 64-bit // builds. The POSIX functions are preferred since they don't suffer from some // performance problems that are present in these implementations. // See crbug.com/781601, crbug.com/985061 for more details. // static bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) { base::ScopedCFTypeRef time_zone( is_local ? CFTimeZoneCopySystem() : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0)); base::ScopedCFTypeRef gregorian(CFCalendarCreateWithIdentifier( kCFAllocatorDefault, kCFGregorianCalendar)); CFCalendarSetTimeZone(gregorian, time_zone); CFAbsoluteTime absolute_time; // 'S' is not defined in componentDesc in Apple documentation, but can be // found at http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c CFCalendarComposeAbsoluteTime( gregorian, &absolute_time, "yMdHmsS", exploded.year, exploded.month, exploded.day_of_month, exploded.hour, exploded.minute, exploded.second, exploded.millisecond); CFAbsoluteTime seconds = absolute_time + kCFAbsoluteTimeIntervalSince1970; // CFAbsolutTime is typedef of double. Convert seconds to // microseconds and then cast to int64. If // it cannot be suited to int64, then fail to avoid overflows. double microseconds = (seconds * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset; if (!IsValueInRangeForNumericType(microseconds)) { *time = Time(0); return false; } base::Time converted_time = Time(static_cast(microseconds)); // 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) converted_time.UTCExplode(&to_exploded); else converted_time.LocalExplode(&to_exploded); if (ExplodedMostlyEquals(to_exploded, exploded)) { *time = converted_time; return true; } *time = Time(0); return false; } void Time::Explode(bool is_local, Exploded* exploded) const { // Avoid rounding issues, by only putting the integral number of seconds // (rounded towards -infinity) into a |CFAbsoluteTime| (which is a |double|). int64_t microsecond = us_ % kMicrosecondsPerSecond; if (microsecond < 0) microsecond += kMicrosecondsPerSecond; CFAbsoluteTime seconds = ((us_ - microsecond - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970; base::ScopedCFTypeRef time_zone( is_local ? CFTimeZoneCopySystem() : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0)); base::ScopedCFTypeRef gregorian(CFCalendarCreateWithIdentifier( kCFAllocatorDefault, kCFGregorianCalendar)); CFCalendarSetTimeZone(gregorian, time_zone); int second, day_of_week; // 'E' sets the day of week, but is not defined in componentDesc in Apple // documentation. It can be found in open source code here: // http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c CFCalendarDecomposeAbsoluteTime(gregorian, seconds, "yMdHmsE", &exploded->year, &exploded->month, &exploded->day_of_month, &exploded->hour, &exploded->minute, &second, &day_of_week); // Make sure seconds are rounded down towards -infinity. exploded->second = floor(second); // |Exploded|'s convention for day of week is 0 = Sunday, i.e. different // from CF's 1 = Sunday. exploded->day_of_week = (day_of_week - 1) % 7; // Calculate milliseconds ourselves, since we rounded the |seconds|, making // sure to round towards -infinity. exploded->millisecond = (microsecond >= 0) ? microsecond / kMicrosecondsPerMillisecond : ((microsecond + 1) / kMicrosecondsPerMillisecond - 1); } } // namespace base