Skip to content

Commit 4dcbbcc

Browse files
authored
Merge pull request #1595 from swiftlang/automerge/merge-main-2025-11-12_09-05
Merge `main` into `future`
2 parents aa5dd1c + d8533a2 commit 4dcbbcc

19 files changed

+792
-397
lines changed

Sources/FoundationEssentials/AttributedString/AttributedString+IndexValidity.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ extension AttributedString.Index {
5656

5757
/// Indicates whether the index is valid for use with the provided discontiguous attributed string.
5858
/// - Parameter text: A discontiguous attributed string used to validate the index.
59-
/// - Returns: `true` when the index is valid for use with the provided discontiguous attributed string; otherwise, false. An index is valid if it is both within the bounds of the discontigous attributed string and was produced from the provided string without any intermediate mutations.
59+
/// - Returns: `true` when the index is valid for use with the provided discontiguous attributed string; otherwise, false. An index is valid if it is both within the bounds of the discontiguous attributed string and was produced from the provided string without any intermediate mutations.
6060
public func isValid(within text: DiscontiguousAttributedSubstring) -> Bool {
6161
self._version == text._guts.version &&
6262
text._indices.contains(self._value)
@@ -100,7 +100,7 @@ extension RangeSet<AttributedString.Index> {
100100
}
101101

102102
/// Indicates whether the range set is valid for use with the provided discontiguous attributed string.
103-
/// - Parameter text: A discontigious attributed string used to validate the range set.
103+
/// - Parameter text: A discontiguous attributed string used to validate the range set.
104104
/// - Returns: `true` when the range set is valid for use with the provided discontiguous attributed string; otherwise, false. A range set is valid if each of its ranges are valid in the discontiguous attributed string.
105105
public func isValid(within text: DiscontiguousAttributedSubstring) -> Bool {
106106
self.ranges.allSatisfy {

Sources/FoundationEssentials/AttributedString/AttributedString+_InternalRuns.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extension AttributedString {
4444
extension AttributedString._InternalRuns {
4545
/// A metric that assigns each run a size of 1; i.e., the metric corresponding to run offsets.
4646
///
47-
/// Runs are not divisable under this metric.
47+
/// Runs are not divisible under this metric.
4848
struct RunMetric: RopeMetric {
4949
typealias Element = AttributedString._InternalRun
5050
typealias Summary = Element.Summary

Sources/FoundationEssentials/AttributedString/FoundationAttributes.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ extension AttributeScopes.FoundationAttributes {
470470
guard text.count == 1 else {
471471
throw DecodingError.dataCorrupted(DecodingError.Context(
472472
codingPath: container.codingPath,
473-
debugDescription: "List item delimeter encoded value must contain only one character / grapheme cluster"
473+
debugDescription: "List item delimiter encoded value must contain only one character / grapheme cluster"
474474
))
475475
}
476476
return text[text.startIndex]
@@ -902,7 +902,7 @@ extension AttributedString {
902902
/// The writing direction of a piece of text.
903903
///
904904
/// Writing direction defines the base direction in which bidirectional text
905-
/// lays out its directional runs. A directional run is a contigous sequence
905+
/// lays out its directional runs. A directional run is a contiguous sequence
906906
/// of characters that all have the same effective directionality, which can
907907
/// be determined using the Unicode BiDi algorithm. The ``leftToRight``
908908
/// writing direction puts the directional run that is placed first in the

Sources/FoundationEssentials/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ add_library(FoundationEssentials
2121
ComparisonResult.swift
2222
Date.swift
2323
DateInterval.swift
24-
DoubleDouble.swift
2524
FoundationEssentials.swift
2625
IndexPath.swift
2726
LockedState.swift

Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,39 +1448,30 @@ internal final class _CalendarGregorian: _CalendarProtocol, @unchecked Sendable
14481448
}
14491449

14501450
func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval? {
1451-
let approximateTime = date._time.head
1451+
let time = date.timeIntervalSinceReferenceDate
14521452
var effectiveUnit = component
14531453
switch effectiveUnit {
14541454
case .calendar, .timeZone, .isLeapMonth, .isRepeatedDay:
14551455
return nil
14561456
case .era:
1457-
if approximateTime < -63113904000.0 {
1457+
if time < -63113904000.0 {
14581458
return DateInterval(start: Date(timeIntervalSinceReferenceDate: -63113904000.0 - inf_ti), duration: inf_ti)
14591459
} else {
14601460
return DateInterval(start: Date(timeIntervalSinceReferenceDate: -63113904000.0), duration: inf_ti)
14611461
}
14621462

14631463
case .hour:
1464-
// Local hours may not be aligned to GMT hours, so we have to apply
1465-
// the time zone adjustment before rounding down, then unapply it.
1466-
let offset = Double(timeZone.secondsFromGMT(for: date))
1467-
let start = ((date._time + offset)/3600).floor() * 3600 - offset
1468-
return DateInterval(
1469-
start: Date(start),
1470-
duration: 3600
1471-
)
1464+
let ti = Double(timeZone.secondsFromGMT(for: date))
1465+
var fixedTime = time + ti // compute local time
1466+
fixedTime = floor(fixedTime / 3600.0) * 3600.0
1467+
fixedTime = fixedTime - ti // compute GMT
1468+
return DateInterval(start: Date(timeIntervalSinceReferenceDate: fixedTime), duration: 3600.0)
14721469
case .minute:
1473-
return DateInterval(
1474-
start: Date((date._time/60).floor() * 60),
1475-
duration: 60
1476-
)
1470+
return DateInterval(start: Date(timeIntervalSinceReferenceDate: floor(time / 60.0) * 60.0), duration: 60.0)
14771471
case .second:
1478-
return DateInterval(start: Date(date._time.floor()), duration: 1)
1472+
return DateInterval(start: Date(timeIntervalSinceReferenceDate: floor(time)), duration: 1.0)
14791473
case .nanosecond:
1480-
return DateInterval(
1481-
start: Date((date._time*1e9).floor() / 1e9),
1482-
duration: 1e-9
1483-
)
1474+
return DateInterval(start: Date(timeIntervalSinceReferenceDate: floor(time * 1.0e+9) * 1.0e-9), duration: 1.0e-9)
14841475
case .year, .yearForWeekOfYear, .quarter, .month, .day, .dayOfYear, .weekOfMonth, .weekOfYear:
14851476
// Continue to below
14861477
break

Sources/FoundationEssentials/Calendar/Calendar_Recurrence.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ extension Calendar {
9393
/// value is used as a lower bound for ``nextBaseRecurrenceDate()``.
9494
let rangeLowerBound: Date?
9595

96-
/// The start date's fractional seconds component
97-
let fractionalSeconds: TimeInterval
96+
/// The start date's nanoseconds component
97+
let startDateNanoseconds: TimeInterval
9898

9999
/// How many occurrences have been found so far
100100
var resultsFound = 0
@@ -131,10 +131,7 @@ extension Calendar {
131131
}
132132
self.recurrence = recurrence
133133

134-
// round start down to whole seconds, set aside fraction.
135-
let wholeSeconds = start._time.floor()
136-
fractionalSeconds = (start._time - wholeSeconds).head
137-
self.start = Date(wholeSeconds)
134+
self.start = start
138135
self.range = range
139136

140137
let frequency = recurrence.frequency
@@ -236,7 +233,9 @@ extension Calendar {
236233
case .monthly: [.second, .minute, .hour, .day]
237234
case .yearly: [.second, .minute, .hour, .day, .month, .isLeapMonth]
238235
}
239-
var componentsForEnumerating = recurrence.calendar._dateComponents(components, from: start)
236+
var componentsForEnumerating = recurrence.calendar._dateComponents(components, from: start)
237+
238+
startDateNanoseconds = start.timeIntervalSinceReferenceDate.truncatingRemainder(dividingBy: 1)
240239

241240
let expansionChangesDay = dayOfYearAction == .expand || dayOfMonthAction == .expand || weekAction == .expand || weekdayAction == .expand
242241
let expansionChangesMonth = dayOfYearAction == .expand || monthAction == .expand || weekAction == .expand
@@ -428,11 +427,11 @@ extension Calendar {
428427
recurrence._limitTimeComponent(.second, dates: &dates, anchor: anchor)
429428
}
430429

431-
if fractionalSeconds != 0 {
430+
if startDateNanoseconds > 0 {
432431
// `_dates(startingAfter:)` above returns whole-second dates,
433432
// so we need to restore the nanoseconds value present in the original start date.
434433
for idx in dates.indices {
435-
dates[idx] += fractionalSeconds
434+
dates[idx] += startDateNanoseconds
436435
}
437436
}
438437
dates = dates.filter { $0 >= self.start }

Sources/FoundationEssentials/Date.swift

Lines changed: 20 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -34,64 +34,12 @@ public typealias TimeInterval = Double
3434
A `Date` is independent of a particular calendar or time zone. To represent a `Date` to a user, you must interpret it in the context of a `Calendar`.
3535
*/
3636
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
37-
public struct Date: Comparable, Hashable, Equatable, Sendable {
38-
/* Date is internally represented as a sum of two Doubles.
39-
40-
Previously Date was backed by a single Double measuring time since
41-
Jan 1 2001 in seconds. Because Double's precision is non-uniform, this
42-
means that times within a hundred days of the epoch are represented
43-
with approximately nanosecond precision, but as you get farther away
44-
from that date the precision decreases. For times close to the time
45-
at which this comment was written, accuracy has been reduced to about
46-
100ns.
47-
48-
The obvious thing would be to adopt an integer-based representation
49-
similar to C's timespec (32b nanoseconds, 64b seconds) or Swift's
50-
Duration (128b attoseconds). These representations suffer from a few
51-
difficulties:
52-
53-
- Existing API on Date takes and produces `TimeInterval` (aka Double).
54-
Making Date use an integer representation internally would mean that
55-
existing users of the public API would suddently start getting
56-
different results for computations that were previously exact; even
57-
though we could add new API, and the overall system would be more
58-
precise, this would be a surprisingly subtle change for users to
59-
navigate.
60-
61-
- We have been told that some software interprets the raw bytes of Date
62-
as a Double for the purposes of fast serialization. These packages
63-
formally violate Foundation's API boundaries, but that doesn't help
64-
users of those packages who would abruptly be broken by switching to
65-
an integer representation.
66-
67-
Using DoubleDouble instead navigates these problems fairly elegantly.
68-
69-
- Because DoubleDouble is still a floating-point type, it still suffers
70-
from non-uniform precision. However, because DoubleDouble is so
71-
fantastically precise, it can represent dates out to ±2.5 quadrillion
72-
years at ~nanosecond or better precision, so in practice this won't
73-
be much of an issue.
74-
75-
- Existing API on Date will produce exactly the same result as it did
76-
previously in cases where those results were exact, minimizing
77-
surprises. In cases where the existing API was not exact, it will
78-
produce much more accurate results, even if users do not adopt new
79-
API, because its internal calculations are now more precise.
80-
81-
- Software that (incorrectly) interprets the raw bytes of Date as a
82-
Double will get at least as accurate of a value as it did previously
83-
(and often a more accurate value). */
84-
internal var _time: DoubleDouble
85-
86-
internal init(_ time: DoubleDouble) {
87-
self._time = time
88-
}
89-
}
37+
public struct Date : Comparable, Hashable, Equatable, Sendable {
38+
39+
internal var _time : TimeInterval
9040

91-
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
92-
extension Date {
9341
/// The number of seconds from 1 January 1970 to the reference date, 1 January 2001.
94-
public static let timeIntervalBetween1970AndReferenceDate: TimeInterval = 978307200.0
42+
public static let timeIntervalBetween1970AndReferenceDate : TimeInterval = 978307200.0
9543

9644
/// The number of seconds from 1 January 1601 to the reference date, 1 January 2001.
9745
internal static let timeIntervalBetween1601AndReferenceDate: TimeInterval = 12622780800.0
@@ -103,23 +51,17 @@ extension Date {
10351

10452
/// Returns a `Date` initialized to the current date and time.
10553
public init() {
106-
_time = .init(uncheckedHead: Self.getCurrentAbsoluteTime(), tail: 0)
54+
_time = Self.getCurrentAbsoluteTime()
10755
}
10856

10957
/// Returns a `Date` initialized relative to the current date and time by a given number of seconds.
11058
public init(timeIntervalSinceNow: TimeInterval) {
111-
self.init(.sum(
112-
Self.getCurrentAbsoluteTime(),
113-
timeIntervalSinceNow
114-
))
59+
self.init(timeIntervalSinceReferenceDate: timeIntervalSinceNow + Self.getCurrentAbsoluteTime())
11560
}
11661

11762
/// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds.
11863
public init(timeIntervalSince1970: TimeInterval) {
119-
self.init(.sum(
120-
timeIntervalSince1970,
121-
-Date.timeIntervalBetween1970AndReferenceDate
122-
))
64+
self.init(timeIntervalSinceReferenceDate: timeIntervalSince1970 - Date.timeIntervalBetween1970AndReferenceDate)
12365
}
12466

12567
/**
@@ -129,12 +71,12 @@ extension Date {
12971
- Parameter date: The reference date.
13072
*/
13173
public init(timeInterval: TimeInterval, since date: Date) {
132-
self.init(date._time + timeInterval)
74+
self.init(timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate + timeInterval)
13375
}
13476

13577
/// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 2001 by a given number of seconds.
13678
public init(timeIntervalSinceReferenceDate ti: TimeInterval) {
137-
_time = .init(uncheckedHead: ti, tail: 0)
79+
_time = ti
13880
}
13981

14082
/**
@@ -143,7 +85,7 @@ extension Date {
14385
This property's value is negative if the date object is earlier than the system's absolute reference date (00:00:00 UTC on 1 January 2001).
14486
*/
14587
public var timeIntervalSinceReferenceDate: TimeInterval {
146-
return _time.head
88+
return _time
14789
}
14890

14991
/**
@@ -158,7 +100,7 @@ extension Date {
158100
- SeeAlso: `timeIntervalSinceReferenceDate`
159101
*/
160102
public func timeIntervalSince(_ date: Date) -> TimeInterval {
161-
return (self._time - date._time).head
103+
return self.timeIntervalSinceReferenceDate - date.timeIntervalSinceReferenceDate
162104
}
163105

164106
/**
@@ -231,9 +173,9 @@ extension Date {
231173

232174
/// Compare two `Date` values.
233175
public func compare(_ other: Date) -> ComparisonResult {
234-
if _time < other._time {
176+
if _time < other.timeIntervalSinceReferenceDate {
235177
return .orderedAscending
236-
} else if _time > other._time {
178+
} else if _time > other.timeIntervalSinceReferenceDate {
237179
return .orderedDescending
238180
} else {
239181
return .orderedSame
@@ -242,27 +184,27 @@ extension Date {
242184

243185
/// Returns true if the two `Date` values represent the same point in time.
244186
public static func ==(lhs: Date, rhs: Date) -> Bool {
245-
return lhs._time == rhs._time
187+
return lhs.timeIntervalSinceReferenceDate == rhs.timeIntervalSinceReferenceDate
246188
}
247189

248190
/// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
249191
public static func <(lhs: Date, rhs: Date) -> Bool {
250-
return lhs._time < rhs._time
192+
return lhs.timeIntervalSinceReferenceDate < rhs.timeIntervalSinceReferenceDate
251193
}
252194

253195
/// Returns true if the left hand `Date` is later in time than the right hand `Date`.
254196
public static func >(lhs: Date, rhs: Date) -> Bool {
255-
return lhs._time > rhs._time
197+
return lhs.timeIntervalSinceReferenceDate > rhs.timeIntervalSinceReferenceDate
256198
}
257199

258200
/// Returns a `Date` with a specified amount of time added to it.
259201
public static func +(lhs: Date, rhs: TimeInterval) -> Date {
260-
return Date(lhs._time + rhs)
202+
return Date(timeIntervalSinceReferenceDate: lhs.timeIntervalSinceReferenceDate + rhs)
261203
}
262204

263205
/// Returns a `Date` with a specified amount of time subtracted from it.
264206
public static func -(lhs: Date, rhs: TimeInterval) -> Date {
265-
return Date(lhs._time - rhs)
207+
return Date(timeIntervalSinceReferenceDate: lhs.timeIntervalSinceReferenceDate - rhs)
266208
}
267209

268210
/// Add a `TimeInterval` to a `Date`.
@@ -278,6 +220,7 @@ extension Date {
278220
public static func -=(lhs: inout Date, rhs: TimeInterval) {
279221
lhs = lhs - rhs
280222
}
223+
281224
}
282225

283226
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
@@ -395,7 +338,7 @@ extension Date : ReferenceConvertible, _ObjectiveCBridgeable {
395338

396339
@_semantics("convertToObjectiveC")
397340
public func _bridgeToObjectiveC() -> NSDate {
398-
return NSDate(timeIntervalSinceReferenceDate: _time.head)
341+
return NSDate(timeIntervalSinceReferenceDate: _time)
399342
}
400343

401344
public static func _forceBridgeFromObjectiveC(_ x: NSDate, result: inout Date?) {

0 commit comments

Comments
 (0)