Skip to content

Commit

Permalink
Improve Date parser demo (#305)
Browse files Browse the repository at this point in the history
* Improve `Date` parser demo

Incorporates a helper from @juri's
[parser](https://github.com/juri/Parse3339) to improve things a bit.

* wip

* wip
  • Loading branch information
stephencelis authored Jul 24, 2023
1 parent d858288 commit 27a3a86
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 101 deletions.
87 changes: 38 additions & 49 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,52 +1,41 @@
{
"object": {
"pins": [
{
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser",
"state": {
"branch": null,
"revision": "6b2aa2748a7881eebb9f84fb10c01293e15b52ca",
"version": "0.5.0"
}
},
{
"package": "Benchmark",
"repositoryURL": "https://github.com/google/swift-benchmark",
"state": {
"branch": null,
"revision": "a0564bf88df5f94eec81348a2f089494c6b28d80",
"version": "0.1.1"
}
},
{
"package": "swift-case-paths",
"repositoryURL": "https://github.com/pointfreeco/swift-case-paths",
"state": {
"branch": null,
"revision": "241301b67d8551c26d8f09bd2c0e52cc49f18007",
"version": "0.8.0"
}
},
{
"package": "SwiftDocCPlugin",
"repositoryURL": "https://github.com/apple/swift-docc-plugin",
"state": {
"branch": null,
"revision": "3303b164430d9a7055ba484c8ead67a52f7b74f6",
"version": "1.0.0"
}
},
{
"package": "xctest-dynamic-overlay",
"repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state": {
"branch": null,
"revision": "50a70a9d3583fe228ce672e8923010c8df2deddd",
"version": "0.2.1"
}
"pins" : [
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "6b2aa2748a7881eebb9f84fb10c01293e15b52ca",
"version" : "0.5.0"
}
]
},
"version": 1
},
{
"identity" : "swift-benchmark",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/swift-benchmark",
"state" : {
"revision" : "a0564bf88df5f94eec81348a2f089494c6b28d80",
"version" : "0.1.1"
}
},
{
"identity" : "swift-case-paths",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-case-paths",
"state" : {
"revision" : "241301b67d8551c26d8f09bd2c0e52cc49f18007",
"version" : "0.8.0"
}
},
{
"identity" : "swift-docc-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-docc-plugin",
"state" : {
"revision" : "3303b164430d9a7055ba484c8ead67a52f7b74f6",
"version" : "1.0.0"
}
}
],
"version" : 2
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ Bool.Scanner.scanBool 916.000 ns ± 30.55 % 1000
Color.Parser 208.000 ns ± 28.34 % 1000000
CSV.Parser 3675250.000 ns ± 1.16 % 380
CSV.Ad hoc mutating methods 651333.000 ns ± 1.00 % 2143
Date.Parser 5833.000 ns ± 5.65 % 238924
Date.Parser 3500.000 ns ± 5.65 % 238924
Date.DateFormatter 23542.000 ns ± 5.50 % 58766
Date.ISO8601DateFormatter 29041.000 ns ± 3.31 % 48028
HTTP.HTTP 10250.000 ns ± 6.24 % 135657
Expand Down
122 changes: 71 additions & 51 deletions Sources/swift-parsing-benchmark/Date.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,76 @@ import Benchmark
import Foundation
import Parsing

#if swift(>=5.8)
struct DateTime: Parser {
var body: some Parser<Substring.UTF8View, Date> {
Parse(Date.init(year:month:day:hour:minute:second:nanosecond:timeZone:)) {
Digits(4)
"-".utf8
Digits(2).filter { (1...12).contains($0) }
"-".utf8
Digits(2).filter { (1...31).contains($0) }
"T".utf8
Digits(2).filter { $0 < 24 }
":".utf8
Digits(2).filter { $0 < 60 }
":".utf8
Digits(2).filter { $0 <= 60 }
Parse {
".".utf8
Prefix(1...9, while: (UInt8(ascii: "0")...UInt8(ascii: "9")).contains)
.compactMap { n in Int(Substring(n)).map { $0 * Int(pow(10, 9 - Double(n.count))) } }
}
.replaceError(with: 0)
OneOf {
"Z".utf8.map { 0 }
Parse {
OneOf {
"+".utf8.map { 1 }
"-".utf8.map { -1 }
}
Digits(2).filter { $0 < 24 }.map { $0 * 60 * 60 }
":".utf8
Digits(2).filter { $0 < 60 }.map { $0 * 60 }
}
.map { $0 * ($1 + $2) }
}
}
}
}

private extension Date {
init(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int,
nanosecond: Int,
timeZone: Int
) {
var components = tm(
tm_sec: Int32(second),
tm_min: Int32(minute),
tm_hour: Int32(hour),
tm_mday: Int32(day),
tm_mon: Int32(month - 1),
tm_year: Int32(year - 1900),
tm_wday: 0,
tm_yday: 0,
tm_isdst: 0,
tm_gmtoff: 0,
tm_zone: nil
)
let time = timegm(&components)
var timeIntervalSince1970 = TimeInterval(time - timeZone)
timeIntervalSince1970 += TimeInterval(nanosecond) / 1_000_000_000
self.init(timeIntervalSince1970: timeIntervalSince1970)
}
}
#endif

/// This benchmarks implements an [RFC-3339-compliant](https://www.ietf.org/rfc/rfc3339.txt) date
/// parser in a relatively naive way and pits it against `DateFormatter` and `ISO8601DateFormatter`.
///
Expand All @@ -10,62 +80,12 @@ import Parsing
/// nanosecond, while the formatters do not parse beyond the millisecond.
let dateSuite = BenchmarkSuite(name: "Date") { suite in
#if swift(>=5.8)
struct DateTime: Parser {
var body: some Parser<Substring.UTF8View, DateComponents> {
Parse { year, month, day, hour, minute, second, nanosecond, timeZone in
DateComponents(
timeZone: timeZone,
year: year,
month: month,
day: day,
hour: hour,
minute: minute,
second: second,
nanosecond: nanosecond
)
} with: {
Digits(4)
"-".utf8
Digits(2)
"-".utf8
Digits(2)
"T".utf8
Digits(2)
":".utf8
Digits(2)
":".utf8
Digits(2)
Optionally {
".".utf8
Prefix(1...9, while: (UInt8(ascii: "0")...UInt8(ascii: "9")).contains)
.compactMap { n in Int(Substring(n)).map { $0 * Int(pow(10, 9 - Double(n.count))) } }
}
OneOf {
"Z".utf8.map { 0 }
Parse {
OneOf {
"+".utf8.map { 1 }
"-".utf8.map { -1 }
}
Digits(2).map { $0 * 60 * 60 }
":".utf8
Digits(2).map { $0 * 60 }
}
.map { $0 * ($1 + $2) }
}
.map { TimeZone(secondsFromGMT: $0) }
}
}
}

let input = "1979-05-27T00:32:00Z"
let expected = Date(timeIntervalSince1970: 296_613_120)
var output: Date!

let dateTimeParser = DateTime().compactMap(Calendar.current.date(from:))
suite.benchmark("Parser") {
var input = input[...].utf8
output = try dateTimeParser.parse(&input)
output = try DateTime().parse(input)
} tearDown: {
precondition(output == expected)
}
Expand Down

0 comments on commit 27a3a86

Please sign in to comment.