宽窄优行-由【嘉易行】项目成品而来
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//
//  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
 
public class ISOFormatter: DateToStringTrasformable {
 
    public struct Options: OptionSet {
        public let rawValue: Int
 
        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
 
        /// The date representation includes the year. The format for year is inferred based on the other specified options.
        /// - If withWeekOfYear is specified, YYYY is used.
        /// - Otherwise, yyyy is used.
        public static let withYear = ISOFormatter.Options(rawValue: 1 << 0)
 
        /// The date representation includes the month. The format for month is MM.
        public static let withMonth = ISOFormatter.Options(rawValue: 1 << 1)
 
        /// The date representation includes the week of the year.
        /// The format for week of year is ww, including the W prefix.
        public static let withWeekOfYear = ISOFormatter.Options(rawValue: 1 << 2)
 
        /// The date representation includes the day. The format for day is inferred based on provided options:
        /// - If withMonth is specified, dd is used.
        /// - If withWeekOfYear is specified, ee is used.
        /// - Otherwise, DDD is used.
        public static let withDay = ISOFormatter.Options(rawValue: 1 << 3)
 
        /// The date representation includes the time. The format for time is HH:mm:ss.
        public static let withTime = ISOFormatter.Options(rawValue: 1 << 4)
 
        /// The date representation includes the timezone. The format for timezone is ZZZZZ.
        public static let withTimeZone = ISOFormatter.Options(rawValue: 1 << 5)
 
        /// The date representation uses a space ( ) instead of T between the date and time.
        public static let withSpaceBetweenDateAndTime = ISOFormatter.Options(rawValue: 1 << 6)
 
        /// The date representation uses the dash separator (-) in the date.
        public static let withDashSeparatorInDate = ISOFormatter.Options(rawValue: 1 << 7)
 
        /// The date representation uses the colon separator (:) in the time.
        public static let withFullDate = ISOFormatter.Options(rawValue: 1 << 8)
 
        /// The date representation includes the hour, minute, and second.
        public static let withFullTime = ISOFormatter.Options(rawValue: 1 << 9)
 
        /// The format used for internet date times, according to the RFC 3339 standard.
        /// Equivalent to specifying withFullDate, withFullTime, withDashSeparatorInDate,
        /// withColonSeparatorInTime, and withColonSeparatorInTimeZone.
        public static let withInternetDateTime = ISOFormatter.Options(rawValue: 1 << 10)
 
        // The format used for internet date times; it's similar to .withInternetDateTime
        // but include milliseconds ('yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ').
        public static let withInternetDateTimeExtended = ISOFormatter.Options(rawValue: 1 << 11)
 
        /// Print the timezone in format `ZZZ` instead of `ZZZZZ`
        /// An example outout maybe be `+0200` instead of `+02:00`.
        public static let withoutTZSeparators = ISOFormatter.Options(rawValue: 1 << 12)
 
        /// Evaluate formatting string
        public var dateFormat: String {
            if contains(.withInternetDateTimeExtended) || contains(.withoutTZSeparators) {
                if contains(.withoutTZSeparators) {
                    return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"
                }
                return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
            }
 
            if contains(.withInternetDateTime) {
                if contains(.withoutTZSeparators) {
                    return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"
                }
                return "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
            }
 
            var format: String = ""
            if contains(.withFullDate) {
                format += "yyyy-MM-dd"
            } else {
                if contains(.withYear) {
                    if contains(.withWeekOfYear) {
                        format += "YYYY"
                    } else if contains(.withMonth) || contains(.withDay) {
                        format += "yyyy"
                    } else {
                        // not valid
                    }
                }
                if contains(.withMonth) {
                    if contains(.withYear) || contains(.withDay) || contains(.withWeekOfYear) {
                        format += "MM"
                    } else {
                        // not valid
                    }
                }
                if contains(.withWeekOfYear) {
                    if contains(.withDay) {
                        format += "'W'ww"
                    } else {
                        if contains(.withYear) || contains(.withMonth) {
                            if contains(.withDashSeparatorInDate) {
                                format += "-'W'ww"
                            } else {
                                format += "'W'ww"
                            }
                        } else {
                            // not valid
                        }
                    }
                }
 
                if contains(.withDay) {
                    if contains(.withWeekOfYear) {
                        format += "FF"
                    } else if contains(.withMonth) {
                        format += "dd"
                    } else if contains(.withYear) {
                        if contains(.withDashSeparatorInDate) {
                            format += "-DDD"
                        } else {
                            format += "DDD"
                        }
                    } else {
                        // not valid
                    }
                }
            }
 
            let hasDate = (contains(.withFullDate) || contains(.withMonth) || contains(.withDay) || contains(.withWeekOfYear) || contains(.withYear))
            if hasDate && (contains(.withFullTime) || contains(.withTimeZone) || contains(.withTime)) {
                if contains(.withSpaceBetweenDateAndTime) {
                    format += " "
                } else {
                    format += "'T'"
                }
            }
 
            if contains(.withFullTime) {
                format += "HH:mm:ssZZZZZ"
            } else {
                if contains(.withTime) {
                    format += "HH:mm:ss"
                }
                if contains(.withTimeZone) {
                    if contains(.withoutTZSeparators) {
                        return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"
                    }
                    format += "ZZZZZ"
                }
            }
 
            return format
        }
    }
 
    public static func format(_ date: DateRepresentable, options: Any?) -> String {
        let formatOptions = ((options as? ISOFormatter.Options) ?? ISOFormatter.Options([.withInternetDateTime]))
        let formatter = date.formatter(format: formatOptions.dateFormat) {
            $0.locale = Locales.englishUnitedStatesComputer.toLocale() // fix for 12/24h
            $0.timeZone = date.region.timeZone
            $0.calendar = Calendars.gregorian.toCalendar()
        }
        return formatter.string(from: date.date)
    }
 
}