Skip to content

Commit 206b672

Browse files
authored
chore: kickoff v1 release
2 parents d43b504 + d35e9a7 commit 206b672

File tree

23 files changed

+434
-105
lines changed

23 files changed

+434
-105
lines changed

Amplify.xcodeproj/project.pbxproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 51;
6+
objectVersion = 54;
77
objects = {
88

99
/* Begin PBXBuildFile section */
@@ -293,6 +293,7 @@
293293
5C763DAE26F2D00F006650E7 /* Geo+ResultsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C763DAD26F2D00F006650E7 /* Geo+ResultsHandler.swift */; };
294294
5CB5DD27271707780078CCA2 /* Geo+SearchOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB5DD26271707780078CCA2 /* Geo+SearchOptions.swift */; };
295295
5CF43D092728C64100F636E1 /* Geo+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF43D082728C64100F636E1 /* Geo+Error.swift */; };
296+
609A3CAC2B290344006830C7 /* TimeZone+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 609A3CAB2B290344006830C7 /* TimeZone+Extension.swift */; };
296297
6B33896823AAACC900561E5B /* ReachabilityUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B33896723AAACC900561E5B /* ReachabilityUpdate.swift */; };
297298
6B452B8225A7D0F600A1A811 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B452B8125A7D0F600A1A811 /* Array+Extensions.swift */; };
298299
6B5087BD2565E5AD000AB673 /* QueryPredicateEvaluateGeneratedDoubleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5087BC2565E5AD000AB673 /* QueryPredicateEvaluateGeneratedDoubleTests.swift */; };
@@ -1262,6 +1263,7 @@
12621263
5C763DAD26F2D00F006650E7 /* Geo+ResultsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Geo+ResultsHandler.swift"; sourceTree = "<group>"; };
12631264
5CB5DD26271707780078CCA2 /* Geo+SearchOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Geo+SearchOptions.swift"; sourceTree = "<group>"; };
12641265
5CF43D082728C64100F636E1 /* Geo+Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Geo+Error.swift"; sourceTree = "<group>"; };
1266+
609A3CAB2B290344006830C7 /* TimeZone+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TimeZone+Extension.swift"; sourceTree = "<group>"; };
12651267
614D1E66BBE236DDD4F8E2E0 /* Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsCoreTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsCoreTests.debug.xcconfig"; path = "Target Support Files/Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsCoreTests/Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsCoreTests.debug.xcconfig"; sourceTree = "<group>"; };
12661268
6B33896723AAACC900561E5B /* ReachabilityUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReachabilityUpdate.swift; sourceTree = "<group>"; };
12671269
6B452B8125A7D0F600A1A811 /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = "<group>"; };
@@ -2872,6 +2874,7 @@
28722874
902AE04B281304B800CD12CA /* Temporal */ = {
28732875
isa = PBXGroup;
28742876
children = (
2877+
609A3CAB2B290344006830C7 /* TimeZone+Extension.swift */,
28752878
902AE0702813052F00CD12CA /* DataStoreError+Temporal.swift */,
28762879
9091FF6A2820771B0021D8E1 /* Date.swift */,
28772880
9091FF762820771B0021D8E1 /* Date+Operation.swift */,
@@ -5606,6 +5609,7 @@
56065609
9091FF8B2820771C0021D8E1 /* TemporalOperation.swift in Sources */,
56075610
769CF2242669B1B9007843A0 /* RetryableGraphQLOperation.swift in Sources */,
56085611
B4251A0124250369007F59EF /* AuthConfirmResetPasswordRequest.swift in Sources */,
5612+
609A3CAC2B290344006830C7 /* TimeZone+Extension.swift in Sources */,
56095613
FAAFAF2F23904B14002CF932 /* AtomicValue+Bool.swift in Sources */,
56105614
211FFEE326CD650500F0DB75 /* DataStoreQuerySnapshot.swift in Sources */,
56115615
FA249EEB24C5FE66009B3CE8 /* AmplifyAPICategory+GraphQLBehavior+Combine.swift in Sources */,

Amplify/Categories/DataStore/Model/Internal/Model+DateFormatting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public struct ModelDateFormatting {
2727
public static let encodingStrategy: JSONEncoder.DateEncodingStrategy = {
2828
let strategy = JSONEncoder.DateEncodingStrategy.custom { date, encoder in
2929
var container = encoder.singleValueContainer()
30-
try container.encode(Temporal.DateTime(date).iso8601String)
30+
try container.encode(Temporal.DateTime(date, timeZone: .utc).iso8601String)
3131
}
3232
return strategy
3333
}()

Amplify/Categories/DataStore/Model/Temporal/Date.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ extension Temporal {
2121
// Inherits documentation from `TemporalSpec`
2222
public let foundationDate: Foundation.Date
2323

24+
// Inherits documentation from `TemporalSpec`
25+
public let timeZone: TimeZone? = .utc
26+
2427
// Inherits documentation from `TemporalSpec`
2528
public static func now() -> Self {
26-
Temporal.Date(Foundation.Date())
29+
Temporal.Date(Foundation.Date(), timeZone: .utc)
2730
}
2831

2932
// Inherits documentation from `TemporalSpec`
30-
public init(_ date: Foundation.Date) {
33+
public init(_ date: Foundation.Date, timeZone: TimeZone?) {
3134
self.foundationDate = Temporal
3235
.iso8601Calendar
3336
.startOfDay(for: date)

Amplify/Categories/DataStore/Model/Temporal/DateTime.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,29 @@ extension Temporal {
1818
// Inherits documentation from `TemporalSpec`
1919
public let foundationDate: Foundation.Date
2020

21+
// Inherits documentation from `TemporalSpec`
22+
public let timeZone: TimeZone?
23+
2124
// Inherits documentation from `TemporalSpec`
2225
public static func now() -> Self {
23-
Temporal.DateTime(Foundation.Date())
26+
Temporal.DateTime(Foundation.Date(), timeZone: .utc)
2427
}
2528

2629
/// `Temporal.Time` of this `Temporal.DateTime`.
2730
public var time: Time {
28-
Time(foundationDate)
31+
Time(foundationDate, timeZone: timeZone)
2932
}
3033

3134
// Inherits documentation from `TemporalSpec`
32-
public init(_ date: Foundation.Date) {
35+
public init(_ date: Foundation.Date, timeZone: TimeZone?) {
3336
let calendar = Temporal.iso8601Calendar
3437
let components = calendar.dateComponents(
3538
DateTime.iso8601DateComponents,
3639
from: date
3740
)
3841

39-
foundationDate = calendar
42+
self.timeZone = timeZone
43+
self.foundationDate = calendar
4044
.date(from: components) ?? date
4145
}
4246

Amplify/Categories/DataStore/Model/Temporal/SpecBasedDateConverting.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Foundation
1212
@usableFromInline
1313
internal struct SpecBasedDateConverting<Spec: TemporalSpec> {
1414
@usableFromInline
15-
internal typealias DateConverter = (_ string: String, _ format: TemporalFormat?) throws -> Date
15+
internal typealias DateConverter = (_ string: String, _ format: TemporalFormat?) throws -> (Date, TimeZone)
1616

1717
@usableFromInline
1818
internal let convert: DateConverter
@@ -28,8 +28,9 @@ internal struct SpecBasedDateConverting<Spec: TemporalSpec> {
2828
internal static func `default`(
2929
iso8601String: String,
3030
format: TemporalFormat? = nil
31-
) throws -> Date {
31+
) throws -> (Date, TimeZone) {
3232
let date: Foundation.Date
33+
let tz = TimeZone(iso8601DateString: iso8601String) ?? .utc
3334
if let format = format {
3435
date = try Temporal.date(
3536
from: iso8601String,
@@ -41,6 +42,6 @@ internal struct SpecBasedDateConverting<Spec: TemporalSpec> {
4142
with: TemporalFormat.sortedFormats(for: Spec.self)
4243
)
4344
}
44-
return date
45+
return (date, tz)
4546
}
4647
}

Amplify/Categories/DataStore/Model/Temporal/Temporal+Comparable.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import Foundation
1515
extension TemporalSpec where Self: Comparable {
1616

1717
public static func == (lhs: Self, rhs: Self) -> Bool {
18-
return lhs.iso8601String == rhs.iso8601String
18+
return lhs.iso8601FormattedString(format: .full, timeZone: .utc)
19+
== rhs.iso8601FormattedString(format: .full, timeZone: .utc)
1920
}
2021

2122
public static func < (lhs: Self, rhs: Self) -> Bool {
22-
return lhs.iso8601String < rhs.iso8601String
23+
return lhs.iso8601FormattedString(format: .full, timeZone: .utc)
24+
< rhs.iso8601FormattedString(format: .full, timeZone: .utc)
2325
}
2426
}
2527

Amplify/Categories/DataStore/Model/Temporal/Temporal.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public protocol TemporalSpec {
2727
/// by a Foundation `Date` instance.
2828
var foundationDate: Foundation.Date { get }
2929

30+
/// The timezone field is an optional field used to specify the timezone associated
31+
/// with a particular date.
32+
var timeZone: TimeZone? { get }
33+
3034
/// The ISO-8601 formatted string in the UTC `TimeZone`.
3135
/// - SeeAlso: `iso8601FormattedString(TemporalFormat, TimeZone) -> String`
3236
var iso8601String: String { get }
@@ -57,7 +61,7 @@ public protocol TemporalSpec {
5761
/// Constructs a `TemporalSpec` from a `Date` object.
5862
/// - Parameter date: The `Date` instance that will be used as the reference of the
5963
/// `TemporalSpec` instance.
60-
init(_ date: Foundation.Date)
64+
init(_ date: Foundation.Date, timeZone: TimeZone?)
6165

6266
/// A string representation of the underlying date formatted using ISO8601 rules.
6367
///
@@ -90,25 +94,25 @@ extension TemporalSpec {
9094
/// The ISO8601 representation of the scalar using `.full` as the format and `.utc` as `TimeZone`.
9195
/// - SeeAlso: `iso8601FormattedString(format:timeZone:)`
9296
public var iso8601String: String {
93-
iso8601FormattedString(format: .full)
97+
iso8601FormattedString(format: .full, timeZone: timeZone ?? .utc)
9498
}
9599

96100
@inlinable
97101
public init(iso8601String: String, format: TemporalFormat) throws {
98-
let date = try SpecBasedDateConverting<Self>()
102+
let (date, tz) = try SpecBasedDateConverting<Self>()
99103
.convert(iso8601String, format)
100104

101-
self.init(date)
105+
self.init(date, timeZone: tz)
102106
}
103107

104108
@inlinable
105109
public init(
106110
iso8601String: String
107111
) throws {
108-
let date = try SpecBasedDateConverting<Self>()
112+
let (date, tz) = try SpecBasedDateConverting<Self>()
109113
.convert(iso8601String, nil)
110114

111-
self.init(date)
115+
self.init(date, timeZone: tz)
112116
}
113117
}
114118

Amplify/Categories/DataStore/Model/Temporal/TemporalOperation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ extension TemporalSpec {
3333
"""
3434
)
3535
}
36-
return Self.init(date)
36+
return Self.init(date, timeZone: timeZone)
3737
}
3838
}

Amplify/Categories/DataStore/Model/Temporal/Time.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ extension Temporal {
1818
// Inherits documentation from `TemporalSpec`
1919
public let foundationDate: Foundation.Date
2020

21+
// Inherits documentation from `TemporalSpec`
22+
public let timeZone: TimeZone? = .utc
23+
2124
// Inherits documentation from `TemporalSpec`
2225
public static func now() -> Self {
23-
Temporal.Time(Foundation.Date())
26+
Temporal.Time(Foundation.Date(), timeZone: .utc)
2427
}
2528

2629
// Inherits documentation from `TemporalSpec`
27-
public init(_ date: Foundation.Date) {
30+
public init(_ date: Foundation.Date, timeZone: TimeZone?) {
2831
// Sets the date to a fixed instant so time-only operations are safe
2932
let calendar = Temporal.iso8601Calendar
3033
var components = calendar.dateComponents(
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Foundation
9+
10+
extension TimeZone {
11+
12+
@usableFromInline
13+
internal init?(iso8601DateString: String) {
14+
switch ISO8601TimeZonePart.from(iso8601DateString: iso8601DateString) {
15+
case .some(.utc):
16+
self.init(abbreviation: "UTC")
17+
case let .some(.hh(hours: hours)):
18+
self.init(secondsFromGMT: hours * 60 * 60)
19+
case let .some(.hhmm(hours: hours, minutes: minutes)),
20+
let .some(.HHMM(hours: hours, minuts: minutes)):
21+
self.init(secondsFromGMT: hours * 60 * 60 +
22+
(hours > 0 ? 1 : -1) * minutes * 60)
23+
case let .some(.HHMMSS(hours: hours, minutes: minutes, seconds: seconds)):
24+
self.init(secondsFromGMT: hours * 60 * 60 +
25+
(hours > 0 ? 1 : -1) * minutes * 60 +
26+
(hours > 0 ? 1 : -1) * seconds)
27+
case .none:
28+
return nil
29+
}
30+
}
31+
}
32+
33+
34+
/// ISO8601 Time Zone formats
35+
/// - Note:
36+
/// `±hh:mm:ss` is not a standard of ISO8601 date formate. It's supported by `AWSDateTime` exclusively.
37+
///
38+
/// references:
39+
/// https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators
40+
/// https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html#graph-ql-aws-appsync-scalars
41+
private enum ISO8601TimeZoneFormat {
42+
case utc, hh, hhmm, HHMM, HHMMSS
43+
44+
var format: String {
45+
switch self {
46+
case .utc:
47+
return "Z"
48+
case .hh:
49+
return "±hh"
50+
case .hhmm:
51+
return "±hhmm"
52+
case .HHMM:
53+
return "±hh:mm"
54+
case .HHMMSS:
55+
return "±hh:mm:ss"
56+
}
57+
}
58+
59+
var regex: NSRegularExpression? {
60+
switch self {
61+
case .utc:
62+
return try? NSRegularExpression(pattern: "^Z$")
63+
case .hh:
64+
return try? NSRegularExpression(pattern: "^[+-]\\d{2}$")
65+
case .hhmm:
66+
return try? NSRegularExpression(pattern: "^[+-]\\d{2}\\d{2}$")
67+
case .HHMM:
68+
return try? NSRegularExpression(pattern: "^[+-]\\d{2}:\\d{2}$")
69+
case .HHMMSS:
70+
return try? NSRegularExpression(pattern: "^[+-]\\d{2}:\\d{2}:\\d{2}$")
71+
}
72+
}
73+
74+
var parts: [NSRange] {
75+
switch self {
76+
case .utc:
77+
return []
78+
case .hh:
79+
return [NSRange(location: 0, length: 3)]
80+
case .hhmm:
81+
return [
82+
NSRange(location: 0, length: 3),
83+
NSRange(location: 3, length: 2)
84+
]
85+
case .HHMM:
86+
return [
87+
NSRange(location: 0, length: 3),
88+
NSRange(location: 4, length: 2)
89+
]
90+
case .HHMMSS:
91+
return [
92+
NSRange(location: 0, length: 3),
93+
NSRange(location: 4, length: 2),
94+
NSRange(location: 7, length: 2)
95+
]
96+
}
97+
}
98+
}
99+
100+
private enum ISO8601TimeZonePart {
101+
case utc
102+
case hh(hours: Int)
103+
case hhmm(hours: Int, minutes: Int)
104+
case HHMM(hours: Int, minuts: Int)
105+
case HHMMSS(hours: Int, minutes: Int, seconds: Int)
106+
107+
static func from(iso8601DateString: String) -> ISO8601TimeZonePart? {
108+
return tryExtract(from: iso8601DateString, with: .utc)
109+
?? tryExtract(from: iso8601DateString, with: .hh)
110+
?? tryExtract(from: iso8601DateString, with: .hhmm)
111+
?? tryExtract(from: iso8601DateString, with: .HHMM)
112+
?? tryExtract(from: iso8601DateString, with: .HHMMSS)
113+
?? nil
114+
}
115+
}
116+
117+
private func tryExtract(
118+
from dateString: String,
119+
with format: ISO8601TimeZoneFormat
120+
) -> ISO8601TimeZonePart? {
121+
guard dateString.count > format.format.count else {
122+
return nil
123+
}
124+
125+
let tz = String(dateString.dropFirst(dateString.count - format.format.count))
126+
127+
guard format.regex.flatMap({
128+
$0.firstMatch(in: tz, range: NSRange(location: 0, length: tz.count))
129+
}) != nil else {
130+
return nil
131+
}
132+
133+
let parts = format.parts.compactMap { range in
134+
Range(range, in: tz).flatMap { Int(tz[$0]) }
135+
}
136+
137+
guard parts.count == format.parts.count else {
138+
return nil
139+
}
140+
141+
switch format {
142+
case .utc: return .utc
143+
case .hh: return .hh(hours: parts[0])
144+
case .hhmm: return .hhmm(hours: parts[0], minutes: parts[1])
145+
case .HHMM: return .HHMM(hours: parts[0], minuts: parts[1])
146+
case .HHMMSS: return .HHMMSS(hours: parts[0], minutes: parts[1], seconds: parts[2])
147+
}
148+
}

0 commit comments

Comments
 (0)