宽窄优行-由【嘉易行】项目成品而来
younger_times
2023-04-06 a1ae6802080a22e6e6ce6d0935e95facb1daca5c
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
//
//  SwiftDate
//  Parse, validate, manipulate, and display dates, time and timezones in Swift
//
//  Created by Daniele Margutti
//   - Web: https://www.danielemargutti.com
//   - Twitter: https://twitter.com/danielemargutti
//   - Mail: hello@danielemargutti.com
//
//  Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
 
import Foundation
 
/// Region define a context both for `Date` and `DateInRegion`.
/// Each `Date` is assigned to the currently set `SwiftDate.default
public struct Region: Decodable, Encodable, Equatable, Hashable, CustomStringConvertible {
 
    // MARK: - Properties
 
    /// Calendar associated with region
    public let calendar: Calendar
 
    /// Locale associated with region
    public var locale: Locale { return calendar.locale! }
 
    /// Timezone associated with region
    public var timeZone: TimeZone { return calendar.timeZone }
 
    /// Description of the object
    public var description: String {
        return "{calendar='\(calendar.identifier)', timezone='\(timeZone.identifier)', locale='\(locale.identifier)'}"
    }
 
    public func hash(into hasher: inout Hasher) {
        hasher.combine(calendar)
    }
 
    // MARK: Initialization
 
    /// Initialize a new region with given parameters.
    ///
    /// - Parameters:
    ///   - calendar: calendar for region, if not specified `defaultRegions`'s calendar is used instead.
    ///   - timezone: timezone for region, if not specified `defaultRegions`'s timezone is used instead.
    ///   - locale: locale for region, if not specified `defaultRegions`'s locale is used instead.
    public init(calendar: CalendarConvertible = SwiftDate.defaultRegion.calendar,
                zone: ZoneConvertible = SwiftDate.defaultRegion.timeZone,
                locale: LocaleConvertible = SwiftDate.defaultRegion.locale) {
        self.calendar = Calendar.newCalendar(calendar, configure: {
            $0.timeZone = zone.toTimezone()
            $0.locale = locale.toLocale()
        })
    }
 
    /// Initialize a new Region by reading the `timeZone`,`calendar` and `locale`
    /// parameters from the passed `DateComponents` instance.
    /// For any `nil` parameter the correspondent `SwiftDate.defaultRegion` is used instead.
    ///
    /// - Parameter fromDateComponents: date components
    public init(fromDateComponents components: DateComponents) {
        let tz = (components.timeZone ?? Zones.current.toTimezone())
        let cal = (components.calendar ?? Calendars.gregorian.toCalendar())
        let loc = (cal.locale ?? Locales.current.toLocale())
        self.init(calendar: cal, zone: tz, locale: loc)
    }
 
    public static var UTC: Region {
        return Region(calendar: Calendar.autoupdatingCurrent,
                      zone: Zones.gmt.toTimezone(),
                      locale: Locale.autoupdatingCurrent)
    }
 
    /// Return the current local device's region where all attributes are set to the device's values.
    ///
    /// - Returns: Region
    public static var local: Region {
        return Region(calendar: Calendar.autoupdatingCurrent,
                      zone: TimeZone.autoupdatingCurrent,
                      locale: Locale.autoupdatingCurrent)
    }
 
    /// ISO Region is defined by the gregorian calendar, gmt timezone and english posix locale
    public static var ISO: Region {
        return Region(calendar: Calendars.gregorian.toCalendar(),
                      zone: Zones.gmt.toTimezone(),
                      locale: Locales.englishUnitedStatesComputer)
    }
 
    /// Return an auto updating region where all settings are obtained from the current's device settings.
    ///
    /// - Returns: Region
    public static var current: Region {
        return Region(calendar: Calendar.autoupdatingCurrent,
                      zone: TimeZone.autoupdatingCurrent,
                      locale: Locale.autoupdatingCurrent)
    }
 
    /// Return a new region in current's device timezone with optional adjust of the calendar and locale.
    ///
    /// - Parameters:
    ///   - locale: locale to set
    ///   - calendar: calendar to set
    /// - Returns: region
    public static func currentIn(locale: LocaleConvertible? = nil, calendar: CalendarConvertible? = nil) -> Region {
        return Region(calendar: (calendar ?? SwiftDate.defaultRegion.calendar),
                      zone: SwiftDate.defaultRegion.timeZone,
                      locale: (locale ?? SwiftDate.defaultRegion.locale))
    }
 
    /// Return the current date expressed into the receiver region.
    ///
    /// - Returns: `DateInRegion` instance
    public func nowInThisRegion() -> DateInRegion {
        return DateInRegion(Date(), region: self)
    }
 
    // MARK: - Codable Support
 
    enum CodingKeys: String, CodingKey {
        case calendar
        case locale
        case timezone
    }
 
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(calendar.timeZone.identifier, forKey: .timezone)
        try container.encode(calendar.locale!.identifier, forKey: .locale)
        try container.encode(calendar.identifier.description, forKey: .calendar)
    }
 
    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let calId = Calendar.Identifier( try values.decode(String.self, forKey: .calendar))
        let tz = (TimeZone(identifier: try values.decode(String.self, forKey: .timezone)) ?? SwiftDate.defaultRegion.timeZone)
        let lc = Locale(identifier: try values.decode(String.self, forKey: .locale))
        calendar = Calendar.newCalendar(calId, configure: {
            $0.timeZone = tz
            $0.locale = lc
        })
    }
 
    // MARK: - Comparable
 
    public static func == (lhs: Region, rhs: Region) -> Bool {
        // Note: equality does not consider other parameters than the identifier of the major
        // attributes (calendar, timezone and locale). Deeper comparisor must be made directly
        // between Calendar (it may fail when you encode/decode autoUpdating calendars).
        return
            (lhs.calendar.identifier == rhs.calendar.identifier) &&
            (lhs.timeZone.identifier == rhs.timeZone.identifier) &&
            (lhs.locale.identifier == rhs.locale.identifier)
    }
}