Repository navigation
Releases: kmoneil/dateparsa.zig
Release list
v0.5.0
Layouts from a format string. Every input parses to what it did in 0.4.2.
Added
Layout.fromFormatString(dialect, format, opts), the inverse of
formatString: a layout from a strftime, Go or Java format string, for a
column whose format is known, so that no value is detected. The order is
the format's, so%d/%m/%Ygives a reusable layout where one detected
from13/02/2024is decided per value. Each dialect reads as its own
parser does, within what a layout holds: a one-or-two-digit field takes
either,%fone to nine digits,%zZor either offset form, and a
month or weekday name any month or weekday in English and the options'
locales.error.Unsupportedfor a field no layout reads,
error.InvalidFormatfor a string that is no date format, and
error.NoBaseTimefor one with no year and no base time. Through C as
dateparsa_layout_from_formatand through Python as
Layout.from_format. Two fuzz targets hold it: a layout's format string
read back reads the value it was spelled from to the same time, and
agrees with the layout wherever both read; and a layout from any format
string spells formats that read back to the same time. (#27)
Changed
- A reused layout whose fields all have fixed widths (ISO 8601, RFC 3339,
SQL, compact dates) reads a value in about 0.9 of the time it did: an ISO
date-time in 9.7 ns where it took 10.3, an ISO date in 6.8 where it took
7.8, on an M5 Max. The fixed-slot path is inlined intoexecuteby hand. formatStringspells from any value the layout reads, not only the one
it was detected from at the same widths: a layout from1/2/2024spells
11/12/2024as%m/%d/%Ywhere it gaveerror.NotDetectedFrom. Python's
Layout.format_stringtakes the value asvalue=.formatStringwrites text between two fields as one run, so Java quotes
it once; refuses a NUL, which no C format string holds; spells a literal
Tafter a one-or-two-digit day rather than refusing it; spells a
weekday only in English and the locales the layout reads, and only as a
whole word, as a format string reads one; and refuses any format
fromFormatStringwould not read back, such as one with more runs of
text than a layout has fields.
v0.4.2
Faster email and HTTP dates. Every input parses to what it did in 0.4.1.
Changed
- An email or HTTP date ("Fri, 15 Mar 2024 10:30:00 +0000", "Sun, 06 Nov
1994 08:49:37 GMT") parses in about a quarter of the time it did, whatever
locales are on: 79 ns where it took 324, on an M5 Max. The textual
detector they went through finds a month name anywhere in any input; the
shapes email and HTTP write now have their fields and a layout for each
month built at comptime, from the detector's own answer, and an input of
one of them is checked against its shape instead. A fuzz target holds the
two to the same answer.
v0.4.1
Faster detection. Every input parses to what it did in 0.4.0.
Changed
- A first parse of a format the signature trie knows takes about 0.6 of
the time it did, and so does everything built on parse (Parseron a
value its cached layout cannot read, the C interface, Python, the
command): detection hands the format back by its table index instead of
copying its 168-byte definition out of the trie and twice more on the
way up, and looks for a CJK date only when 年 is the fifth byte. Against
the Go library's benchmarks, ISO 8601, RFC 3339, SQL and compact forms
now take 0.54 to 0.66 of Go's time, where they took 0.88 to 1.05.
v0.4.0
A column in one call, from C and from Python. Every input parses to what it
did in 0.3.3.
Added
dateparsa_parse_column: every value in a buffer, each ending at a
separator byte, parsed down one parser in one call, into an array of the
newdateparsa_value(the time, Unix seconds, kind, whether it was a
guess, and a status saying why a value did not parse). It allocates
nothing; every array is the caller's. For a language where a call into C
costs more than a parse does.dateparsa.parse_column(values, **options)in Python, on top of it: one
call into the library for the whole column, withNoneread as a value
that does not parse. Down 10,000 RFC 3339 values on Python 3.13
(docs/PERF.md) it takes 169 ns a value to datetimes, where aParser
called once a value takes 1.12 µs, and 32.9 ns into numpy, where pandas
working out the format takes 78.2. What it returns givesdatetimes(),
times(),failedandambiguous, anddatetime64()for a numpy array
pandas takes,NaTwhere a value did not parse; numpy is not a
dependency.- A fuzz target: the column call, on any bytes split at any separator,
answers for each value what aParseranswers parsing them in turn.
v0.3.3
A faster Python package. No library code changed, and every input parses
to what it did in 0.3.2.
Changed
- The Python package spends far less of each call in Python:
parse
takes a quarter to a third of the time it did (3.5 µs to 1.1 on Python
3.13, in docs/PERF.md),Parser.parseabout half, andLayout.parse
anddetecta half to two thirds, with the same answers. From Python,
parseis now faster thanstrptimegiven the format. The
default options go to the library as NULL and other options are built
once and shared; a result's fields are read in onestructunpack, its
TimeandResultbuilt without the dataclass__init__, and its
layout is a view of the result rather than a copy. A failed call is
run again with diagnostics, for its message, so a parse that succeeds
never pays for them.
v0.3.2
Benchmarks against other parsers. No library code changed, and every input
parses to what it did in 0.3.1.
Added
tools/bench_others.sh: dateparsa against other date parsers on one
machine. In Zig, against zeit. In Rust, through the C interface, against
chrono, jiff, time, speedate and dateparser, with heap allocations per
parse counted. In Python, through the package, againstfromisoformat,
strptime, ciso8601, dateutil, dateparser and pandas, one value at a time
and down a column. Every parser's answer is checked before it is timed,
and each harness pins the versions it compares against.docs/PERF.md
has the first run and what it shows, including what the shared library
imports: on macOS,bzero,memcpyandmemset, and nothing that could
ask the system for memory.
v0.3.1
The dateparsa command, prebuilt. No library code changed, and every input
parses to what it did in 0.3.0.
Added
- The
dateparsacommand.dateparsa detectreports how a column is
written: the formats its values take, each spelled for strftime, Go and
Java, the order its numeric dates are in and the value that settled it, the
values that needed the column's order, and the values it could not read, by
line.dateparsa normalizewrites the input back with the column as RFC
3339, changing no other byte, and refuses to guess an order nothing in the
column settles. CSV with--col Nor--col NAME(quoted fields, any
one-byte separator), or one value a line, from a file or standard input;
--order,--base,--tz,--utc,--localeanddetect --json. The
release archives carry it inbin/for every platform, and each release
runs it on Linux, macOS and Windows before publishing.
v0.3.0
A column's date order, format strings for other tools, and a Python
package. All of it is new API: nothing that existed changed, and every input
parses to what it did in 0.2.1.
Added
detectColumnandColumn: which order a column writes its numeric dates
in, month, day or year first, from all of its values where one value
cannot say. One13/02/2024settles day first for the whole column, and
two-digit years narrow it together. The report gives the order, the value
that settled it, any values that contradict it, and theOptionsthat read
the column in it.Columntakes values one at a time and allocates
nothing. In C:dateparsa_column_init,dateparsa_column_addand
dateparsa_column_summarize.Layout.dateOrder(): the order a numeric layout reads year, month and day
in.Layout.formatString(dialect, detected_from, writer): a detected format as
another tool's format string, in strftime (C, Python, pandas, Ruby), Go or
Java (also Kotlin and Spark) form, such as%d/%m/%Y %H:%M. A field a
dialect cannot spell iserror.Unsupported, never a string that reads
something else. Every example in the format table is checked in Python, Go
and Java bytools/format_check.sh. In C:dateparsa_format_stringand
the new statusDATEPARSA_UNSUPPORTED.- A Python package,
pip install dateparsa: wheels for Linux (glibc and musl),
macOS 13 and later, and Windows, on x86_64 and arm64, for Python 3.9 and
later, wrapping the release libraries withctypes.parse,detect,
Parser,Layout(withformat_string),detect_columnandColumn,
with options as keyword arguments, results asdatetimes, andNoMatch,
AmbiguousandNoBaseTimeasValueErrors. Every release installs each
wheel on its platform and runs the tests before publishing to PyPI as a
trusted publisher.
v0.2.1
The C library, prebuilt. No library code changed, and every input parses to
what it did in 0.2.0.
Added
- Prebuilt
libdateparsaon every release, for Linux, macOS 13 and later, and
Windows, each on x86_64 and arm64: one archive per platform with the static
and the shared library, the header and the licences, plusSHA256SUMSand
build provenance. Before a release publishes them, each runner OS links the
C tests against its own platform's archive with the compiler it already
has, and runs them.
v0.2.0
The C interface. Nothing a Zig caller sees changed, and every input parses to
what it did in 0.1.0.
Added
- A C interface, for every language that can call C.
zig build libbuilds
libdateparsa, static or with-Dlinkage=dynamicshared, and installs
include/dateparsa.h. It coversparse,detect, layout reuse,Parser,
strict mode and its diagnostics, locales by bit or by BCP 47 tag, RFC 3339
output and Unix seconds, and allocates nothing: every struct is the
caller's. What a C caller passes in is checked, not trusted: a base time
that is no date, an offset of a day or more, a base year more than
999,999,999 from year 0, a locale bit the build left out, aNULL, or a
layout or parser the library never filled in is
DATEPARSA_INVALID_ARGUMENT.