# datefinder: extracting datetime objects from unstructured text in Python

> datefinder finds date-like strings inside messy documents and returns Python datetime objects or typed matches. Version 1.0.0 makes the v2 engine the default and moves the original parser behind find_dates_legacy.

**akoumjian/datefinder** — Find dates inside text using Python and get back datetime objects

- Repository: https://github.com/akoumjian/datefinder
- Website: http://datefinder.readthedocs.org/en/latest/
- Stars: 663 · Forks: 170
- Language: Python
- License: MIT
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/akoumjian-datefinder

## The problem datefinder solves, and who actually has it

Most date parsing in Python assumes you already know the format. datetime.strptime takes a pattern, and dateutil.parser.parse takes a single string that is entirely a date. Neither helps when the date is buried in a sentence. An email that says "entries are due by January 4th, 2017 at 8:00pm" is three tokens of context wrapped around one date, and the README uses exactly that string as its worked example.

datefinder is built for that case. The README lists the intended inputs directly: emails, tickets and support conversations; contracts, policies and legal text; logs, reports and markdown pages; scraped HTML and mixed-format documents. The common thread is that the data was never normalized, so the parser has to locate the date before it can parse it.

The audience is narrower than "anyone handling dates." If your dates come out of a database column, a CSV with a fixed schema, or an API that emits ISO 8601, you do not have this problem and datefinder adds a dependency plus a false-positive risk for nothing. It earns its place when the text is human-written and the format varies from document to document.

## How datefinder works: a Rust kernel behind a Python API

The repository layout shows a split implementation. Python source lives in datefinder/, and a Rust crate lives in rust/datefinder-kernel. The pyproject.toml uses maturin as the build backend, points manifest-path at rust/datefinder-kernel/Cargo.toml, and declares module-name = "datefinder._kernel". The README states plainly that the Rust kernel "is required for v2/default runtime behavior."

That is the central architectural fact of 1.0.0. The default path is not pure Python. The Python dependencies listed in pyproject.toml are regex, python-dateutil and pytz, but those sit alongside a compiled extension that does the v2 work.

On top of that kernel sit several entry points, and the README treats them as distinct engines rather than aliases. find_dates(...) defaults to the v2 compatibility engine. find_dates_legacy(...) preserves the original parser. find_dates_compat(...) is described as a helper for migrating existing code. extract(...) returns typed match objects with a kind, the matched text, a span, a confidence value and a structured value.

The typed output is where the data model diverges from a plain datetime. The README says the extract engine emits relative values that carry both resolved_datetime and delta_seconds, and duration values that carry total_seconds plus normalized components. So "in 3 days" is not flattened into a single timestamp at extraction time; the offset survives. For anything downstream that needs to know how precise a match was, that distinction matters more than the datetime itself.

## Installing datefinder and running a first extraction

The README requires Python 3.9 or newer and gives pip as the install path. It also warns that if a compatible prebuilt wheel is unavailable for your platform, pip will build from source and a Rust toolchain is required. That warning is worth reading before you add this to a CI image.

```bash
pip install datefinder
```

The README also carries a note that the maintainer does not publish the version on conda forge and cannot verify its integrity, so treat conda channels as an unverified source for this package.

Once installed, the shortest useful call is find_dates. The README's own example passes a block of text containing two dates and prints the results.

```python
import datefinder

string_with_dates = """
entries are due by January 4th, 2017 at 8:00pm
created 01/15/2005 by ACME Inc. and associates.
"""

for match in datefinder.find_dates(string_with_dates):
    print(match)
```

The README shows that this prints 2017-01-04 20:00:00 and 2005-01-15 00:00:00. Note what happened to the time in the second line: no time was present, so midnight was filled in.

Relative expressions need an anchor. The README's example passes base_date to find_dates and reference_dt to extract, and the CLI exposes the same idea as --reference with an ISO 8601 string.

```python
import datefinder
from datetime import datetime, timezone

text = "Meeting tomorrow; launch on 2024-11-03 18:00 UTC."
ref = datetime(2026, 3, 19, 12, 0, tzinfo=timezone.utc)

for match in datefinder.extract(text, reference_dt=ref):
    print(match.kind, match.text, match.value)
```

If you would rather not write Python, the package installs a datefinder console script, declared in pyproject.toml as datefinder.cli:main. The README shows it reading from stdin as well, so it drops into shell pipelines.

```bash
cat document.txt | datefinder --engine extract --json
```

## Where datefinder breaks down

The most concrete limitation is distribution, not parsing. The README lists the release wheel targets explicitly: manylinux2014 and musllinux_1_2 for x86_64 and aarch64, macOS for x86_64 and arm64, and Windows for x86_64. Anything outside that matrix falls back to a source build against the Rust kernel. On an Alpine image with an unusual architecture, or in an air-gapped build environment without a Rust toolchain, the install simply fails. The README does not document a pure-Python fallback for the default engine.

Ambiguity is the second problem, and it is inherent rather than fixable. The CLI exposes --first {month,day,year} precisely because 01/15/2005 and 03/04/2005 cannot both be resolved by pattern alone, and --no-month-only exists because the parser will otherwise turn a bare "May" into YYYY-05-01. Those flags are admissions that the library guesses, and the guesses are configurable rather than correct.

That makes datefinder the wrong tool for compliance-grade extraction. If a contract date must be exact, a regex anchored to your document template, or a human review step, will be more reliable than a parser that infers a year for a month name. The README also does not document rollback or a downgrade path, so pinning the version in your requirements file is the only stated protection against a change in default behavior.

Finally, the project's own classifiers mark it Development Status :: 4 - Beta, even at version 1.0.0. The last push was on 2026-03-25, the same day v1.0.0 was released, and the previous release before that was v0.7.3 in 2022. A four-year gap between releases is worth weighing if you depend on upstream fixes.

## datefinder versus dateutil.parser.parse

The obvious alternative is dateutil.parser.parse, which datefinder already depends on. The difference is scope, and it is not a small one. parse takes one string and assumes that string is a date. Feed it "entries are due by January 4th, 2017 at 8:00pm" and it has to cope with leading prose; feed it a whole email and it will either fail or pick something arbitrary.

datefinder does the locating step first. It scans the document, finds candidate substrings, and returns a sequence of matches. That is why the README positions it against "large documents with mixed formatting" rather than against single values.

The practical consequence is that datefinder is a superset in workflow terms but a subset in control terms. With parse you get one result and you know which string produced it. With find_dates you get a generator, and in the default engine you get datetimes without spans. The README notes that --source and --index, which include source span details, apply to the default and legacy engines only; the typed extract engine is where spans, kinds and confidence live. If you need to know which part of the text produced a match, extract is the engine to use, not find_dates.

A second alternative is simply writing regexes for the formats you actually see. That is more work up front and it wins when your corpus is narrow, because a regex cannot invent a year for a month name.

## Migrating from the old datefinder to 1.0.0

The 1.0.0 release changes what find_dates does by default. The README states that as of 1.0.0, find_dates(...) defaults to the v2 compatibility engine, and that the original engine remains available as find_dates_legacy(...). Code written against 0.7.x therefore keeps working only in the sense that it still runs; the results may differ.

The README recommends a specific order for this: use find_dates_legacy(...) for parity, then move to find_dates(...). There is also find_dates_compat(...), described as a compatibility helper for migrating existing code, which is the gentler intermediate step.

```python
import datefinder

for dt in datefinder.find_dates_compat("tomorrow and 2024-12-10"):
    print(dt)
```

The repository also contains a conformance/ directory and a bench/ directory, and the README mentions building a reproducible corpus from legacy tests to generate differential reports. That is the mechanism the project provides for checking the migration rather than trusting it. The README text on this section is truncated, so the exact commands for generating those reports are not documented in what is available here.

The upgrade cost is therefore mostly verification time: run your own text through both engines, diff the outputs, and decide whether the v2 differences are improvements for your corpus. The dependency footprint also grows, since the Rust kernel is required for the default path.

## Licence and what it means for shipping datefinder

datefinder is MIT licensed. The pyproject.toml declares license = { text = "MIT" }, and the repository root carries a LICENSE file that maturin is configured to include in the build. MIT is permissive, so the usual obligations apply: keep the copyright notice and permission notice with any distribution. Nothing here suggests a copyleft obligation or a commercial-use restriction.

One detail to keep in view is that datefinder is not a single-language package. The default runtime behavior depends on a Rust kernel under rust/datefinder-kernel, and the workspace is declared in the root Cargo.toml. If your organization runs licence scanning per ecosystem, the Rust crate is a separate artifact to enumerate. The README does not list a separate licence for the Rust kernel, so confirm that the crate inherits the same terms before you rely on the MIT label covering the whole build. That is a gap in the documentation, not legal advice; your own review decides.

## Conclusion

Adopt datefinder if your input is unstructured prose and you need datetime objects rather than a strict format string, and start on 1.0.0 with find_dates_compat before moving to extract. Do not adopt it if your dates already arrive in a known format, since datetime.strptime or dateutil.parser.parse is deterministic and cheaper. Before committing, verify that a prebuilt wheel exists for your platform, because a source build needs a Rust toolchain, and run your own corpus through both find_dates_legacy and find_dates to see how far the v2 default moves your results.

## FAQ

### What is datefinder?

datefinder is a Python module for locating dates inside text and turning them into datetime or temporal objects. It handles explicit dates like January 4th, 2017 as well as relative expressions like tomorrow and in 3 days, and it ships a CLI entry point in addition to the Python API.

### How do I search for a date in a block of text with datefinder?

Call datefinder.find_dates with the text and iterate over the result, which yields datetime objects. For typed output that includes the match kind, the matched text, spans and confidence, use datefinder.extract with a reference_dt instead.

### How to find a date for today using datefinder?

Relative expressions are resolved against an anchor. Pass base_date to find_dates, reference_dt to extract, or --reference with an ISO 8601 string on the CLI, and the parser resolves terms like tomorrow or in 3 days relative to that value.

### How do I use a date finder in Python?

Install the package with pip install datefinder, then import it and call find_dates on your text. The README's example prints 2017-01-04 20:00:00 and 2005-01-15 00:00:00 for a two-date input, and the CLI offers the same engines through datefinder --engine extract --json.

### Is DateFinder legit?

The Python package is published on PyPI under the name datefinder and the repository is MIT licensed with source on GitHub. The README notes that the maintainer does not publish the version on conda forge and cannot verify its integrity, so install from PyPI or build from source rather than trusting a conda channel.

## Sources

- [akoumjian/datefinder on GitHub](https://github.com/akoumjian/datefinder)
- [License: MIT](https://github.com/akoumjian/datefinder/blob/master/LICENSE)
- [Project website](http://datefinder.readthedocs.org/en/latest/)
- [README](https://github.com/akoumjian/datefinder/blob/master/README.md)
- [Releases](https://github.com/akoumjian/datefinder/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/akoumjian-datefinder
