-
Notifications
You must be signed in to change notification settings - Fork 128
/
Date+IntervalFormatStyle.swift
166 lines (136 loc) · 6.26 KB
/
Date+IntervalFormatStyle.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if canImport(FoundationEssentials)
import FoundationEssentials
#endif
extension Date {
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
public struct IntervalFormatStyle : Codable, Hashable, Sendable {
public typealias DateStyle = Date.FormatStyle.DateStyle
public typealias TimeStyle = Date.FormatStyle.TimeStyle
public var locale: Locale
public var timeZone: TimeZone
public var calendar: Calendar
/// Creates a new `FormatStyle` with the given configurations.
/// - Parameters:
/// - date: The style for formatting the date part of the given date pairs. Note that if `.omitted` is specified, but the date interval spans more than one day, a locale-specific fallback will be used.
/// - time: The style for formatting the time part of the given date pairs.
/// - locale: The locale to use when formatting date and time values.
/// - calendar: The calendar to use for date values.
/// - timeZone: The time zone with which to specify date and time values.
/// - Important: Always specify the date length, time length, or the date components to be included in the formatted string with the symbol modifiers. Otherwise, an empty string will be returned when you use the instance to format an object.
/// - Note: If specifying the date fields, and the `DateInterval` range is larger than the specified units, a locale-specific fallback will be used.
/// - Example: for the range 2010-03-04 07:56 - 2010-03-08 16:11 (4 days, 8 hours, 15 minutes), specifying `.hour().minute()` will produce
/// - for en_US, "3/4/2010 7:56 AM - 3/8/2010 4:11 PM"
/// - for en_GB, "4/3/2010 7:56 - 8/3/2010 16:11"
public init(date: DateStyle? = nil, time: TimeStyle? = nil, locale: Locale = .autoupdatingCurrent, calendar: Calendar = .autoupdatingCurrent, timeZone: TimeZone = .autoupdatingCurrent) {
self.locale = locale
self.calendar = calendar
self.timeZone = timeZone
if let dateStyle = date {
self.symbols = self.symbols.collection(date: dateStyle)
}
if let timeStyle = time {
self.symbols = self.symbols.collection(time: timeStyle)
}
}
// MARK: - FormatStyle conformance
public func format(_ v: Range<Date>) -> String {
let formatter = Self.cache.formatter(for: self) {
var template = symbols.formatterTemplate(overridingDayPeriodWithLocale: locale)
if template.isEmpty {
let defaultSymbols = Date.FormatStyle.DateFieldCollection()
.collection(date: .numeric)
.collection(time: .shortened)
template = defaultSymbols.formatterTemplate(overridingDayPeriodWithLocale: locale)
}
return ICUDateIntervalFormatter(locale: locale, calendar: calendar, timeZone: timeZone, dateTemplate: template)
}
return formatter.string(from: v)
}
public func locale(_ locale: Locale) -> Self {
var new = self
new.locale = locale
return new
}
// Internal
private var symbols = Date.FormatStyle.DateFieldCollection()
private static let cache = FormatterCache<Self, ICUDateIntervalFormatter>()
}
}
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
extension Date.IntervalFormatStyle : FormatStyle {}
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
extension Date.IntervalFormatStyle {
public typealias Symbol = Date.FormatStyle.Symbol
public func year() -> Self {
var new = self
new.symbols.year = .defaultDigits
return new
}
public func month(_ format: Symbol.Month = .abbreviated) -> Self {
var new = self
new.symbols.month = format.option
return new
}
public func day() -> Self {
var new = self
new.symbols.day = .defaultDigits
return new
}
public func weekday(_ format: Symbol.Weekday = .abbreviated) -> Self {
var new = self
new.symbols.weekday = format.option
return new
}
public func hour(_ format: Symbol.Hour = .defaultDigits(amPM: .abbreviated)) -> Self {
var new = self
new.symbols.hour = format.option
return new
}
public func minute() -> Self {
var new = self
new.symbols.minute = .defaultDigits
return new
}
public func second() -> Self {
var new = self
new.symbols.second = .defaultDigits
return new
}
public func timeZone(_ format: Symbol.TimeZone = .genericName(.short)) -> Self {
var new = self
new.symbols.timeZoneSymbol = format.option
return new
}
}
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
public extension FormatStyle where Self == Date.IntervalFormatStyle {
static var interval: Self {
return Date.IntervalFormatStyle()
}
}
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
extension Range where Bound == Date {
/// Formats the date range as an interval.
public func formatted() -> String {
Date.IntervalFormatStyle().format(self)
}
/// Formats the date range using the specified date and time format styles.
public func formatted(date: Date.IntervalFormatStyle.DateStyle, time: Date.IntervalFormatStyle.TimeStyle) -> String {
Date.IntervalFormatStyle(date: date, time: time).format(self)
}
/// Formats the date range using the specified style.
public func formatted<S>(_ style: S) -> S.FormatOutput where S : FormatStyle, S.FormatInput == Range<Date> {
style.format(self)
}
}