Skip to content

Commit 3fc9b82

Browse files
committed
Add NaiveDateTimeFormatter, NaiveDateTimeRangeFormatter
1 parent b82e5ad commit 3fc9b82

8 files changed

Lines changed: 215 additions & 79 deletions

File tree

NaiveDateTime.playground/Contents.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import NaiveDateTime
1111
let date = NaiveDate("2017-11-01")!
1212
Calendar.current.date(from: date)
1313

14+
NaiveDateFormatter(dateStyle: .short).string(from: date)
15+
1416

1517
/*:
1618
## `NaiveTime`
@@ -21,6 +23,7 @@ Calendar.current.date(from: date)
2123
let time = NaiveTime("15:00")!
2224
Calendar.current.date(from: time)
2325

26+
NaiveDateFormatter(timeStyle: .short).string(from: time)
2427

2528
/*:
2629
## `NaiveDateTime`
@@ -30,3 +33,5 @@ Calendar.current.date(from: time)
3033

3134
let dateTime = NaiveDateTime("2017-11-01T15:30:00")!
3235
Calendar.current.date(from: dateTime)
36+
37+
NaiveDateFormatter(dateStyle: .short, timeStyle: .short).string(from: dateTime)

NaiveDateTime.xcodeproj/project.pbxproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
0CAA5D3E1DB1495E001F6D96 /* NaiveDateTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5108211D4E81CE00F1F405 /* NaiveDateTime.framework */; };
1919
0CAA5D441DB1496B001F6D96 /* NaiveDateTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAA5D1F1DB14780001F6D96 /* NaiveDateTimeTests.swift */; };
2020
0CE6A8AF1FD1DB9700627ED1 /* Calendar+NaiveDateTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8AE1FD1DB9700627ED1 /* Calendar+NaiveDateTime.swift */; };
21+
0CE6A8B61FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8B51FD1E00900627ED1 /* NaiveDateTimeFormatter.swift */; };
22+
0CE6A8B71FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8B51FD1E00900627ED1 /* NaiveDateTimeFormatter.swift */; };
23+
0CE6A8B81FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8B51FD1E00900627ED1 /* NaiveDateTimeFormatter.swift */; };
24+
0CE6A8B91FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8B51FD1E00900627ED1 /* NaiveDateTimeFormatter.swift */; };
25+
0CE6A8BB1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8BA1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift */; };
26+
0CE6A8BC1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8BA1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift */; };
27+
0CE6A8BD1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE6A8BA1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift */; };
2128
/* End PBXBuildFile section */
2229

2330
/* Begin PBXContainerItemProxy section */
@@ -63,6 +70,8 @@
6370
0CAA5D461DB149FF001F6D96 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = "<group>"; };
6471
0CE052001D4E830700A12B45 /* NaiveDateTime.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NaiveDateTime.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6572
0CE6A8AE1FD1DB9700627ED1 /* Calendar+NaiveDateTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+NaiveDateTime.swift"; sourceTree = "<group>"; };
73+
0CE6A8B51FD1E00900627ED1 /* NaiveDateTimeFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaiveDateTimeFormatter.swift; sourceTree = "<group>"; };
74+
0CE6A8BA1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaiveDateTimeFormatterTest.swift; sourceTree = "<group>"; };
6675
/* End PBXFileReference section */
6776

6877
/* Begin PBXFrameworksBuildPhase section */
@@ -152,6 +161,7 @@
152161
children = (
153162
0C20715A1D4E83A700EA5BEF /* NaiveDateTime.swift */,
154163
0CE6A8AE1FD1DB9700627ED1 /* Calendar+NaiveDateTime.swift */,
164+
0CE6A8B51FD1E00900627ED1 /* NaiveDateTimeFormatter.swift */,
155165
);
156166
path = Sources;
157167
sourceTree = "<group>";
@@ -181,6 +191,7 @@
181191
children = (
182192
0CAA5D1E1DB14780001F6D96 /* Info.plist */,
183193
0CAA5D1F1DB14780001F6D96 /* NaiveDateTimeTests.swift */,
194+
0CE6A8BA1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift */,
184195
);
185196
path = Tests;
186197
sourceTree = "<group>";
@@ -479,13 +490,15 @@
479490
buildActionMask = 2147483647;
480491
files = (
481492
0C20715C1D4E83A700EA5BEF /* NaiveDateTime.swift in Sources */,
493+
0CE6A8B71FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */,
482494
);
483495
runOnlyForDeploymentPostprocessing = 0;
484496
};
485497
0C5108321D4E824A00F1F405 /* Sources */ = {
486498
isa = PBXSourcesBuildPhase;
487499
buildActionMask = 2147483647;
488500
files = (
501+
0CE6A8B61FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */,
489502
0CE6A8AF1FD1DB9700627ED1 /* Calendar+NaiveDateTime.swift in Sources */,
490503
0C20715B1D4E83A700EA5BEF /* NaiveDateTime.swift in Sources */,
491504
);
@@ -496,13 +509,15 @@
496509
buildActionMask = 2147483647;
497510
files = (
498511
0C20715D1D4E83A700EA5BEF /* NaiveDateTime.swift in Sources */,
512+
0CE6A8B81FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */,
499513
);
500514
runOnlyForDeploymentPostprocessing = 0;
501515
};
502516
0CAA5D0E1DB14766001F6D96 /* Sources */ = {
503517
isa = PBXSourcesBuildPhase;
504518
buildActionMask = 2147483647;
505519
files = (
520+
0CE6A8BB1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift in Sources */,
506521
0CAA5D211DB14780001F6D96 /* NaiveDateTimeTests.swift in Sources */,
507522
);
508523
runOnlyForDeploymentPostprocessing = 0;
@@ -511,6 +526,7 @@
511526
isa = PBXSourcesBuildPhase;
512527
buildActionMask = 2147483647;
513528
files = (
529+
0CE6A8BC1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift in Sources */,
514530
0CAA5D331DB14912001F6D96 /* NaiveDateTimeTests.swift in Sources */,
515531
);
516532
runOnlyForDeploymentPostprocessing = 0;
@@ -519,6 +535,7 @@
519535
isa = PBXSourcesBuildPhase;
520536
buildActionMask = 2147483647;
521537
files = (
538+
0CE6A8BD1FD1E69400627ED1 /* NaiveDateTimeFormatterTest.swift in Sources */,
522539
0CAA5D441DB1496B001F6D96 /* NaiveDateTimeTests.swift in Sources */,
523540
);
524541
runOnlyForDeploymentPostprocessing = 0;
@@ -528,6 +545,7 @@
528545
buildActionMask = 2147483647;
529546
files = (
530547
0C20715E1D4E83A700EA5BEF /* NaiveDateTime.swift in Sources */,
548+
0CE6A8B91FD1E00900627ED1 /* NaiveDateTimeFormatter.swift in Sources */,
531549
);
532550
runOnlyForDeploymentPostprocessing = 0;
533551
};

README.md

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,55 +23,58 @@ Each of the provided types implements `Equatable`, `Comparable`, `LosslessString
2323

2424
**Important!** The naive types do not perform any validation of the input components (year, hour, etc). If you do need to do any precise manipulations with time use native `Date` and `Calendar` types.
2525

26-
### `NaiveDate`
26+
### Create
2727

28-
Calendar date without timezone.
28+
Naive dates and times can be created either from strings (using a predefined format) or by using a memberwise method:
2929

3030
```swift
31-
NaiveDate(year: 2017, month: 10, day: 1)
3231
NaiveDate("2017-10-01")
33-
print(NaiveDate(year: 2017, month: 10, day: 1) // prints "2017-10-01"
32+
NaiveDate(year: 2017, month: 10, day: 1)
33+
34+
NaiveTime("15:30:00")
35+
NaiveTime(hour: 15, minute: 30, second: 0)
36+
37+
NaiveDateTime("2017-10-01T15:30")
38+
NaiveDateTime(
39+
date: NaiveDate(year: 2017, month: 10, day: 1),
40+
time: NaiveTime(hour: 15, minute: 30, second: 0)
41+
)
3442
```
3543

36-
When you need time zones, convert `NaiveDate` to `Date`:
44+
### Format
45+
46+
Format dates without having to worry about time zones:
3747

3848
```swift
39-
let date = NaiveDate(year: 2017, month: 10, day: 1)
49+
let date = NaiveDate("2017-11-01")!
50+
NaiveDateFormatter(dateStyle: .short).string(from: date)
51+
// prints "11/1/17"
4052

41-
// Creates `Date` in a calendar's time zone
42-
// "2017-10-01T00:00:00+0300" if user is in MSK
43-
Calendar.current.date(from: date)
53+
let time = NaiveTime("15:00")!
54+
NaiveDateFormatter(timeStyle: .short).string(from: time)
55+
// prints "3:00 PM"
4456

45-
// Creates `Date` with +0000 offset
46-
// "2017-10-01T00:00:00Z"
47-
Calendar.current.date(from: date, in: TimeZone(secondsFromGMT: 0)!)
57+
let dateTime = NaiveDateTime("2017-11-01T15:30:00")!
58+
NaiveDateFormatter(dateStyle: .short, timeStyle: .short).string(from: dateTime)
59+
// prints "11/1/17, 3:30 PM"
4860
```
4961

62+
### Convert
5063

51-
### `NaiveTime`
52-
53-
Time without timezone.
64+
When you do need time zones, convert `NaiveDate` to `Date`:
5465

5566
```swift
56-
NaiveTime(hour: 15, minute: 30, second: 0)
57-
NaiveTime("15:30:00")
58-
```
59-
60-
61-
### `NaiveDateTime`
67+
let date = NaiveDate(year: 2017, month: 10, day: 1)
6268

63-
Combined date and time without timezone.
69+
// Creates `Date` in a calendar's time zone
70+
// "2017-10-01T00:00:00+0300" if user is in MSK
71+
Calendar.current.date(from: date)
6472

65-
```swift
66-
NaiveDateTime(
67-
date: NaiveDate(year: 2017, month: 10, day: 1),
68-
time: NaiveTime(hour: 15, minute: 30, second: 0)
69-
)
70-
NaiveDateTime("2017-10-01T15:30")
73+
// Creates `Date` with +0000 offset
74+
// "2017-10-01T00:00:00Z"
75+
Calendar.current.date(from: date, in: TimeZone(secondsFromGMT: 0)!)
7176
```
7277

73-
When you need time zones, convert `NaiveDateTime` to `Date`:
74-
7578
```swift
7679
let dateTime = NaiveDateTime(
7780
date: NaiveDate(year: 2017, month: 10, day: 1),

Sources/Calendar+NaiveDateTime.swift

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@ public extension Calendar {
1111
/// Returns a date created from the specified naive date in a given time zone.
1212
/// - parameter timeZone: `nil` by default (uses Calendar time zone).
1313
public func date(from date: NaiveDate, in timeZone: TimeZone? = nil) -> Date? {
14-
return self.date(from: DateComponents(self, timeZone: timeZone, date: date))
14+
return _date(from: date, in: timeZone)
1515
}
1616

1717
/// Returns a date created from the specified naive time in a given time zone.
1818
/// - parameter timeZone: `nil` by default (uses Calendar time zone).
1919
public func date(from time: NaiveTime, in timeZone: TimeZone? = nil) -> Date? {
20-
return self.date(from: DateComponents(self, timeZone: timeZone, time: time))
20+
return _date(from: time, in: timeZone)
2121
}
2222

2323
/// Returns a date created from the specified naive datetime in a given time zone.
2424
/// - parameter timeZone: `nil` by default (uses Calendar time zone).
2525
public func date(from dateTime: NaiveDateTime, in timeZone: TimeZone? = nil) -> Date? {
26-
return self.date(from: DateComponents(self, timeZone: timeZone, dateTime: dateTime))
26+
return _date(from: dateTime, in: timeZone)
27+
}
28+
29+
internal func _date<T: _DateComponentsConvertible>(from value: T, in timeZone: TimeZone? = nil) -> Date? {
30+
return self.date(from: value.dateComponents(timeZone: timeZone))
2731
}
2832
}
2933

@@ -54,21 +58,3 @@ public extension Calendar {
5458
)
5559
}
5660
}
57-
58-
// MARK: - Naive* -> DateComponents
59-
60-
public extension DateComponents {
61-
public init(_ calendar: Calendar? = nil, timeZone: TimeZone? = nil, date: NaiveDate) {
62-
self.init(calendar: calendar, timeZone: timeZone, year: date.year, month: date.month, day: date.day)
63-
}
64-
65-
public init(_ calendar: Calendar? = nil, timeZone: TimeZone? = nil, time: NaiveTime) {
66-
self.init(calendar: calendar, timeZone: timeZone, hour: time.hour, minute: time.minute, second: time.second)
67-
}
68-
69-
public init(_ calendar: Calendar? = nil, timeZone: TimeZone? = nil, dateTime: NaiveDateTime) {
70-
let date = dateTime.date
71-
let time = dateTime.time
72-
self.init(calendar: calendar, timeZone: timeZone, year: date.year, month: date.month, day: date.day, hour: time.hour, minute: time.minute, second: time.second)
73-
}
74-
}

Sources/NaiveDateTime.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Foundation
88
// MARK: - NaiveDate -
99

1010
/// Calendar date without a timezone.
11-
public struct NaiveDate: Equatable, Hashable, Comparable, LosslessStringConvertible, Codable {
11+
public struct NaiveDate: Equatable, Hashable, Comparable, LosslessStringConvertible, Codable, _DateComponentsConvertible {
1212
public let year: Int, month: Int, day: Int
1313

1414
/// Initializes the naive date with a given date components.
@@ -58,13 +58,19 @@ public struct NaiveDate: Equatable, Hashable, Comparable, LosslessStringConverti
5858
public func encode(to encoder: Encoder) throws {
5959
try _encode(self, to: encoder)
6060
}
61+
62+
// MARK: _DateComponentsConvertible
63+
64+
public func dateComponents(timeZone: TimeZone? = nil) -> DateComponents {
65+
return DateComponents(timeZone: timeZone, year: year, month: month, day: day)
66+
}
6167
}
6268

6369

6470
// MARK: - NaiveTime -
6571

6672
/// Time without a timezone. Allows for second precision.
67-
public struct NaiveTime: Equatable, Hashable, Comparable, LosslessStringConvertible, Codable {
73+
public struct NaiveTime: Equatable, Hashable, Comparable, LosslessStringConvertible, Codable, _DateComponentsConvertible {
6874
public let hour: Int, minute: Int, second: Int
6975

7076
/// Initializes the naive time with a given date components.
@@ -137,13 +143,19 @@ public struct NaiveTime: Equatable, Hashable, Comparable, LosslessStringConverti
137143
public func encode(to encoder: Encoder) throws {
138144
try _encode(self, to: encoder)
139145
}
146+
147+
// MARK: _DateComponentsConvertible
148+
149+
public func dateComponents(timeZone: TimeZone? = nil) -> DateComponents {
150+
return DateComponents(timeZone: timeZone, hour: hour, minute: minute, second: second)
151+
}
140152
}
141153

142154

143155
// MARK: - NaiveDateTime -
144156

145157
/// Combined date and time without timezone.
146-
public struct NaiveDateTime: Equatable, Hashable, Comparable, LosslessStringConvertible, Codable {
158+
public struct NaiveDateTime: Equatable, Hashable, Comparable, LosslessStringConvertible, Codable, _DateComponentsConvertible {
147159
public let date: NaiveDate
148160
public let time: NaiveTime
149161

@@ -193,11 +205,22 @@ public struct NaiveDateTime: Equatable, Hashable, Comparable, LosslessStringConv
193205
public func encode(to encoder: Encoder) throws {
194206
try _encode(self, to: encoder)
195207
}
208+
209+
// MARK: _DateComponentsConvertible
210+
211+
public func dateComponents(timeZone: TimeZone? = nil) -> DateComponents {
212+
return DateComponents(timeZone: timeZone, year: date.year, month: date.month, day: date.day, hour: time.hour, minute: time.minute, second: time.second)
213+
}
196214
}
197215

198216

199217
// MARK: - Private -
200218

219+
/// A type that can be converted to DateComponents (and in turn to Date).
220+
internal protocol _DateComponentsConvertible {
221+
func dateComponents(timeZone: TimeZone?) -> DateComponents
222+
}
223+
201224
private func _decode<T: LosslessStringConvertible>(from decoder: Decoder) throws -> T {
202225
let container = try decoder.singleValueContainer()
203226
let string = try container.decode(String.self)

0 commit comments

Comments
 (0)