• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/time/time.h"
6 
7 #include <CoreFoundation/CFDate.h>
8 #include <CoreFoundation/CFCalendar.h>
9 #include <CoreFoundation/CFTimeZone.h>
10 
11 #include "base/apple/scoped_cftyperef.h"
12 #include "base/numerics/safe_conversions.h"
13 
14 #if __LP64__
15 #error Use posix implementation on 64-bit platforms.
16 #endif  // __LP64__
17 
18 namespace base {
19 
20 // Note: These implementations of Time::FromExploded() and Time::Explode() are
21 // only used on iOS now. Since Mac is now always 64-bit, we can use the POSIX
22 // versions of these functions as time_t is not capped at year 2038 on 64-bit
23 // builds. The POSIX functions are preferred since they don't suffer from some
24 // performance problems that are present in these implementations.
25 // See crbug.com/781601, crbug.com/985061 for more details.
26 
27 // static
FromExploded(bool is_local,const Exploded & exploded,Time * time)28 bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
29   ScopedCFTypeRef<CFTimeZoneRef> time_zone(
30       is_local
31           ? CFTimeZoneCopySystem()
32           : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0));
33   ScopedCFTypeRef<CFCalendarRef> gregorian(CFCalendarCreateWithIdentifier(
34       kCFAllocatorDefault, kCFGregorianCalendar));
35   CFCalendarSetTimeZone(gregorian, time_zone);
36   CFAbsoluteTime absolute_time;
37   // 'S' is not defined in componentDesc in Apple documentation, but can be
38   // found at http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c
39   CFCalendarComposeAbsoluteTime(
40       gregorian, &absolute_time, "yMdHmsS", exploded.year, exploded.month,
41       exploded.day_of_month, exploded.hour, exploded.minute, exploded.second,
42       exploded.millisecond);
43   CFAbsoluteTime seconds = absolute_time + kCFAbsoluteTimeIntervalSince1970;
44 
45   // CFAbsolutTime is typedef of double. Convert seconds to
46   // microseconds and then cast to int64. If
47   // it cannot be suited to int64, then fail to avoid overflows.
48   double microseconds =
49       (seconds * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset;
50   if (!IsValueInRangeForNumericType<int64_t>(microseconds)) {
51     *time = Time(0);
52     return false;
53   }
54 
55   base::Time converted_time = Time(static_cast<int64_t>(microseconds));
56 
57   // If |exploded.day_of_month| is set to 31
58   // on a 28-30 day month, it will return the first day of the next month.
59   // Thus round-trip the time and compare the initial |exploded| with
60   // |utc_to_exploded| time.
61   base::Time::Exploded to_exploded;
62   if (!is_local)
63     converted_time.UTCExplode(&to_exploded);
64   else
65     converted_time.LocalExplode(&to_exploded);
66 
67   if (ExplodedMostlyEquals(to_exploded, exploded)) {
68     *time = converted_time;
69     return true;
70   }
71 
72   *time = Time(0);
73   return false;
74 }
75 
Explode(bool is_local,Exploded * exploded) const76 void Time::Explode(bool is_local, Exploded* exploded) const {
77   // Avoid rounding issues, by only putting the integral number of seconds
78   // (rounded towards -infinity) into a |CFAbsoluteTime| (which is a |double|).
79   int64_t microsecond = us_ % kMicrosecondsPerSecond;
80   if (microsecond < 0)
81     microsecond += kMicrosecondsPerSecond;
82   CFAbsoluteTime seconds = ((us_ - microsecond - kTimeTToMicrosecondsOffset) /
83                             kMicrosecondsPerSecond) -
84                            kCFAbsoluteTimeIntervalSince1970;
85 
86   ScopedCFTypeRef<CFTimeZoneRef> time_zone(
87       is_local
88           ? CFTimeZoneCopySystem()
89           : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0));
90   ScopedCFTypeRef<CFCalendarRef> gregorian(CFCalendarCreateWithIdentifier(
91       kCFAllocatorDefault, kCFGregorianCalendar));
92   CFCalendarSetTimeZone(gregorian, time_zone);
93   int second, day_of_week;
94   // 'E' sets the day of week, but is not defined in componentDesc in Apple
95   // documentation. It can be found in open source code here:
96   // http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c
97   CFCalendarDecomposeAbsoluteTime(gregorian, seconds, "yMdHmsE",
98                                   &exploded->year, &exploded->month,
99                                   &exploded->day_of_month, &exploded->hour,
100                                   &exploded->minute, &second, &day_of_week);
101   // Make sure seconds are rounded down towards -infinity.
102   exploded->second = floor(second);
103   // |Exploded|'s convention for day of week is 0 = Sunday, i.e. different
104   // from CF's 1 = Sunday.
105   exploded->day_of_week = (day_of_week - 1) % 7;
106   // Calculate milliseconds ourselves, since we rounded the |seconds|, making
107   // sure to round towards -infinity.
108   exploded->millisecond =
109       (microsecond >= 0) ? microsecond / kMicrosecondsPerMillisecond
110                          : ((microsecond + 1) / kMicrosecondsPerMillisecond - 1);
111 }
112 
113 }  // namespace base
114