Skip to content

Commit 27a3a86

Browse files
authored
Improve Date parser demo (#305)
* Improve `Date` parser demo Incorporates a helper from @juri's [parser](https://github.com/juri/Parse3339) to improve things a bit. * wip * wip
1 parent d858288 commit 27a3a86

File tree

3 files changed

+110
-101
lines changed

3 files changed

+110
-101
lines changed

Package.resolved

+38-49
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,41 @@
11
{
2-
"object": {
3-
"pins": [
4-
{
5-
"package": "swift-argument-parser",
6-
"repositoryURL": "https://github.com/apple/swift-argument-parser",
7-
"state": {
8-
"branch": null,
9-
"revision": "6b2aa2748a7881eebb9f84fb10c01293e15b52ca",
10-
"version": "0.5.0"
11-
}
12-
},
13-
{
14-
"package": "Benchmark",
15-
"repositoryURL": "https://github.com/google/swift-benchmark",
16-
"state": {
17-
"branch": null,
18-
"revision": "a0564bf88df5f94eec81348a2f089494c6b28d80",
19-
"version": "0.1.1"
20-
}
21-
},
22-
{
23-
"package": "swift-case-paths",
24-
"repositoryURL": "https://github.com/pointfreeco/swift-case-paths",
25-
"state": {
26-
"branch": null,
27-
"revision": "241301b67d8551c26d8f09bd2c0e52cc49f18007",
28-
"version": "0.8.0"
29-
}
30-
},
31-
{
32-
"package": "SwiftDocCPlugin",
33-
"repositoryURL": "https://github.com/apple/swift-docc-plugin",
34-
"state": {
35-
"branch": null,
36-
"revision": "3303b164430d9a7055ba484c8ead67a52f7b74f6",
37-
"version": "1.0.0"
38-
}
39-
},
40-
{
41-
"package": "xctest-dynamic-overlay",
42-
"repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay",
43-
"state": {
44-
"branch": null,
45-
"revision": "50a70a9d3583fe228ce672e8923010c8df2deddd",
46-
"version": "0.2.1"
47-
}
2+
"pins" : [
3+
{
4+
"identity" : "swift-argument-parser",
5+
"kind" : "remoteSourceControl",
6+
"location" : "https://github.com/apple/swift-argument-parser",
7+
"state" : {
8+
"revision" : "6b2aa2748a7881eebb9f84fb10c01293e15b52ca",
9+
"version" : "0.5.0"
4810
}
49-
]
50-
},
51-
"version": 1
11+
},
12+
{
13+
"identity" : "swift-benchmark",
14+
"kind" : "remoteSourceControl",
15+
"location" : "https://github.com/google/swift-benchmark",
16+
"state" : {
17+
"revision" : "a0564bf88df5f94eec81348a2f089494c6b28d80",
18+
"version" : "0.1.1"
19+
}
20+
},
21+
{
22+
"identity" : "swift-case-paths",
23+
"kind" : "remoteSourceControl",
24+
"location" : "https://github.com/pointfreeco/swift-case-paths",
25+
"state" : {
26+
"revision" : "241301b67d8551c26d8f09bd2c0e52cc49f18007",
27+
"version" : "0.8.0"
28+
}
29+
},
30+
{
31+
"identity" : "swift-docc-plugin",
32+
"kind" : "remoteSourceControl",
33+
"location" : "https://github.com/apple/swift-docc-plugin",
34+
"state" : {
35+
"revision" : "3303b164430d9a7055ba484c8ead67a52f7b74f6",
36+
"version" : "1.0.0"
37+
}
38+
}
39+
],
40+
"version" : 2
5241
}

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ Bool.Scanner.scanBool 916.000 ns ± 30.55 % 1000
317317
Color.Parser 208.000 ns ± 28.34 % 1000000
318318
CSV.Parser 3675250.000 ns ± 1.16 % 380
319319
CSV.Ad hoc mutating methods 651333.000 ns ± 1.00 % 2143
320-
Date.Parser 5833.000 ns ± 5.65 % 238924
320+
Date.Parser 3500.000 ns ± 5.65 % 238924
321321
Date.DateFormatter 23542.000 ns ± 5.50 % 58766
322322
Date.ISO8601DateFormatter 29041.000 ns ± 3.31 % 48028
323323
HTTP.HTTP 10250.000 ns ± 6.24 % 135657

Sources/swift-parsing-benchmark/Date.swift

+71-51
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,76 @@ import Benchmark
22
import Foundation
33
import Parsing
44

5+
#if swift(>=5.8)
6+
struct DateTime: Parser {
7+
var body: some Parser<Substring.UTF8View, Date> {
8+
Parse(Date.init(year:month:day:hour:minute:second:nanosecond:timeZone:)) {
9+
Digits(4)
10+
"-".utf8
11+
Digits(2).filter { (1...12).contains($0) }
12+
"-".utf8
13+
Digits(2).filter { (1...31).contains($0) }
14+
"T".utf8
15+
Digits(2).filter { $0 < 24 }
16+
":".utf8
17+
Digits(2).filter { $0 < 60 }
18+
":".utf8
19+
Digits(2).filter { $0 <= 60 }
20+
Parse {
21+
".".utf8
22+
Prefix(1...9, while: (UInt8(ascii: "0")...UInt8(ascii: "9")).contains)
23+
.compactMap { n in Int(Substring(n)).map { $0 * Int(pow(10, 9 - Double(n.count))) } }
24+
}
25+
.replaceError(with: 0)
26+
OneOf {
27+
"Z".utf8.map { 0 }
28+
Parse {
29+
OneOf {
30+
"+".utf8.map { 1 }
31+
"-".utf8.map { -1 }
32+
}
33+
Digits(2).filter { $0 < 24 }.map { $0 * 60 * 60 }
34+
":".utf8
35+
Digits(2).filter { $0 < 60 }.map { $0 * 60 }
36+
}
37+
.map { $0 * ($1 + $2) }
38+
}
39+
}
40+
}
41+
}
42+
43+
private extension Date {
44+
init(
45+
year: Int,
46+
month: Int,
47+
day: Int,
48+
hour: Int,
49+
minute: Int,
50+
second: Int,
51+
nanosecond: Int,
52+
timeZone: Int
53+
) {
54+
var components = tm(
55+
tm_sec: Int32(second),
56+
tm_min: Int32(minute),
57+
tm_hour: Int32(hour),
58+
tm_mday: Int32(day),
59+
tm_mon: Int32(month - 1),
60+
tm_year: Int32(year - 1900),
61+
tm_wday: 0,
62+
tm_yday: 0,
63+
tm_isdst: 0,
64+
tm_gmtoff: 0,
65+
tm_zone: nil
66+
)
67+
let time = timegm(&components)
68+
var timeIntervalSince1970 = TimeInterval(time - timeZone)
69+
timeIntervalSince1970 += TimeInterval(nanosecond) / 1_000_000_000
70+
self.init(timeIntervalSince1970: timeIntervalSince1970)
71+
}
72+
}
73+
#endif
74+
575
/// This benchmarks implements an [RFC-3339-compliant](https://www.ietf.org/rfc/rfc3339.txt) date
676
/// parser in a relatively naive way and pits it against `DateFormatter` and `ISO8601DateFormatter`.
777
///
@@ -10,62 +80,12 @@ import Parsing
1080
/// nanosecond, while the formatters do not parse beyond the millisecond.
1181
let dateSuite = BenchmarkSuite(name: "Date") { suite in
1282
#if swift(>=5.8)
13-
struct DateTime: Parser {
14-
var body: some Parser<Substring.UTF8View, DateComponents> {
15-
Parse { year, month, day, hour, minute, second, nanosecond, timeZone in
16-
DateComponents(
17-
timeZone: timeZone,
18-
year: year,
19-
month: month,
20-
day: day,
21-
hour: hour,
22-
minute: minute,
23-
second: second,
24-
nanosecond: nanosecond
25-
)
26-
} with: {
27-
Digits(4)
28-
"-".utf8
29-
Digits(2)
30-
"-".utf8
31-
Digits(2)
32-
"T".utf8
33-
Digits(2)
34-
":".utf8
35-
Digits(2)
36-
":".utf8
37-
Digits(2)
38-
Optionally {
39-
".".utf8
40-
Prefix(1...9, while: (UInt8(ascii: "0")...UInt8(ascii: "9")).contains)
41-
.compactMap { n in Int(Substring(n)).map { $0 * Int(pow(10, 9 - Double(n.count))) } }
42-
}
43-
OneOf {
44-
"Z".utf8.map { 0 }
45-
Parse {
46-
OneOf {
47-
"+".utf8.map { 1 }
48-
"-".utf8.map { -1 }
49-
}
50-
Digits(2).map { $0 * 60 * 60 }
51-
":".utf8
52-
Digits(2).map { $0 * 60 }
53-
}
54-
.map { $0 * ($1 + $2) }
55-
}
56-
.map { TimeZone(secondsFromGMT: $0) }
57-
}
58-
}
59-
}
60-
6183
let input = "1979-05-27T00:32:00Z"
6284
let expected = Date(timeIntervalSince1970: 296_613_120)
6385
var output: Date!
6486

65-
let dateTimeParser = DateTime().compactMap(Calendar.current.date(from:))
6687
suite.benchmark("Parser") {
67-
var input = input[...].utf8
68-
output = try dateTimeParser.parse(&input)
88+
output = try DateTime().parse(input)
6989
} tearDown: {
7090
precondition(output == expected)
7191
}

0 commit comments

Comments
 (0)