juri/parse3339
Parse3339 is a fast [RFC 3339] time stamp parser written in pure Swift.
Usage
import Parse3339
let s = "2023-07-09T13:14:00+03:00"
guard let parts = Parse3339::parse(s) else {
return
}
let date = parts.date
print(date.timeIntervalSinceReferenceDate)
// output: 710590440.0There are variants of parse for String, Substring and Span<UInt8>. By default parse parses the start of the string and allows trailing data. If you don't want to allow trailing data, you can specify requireFullConsumption:
Parse3339::parse(s, requireFullConsumption: true)There's a helper function you can use with Foundation's JSONDecoder:
import Parse3339
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(Parse3339::parseFromDecoder(_:))Parse3339 ships with Foundation integration (Date, DateComponents) enabled by default, but they can be disabled by disabling the WithFoundation trait. The parsing itself uses only Swift standard library types.
For Package.swift snippets and documentation, visit the Swift Package Index page.
Speed and memory usage
Parse3339 is pleasantly fast and stingy with memory usage. The package has benchmarks:
swift package benchmarkTo measure parsing without date conversion, run the parser-only benchmarks:
swift package benchmark --filter 'Parse with Parse3339 \((String|Span) only\)' --metric wallClock --metric throughput --scaleThese benchmarks use 8,192 timestamps with varying dates, fractional precision, and time zones. They measure the String and byte-span entry points separately.
There are also benchmarks that parse the same string using Foundation's DateFormatter, Foundation's ISO8601DateFormatter, Parse3339 creating a Date with Foundation's DateComponents and Calendar, and Parse3339 creating a Date with standard Unix functions.
Output from one run (macOS 27.0.1, MacBook Pro M5):
Parse with DateFormatter
| Metric | p0 | p25 | p50 | p75 | p90 | p99 | p100 | Samples | |:-------------------------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| | Free (total) | 1061 | 1061 | 1061 | 1061 | 1061 | 1061 | 1063 | 100000 | | Instructions (K) | 2161 | 2163 | 2163 | 2163 | 2163 | 2179 | 2421 | 100000 | | Malloc (bytes total) | 232713 | 232831 | 232831 | 232831 | 232831 | 232831 | 237065 | 100000 | | Malloc (total) | 1063 | 1063 | 1063 | 1063 | 1063 | 1063 | 1067 | 100000 | | Malloc / free Δ | 2 | 2 | 2 | 2 | 2 | 2 | 6 | 100000 | | Malloc / free Δ (bytes) | 99 | 99 | 99 | 99 | 99 | 99 | 4195 | 100000 | | Memory (resident peak) (M) | 17 | 21 | 25 | 29 | 31 | 32 | 32 | 100000 | | Throughput (# / s) (K) | 12 | 11 | 11 | 11 | 11 | 9 | 6 | 100000 | | Time (total CPU) (ns) | 87918 | 94271 | 94847 | 95423 | 96639 | 107711 | 176791 | 100000 | | Time (wall clock) (ns) | 86750 | 92927 | 93503 | 94079 | 95167 | 105663 | 178750 | 100000 |
Parse with ISO8601DateFormatter
| Metric | p0 | p25 | p50 | p75 | p90 | p99 | p100 | Samples | |:-------------------------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| | Free (total) | 1239 | 1239 | 1239 | 1239 | 1239 | 1239 | 1239 | 100000 | | Instructions (K) | 2642 | 2642 | 2642 | 2642 | 2642 | 2660 | 2857 | 100000 | | Malloc (bytes total) | 330588 | 330751 | 330751 | 330751 | 330751 | 330751 | 331004 | 100000 | | Malloc (total) | 1239 | 1239 | 1239 | 1239 | 1239 | 1239 | 1245 | 100000 | | Malloc / free Δ | 0 | 0 | 0 | 0 | 0 | 0 | 6 | 100000 | | Malloc / free Δ (bytes) | 0 | 0 | 0 | 0 | 0 | 0 | 416 | 100000 | | Memory (resident peak) (M) | 15 | 16 | 16 | 16 | 16 | 16 | 16 | 100000 | | Throughput (# / s) (#) | 9012 | 8415 | 8359 | 8303 | 8175 | 7439 | 5318 | 100000 | | Time (total CPU) (ns) | 112375 | 120319 | 121215 | 122047 | 123967 | 135807 | 177208 | 100000 | | Time (wall clock) (ns) | 110958 | 118847 | 119679 | 120575 | 122367 | 134527 | 188042 | 100000 |
Parse with Parse3339 (DateComponents)
| Metric | p0 | p25 | p50 | p75 | p90 | p99 | p100 | Samples | |:-------------------------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| | Free (total) | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 100000 | | Instructions (K) | 6 | 6 | 6 | 6 | 6 | 7 | 60 | 100000 | | Malloc (bytes total) | 153 | 153 | 153 | 153 | 153 | 153 | 153 | 100000 | | Malloc (total) | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 100000 | | Malloc / free Δ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc / free Δ (bytes) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Memory (resident peak) (M) | 13 | 13 | 13 | 13 | 13 | 13 | 13 | 100000 | | Throughput (# / s) (K) | 1845 | 1500 | 1413 | 1413 | 1411 | 1144 | 54 | 100000 | | Time (total CPU) (ns) | 1749 | 1959 | 2000 | 2042 | 2083 | 2543 | 23667 | 100000 | | Time (wall clock) (ns) | 542 | 667 | 708 | 708 | 709 | 875 | 18584 | 100000 |
Parse with Parse3339 (Span only)
| Metric | p0 | p25 | p50 | p75 | p90 | p99 | p100 | Samples | |:-------------------------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| | Free (total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Instructions | 464 | 464 | 464 | 464 | 464 | 464 | 503 | 100000 | | Malloc (bytes total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc (total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc / free Δ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc / free Δ (bytes) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Memory (resident peak) (M) | 12 | 13 | 13 | 13 | 13 | 13 | 13 | 100000 | | Throughput (# / s) (M) | 89 | 82 | 82 | 82 | 81 | 67 | 25 | 100000 | | Time (total CPU) (ns) | 13 | 14 | 14 | 14 | 14 | 17 | 38 | 100000 | | Time (wall clock) (ns) | 11 | 12 | 12 | 12 | 12 | 15 | 40 | 100000 |
Parse with Parse3339 (String only)
| Metric | p0 | p25 | p50 | p75 | p90 | p99 | p100 | Samples | |:-------------------------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| | Free (total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Instructions | 525 | 525 | 525 | 525 | 525 | 525 | 573 | 100000 | | Malloc (bytes total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc (total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc / free Δ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc / free Δ (bytes) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Memory (resident peak) (M) | 12 | 13 | 13 | 13 | 13 | 13 | 13 | 100000 | | Throughput (# / s) (M) | 68 | 62 | 62 | 61 | 60 | 51 | 16 | 100000 | | Time (total CPU) (ns) | 16 | 17 | 18 | 18 | 18 | 22 | 57 | 100000 | | Time (wall clock) (ns) | 15 | 16 | 16 | 16 | 17 | 20 | 62 | 100000 |
Parse with Parse3339 (Unix time)
| Metric | p0 | p25 | p50 | p75 | p90 | p99 | p100 | Samples | |:-------------------------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| | Free (total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Instructions (K) | 54 | 54 | 54 | 54 | 54 | 55 | 70 | 100000 | | Malloc (bytes total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc (total) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc / free Δ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Malloc / free Δ (bytes) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100000 | | Memory (resident peak) (M) | 12 | 13 | 13 | 13 | 13 | 13 | 13 | 100000 | | Throughput (# / s) (K) | 490 | 445 | 437 | 429 | 429 | 343 | 50 | 100000 | | Time (total CPU) (ns) | 3209 | 3583 | 3625 | 3667 | 3709 | 4583 | 19332 | 100000 | | Time (wall clock) (ns) | 2041 | 2251 | 2291 | 2333 | 2335 | 2917 | 20000 | 100000 |
Package Metadata
Repository: juri/parse3339
Default branch: main
README: README.md