Fuzzy, natural-language time parsing that goes past dateparser — resolves phrases like 'sometime next week' into datetimes or ranges, with a confidence score.
Project description
timefuzz
Fuzzy, natural-language time parsing that goes past
dateparser.
timefuzz resolves human phrases like "sometime next week", "the Tuesday
after my birthday", or "end of Q3" into concrete datetimes or ranges —
with a confidence score, so your app knows when to ask the user to confirm.
Rust core, thin Python API. Ships as compiled wheels: pip install timefuzz,
no Rust toolchain required.
import timefuzz as tf
from datetime import date, datetime
now = datetime(2026, 7, 12, 15, 30)
tf.parse("next friday", now=now)
# Instant(when=datetime(2026, 7, 17, 9, 0), confidence=0.95,
# interpretation='next Friday, default 09:00')
tf.parse("sometime next week", now=now)
# Range(start=datetime(2026, 7, 13, 0, 0), end=datetime(2026, 7, 19, 23, 59, 59),
# confidence=0.8, interpretation='the calendar week after this one')
tf.parse("the tuesday after my birthday", now=now,
anchors={"my birthday": date(2026, 8, 3)})
# Instant(when=datetime(2026, 8, 4, 9, 0), confidence=0.9,
# interpretation='first Tuesday strictly after 2026-08-03')
tf.parse("end of q3", now=now)
# Range(start=datetime(2026, 9, 1, 0, 0), end=datetime(2026, 9, 30, 23, 59, 59),
# confidence=0.85, interpretation='last month of Q3')
Why another date parser?
Existing parsers (dateparser, parsedatetime) handle "in 3 days" and
"next Friday" but fall over on:
- Ranges & vagueness — "sometime next week" is a span, not an instant.
- Anchored relatives — "the Tuesday after my birthday" needs a user-supplied anchor date.
- Business calendars — "end of Q3", "next business day", "the 2nd Monday of March", "10 business days after the invoice date".
- Honest ambiguity — "next weekend" said on a Wednesday has two defensible readings; timefuzz returns both instead of silently guessing.
- Confidence — callers want to know how fuzzy a result is, so they can confirm low-confidence parses with the user instead of silently guessing.
Every parse returns the resolved value plus a confidence score plus the interpretation the parser chose, so schedulers, reminder apps, and chat bots can decide when to double-check.
Install
pip install timefuzz
Wheels are published for CPython 3.10–3.13 on Linux (manylinux), macOS (x86-64 + arm64), and Windows (x86-64). An sdist falls back to building from source (requires a Rust toolchain) if no wheel matches.
What it returns
Every parse yields one of three shapes:
| Shape | Fields | Example input |
|---|---|---|
Instant |
when, confidence, interpretation |
"next friday" |
Range |
start, end (inclusive), confidence, interpretation |
"sometime next week" |
Ambiguous |
candidates: list[Instant | Range], reason |
"next weekend" said midweek |
If nothing matches at all, ParseError is raised with the reason.
An unknown anchor ("after my graduation" with no "my graduation" anchor)
returns Ambiguous with an empty candidate list and an explanatory reason.
Candidates are ordered most-likely-first.
match tf.parse(user_text):
case tf.Instant(when=when, confidence=c) if c >= 0.8:
schedule(when)
case tf.Instant(when=when):
confirm_with_user(when) # low confidence -> ask first
case tf.Range(start=s, end=e):
offer_slot_picker(s, e)
case tf.Ambiguous(candidates=cands, reason=why):
disambiguate(cands, why)
API
def parse(
text: str,
now: datetime | None = None, # reference moment (default: datetime.now())
anchors: dict[str, date] | None = None,
config: Config | None = None,
) -> Instant | Range | Ambiguous: ...
@dataclass(frozen=True)
class Config:
default_time: time = time(9, 0) # time attached to date-only results
week_start: Weekday = Weekday.MON # or Weekday.SUN
next_skips_today: bool = True # "next friday" said on a Friday
tz: tzinfo | None = None # naive by default; tz-aware opt-in
holidays: Callable[[date], bool] | None = None # business-day hook
What the grammar understands (v0.2)
- Relative offsets:
in 3 days,2 weeks ago,3 days from now,in a week,tomorrow,yesterday,today,now - Period spans:
next week,this month,last quarter,next year, bare month names (august) - Weekday navigation:
next friday,this tuesday,last monday, barefriday,friday next week,monday last week - Weekends:
this weekend,next weekend,last weekend,sometime next weekend,the weekend after the wedding - Ordinal-in-period:
2nd monday of march,2nd monday of march 2027,last friday of october,first monday of next month - Anchored:
the tuesday after my birthday,2nd tuesday after my birthday,the day before the wedding,2 weeks after my birthday,the week after my birthday,3 business days after the invoice date, bareon my birthday(anchors supplied by you) - Clock times:
next friday at 3pm,tomorrow at 7:30,friday at midnight, bare3pm/15:45/noon(next occurrence) - Vague spans:
sometime next week,sometime in august,early march,mid q3,mid month,late next month,sometime early next month - Business calendar:
next business day,in 3 business days,5 business days ago,end of q3,start of q4,end of month,end of next month,first business day of next month,last business day of the month,eow/eom/eoq/eoy
See docs/grammar_reference.md for the complete rule catalogue and docs/cookbook.md for recipes.
Conventions (the fine print)
These are deliberate, documented choices — see the config knobs above:
- "next Friday" when today is Friday is culture-dependent. Default:
skip today (
next_skips_today=True), i.e. you get the Friday seven days out. - Date-only results get
default_time(09:00 by default); an explicit trailing time (… at 3pm) overrides it. - Arithmetic offsets keep the clock:
in 3 days= now + 72h;tomorrow= tomorrow atdefault_time. - Ranges are inclusive,
00:00:00through23:59:59of the last day. - Weekends are Sat–Sun, whatever
week_startsays. - Naive by default. Set
Config(tz=...)to get tz-aware results; math is wall-clock, so "tomorrow 09:00" is 09:00 across a DST jump. - Month names and quarters assume the current cycle and roll forward if already past (the interpretation string says so).
- Genuinely contested phrases return
Ambiguousrather than a guess:next weekendmidweek,sundaysaid on a Sunday,julysaid during July. - Confidence is deterministic, not learned — the same phrase always gets
the same score, offsets past ~10 years are trusted slightly less, and
stacked hedges (
sometime early …) cap lower. See docs/confidence.md.
Development
# build + test
pip install maturin
maturin develop --release
pytest
# rust checks
cargo clippy --all-targets
cargo fmt --check
cargo bench # informational: full-corpus, single-phrase, tokenizer-only
The test suite is corpus-driven: tests/corpus*.jsonl map phrases (plus a
fixed reference now) to expected outputs. Adding a phrase = adding a line.
Roadmap
- v0.2 — ✅ richer anchored phrases + business-calendar rules
- v0.3 — ✅ confidence-model refinement, more
Ambiguouscandidates - v0.4 — ✅ corpus expansion, benches, cookbook growth
- v1.0 — ✅ i18n-ready grammar structure (
Localeseam, English-only), ✅ clock-time support, ✅ written stability policy; remaining: a soak period on 0.3.x, then the freeze
See CHANGELOG.md for details.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file timefuzz-0.3.1.tar.gz.
File metadata
- Download URL: timefuzz-0.3.1.tar.gz
- Upload date:
- Size: 53.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e34a62a9f0cdfdb72cc89669a919c1a05c4a1db03c3679eb7f5892f688c49a1
|
|
| MD5 |
332a43525e0dc9c634c0eadfcf62ec91
|
|
| BLAKE2b-256 |
42f8a3334cce13c0697035a481735794ff0c4d4e835a0d5a7e597cdfe8d4a9ad
|
Provenance
The following attestation bundles were made for timefuzz-0.3.1.tar.gz:
Publisher:
release.yml on Londopy/timefuzz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timefuzz-0.3.1.tar.gz -
Subject digest:
3e34a62a9f0cdfdb72cc89669a919c1a05c4a1db03c3679eb7f5892f688c49a1 - Sigstore transparency entry: 2157797026
- Sigstore integration time:
-
Permalink:
Londopy/timefuzz@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Londopy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Trigger Event:
push
-
Statement type:
File details
Details for the file timefuzz-0.3.1-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: timefuzz-0.3.1-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 201.7 kB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
248133bc5b7f1b2e3d45c2eb866568d1ac670124593286fec7d8271a7b9f2efb
|
|
| MD5 |
5ed268b33e66b25af8d4ee17c0fec28c
|
|
| BLAKE2b-256 |
efc858051acd8170d92799a8f1a06c9091e0ffde414e27c8eeb32ce0c755d64c
|
Provenance
The following attestation bundles were made for timefuzz-0.3.1-cp310-abi3-win_amd64.whl:
Publisher:
release.yml on Londopy/timefuzz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timefuzz-0.3.1-cp310-abi3-win_amd64.whl -
Subject digest:
248133bc5b7f1b2e3d45c2eb866568d1ac670124593286fec7d8271a7b9f2efb - Sigstore transparency entry: 2157797850
- Sigstore integration time:
-
Permalink:
Londopy/timefuzz@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Londopy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Trigger Event:
push
-
Statement type:
File details
Details for the file timefuzz-0.3.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: timefuzz-0.3.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 320.8 kB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bab455972e9813dac1a9a294c14d892431ae8724da84e8b6cd425c1d9929bd22
|
|
| MD5 |
87ed6ad4132d9314cc98afc6cd9b6227
|
|
| BLAKE2b-256 |
9bc571a0036e4c8207f236332e9e8770a3d54f7cd7f9c72e1ab934f8094c5942
|
Provenance
The following attestation bundles were made for timefuzz-0.3.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on Londopy/timefuzz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timefuzz-0.3.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
bab455972e9813dac1a9a294c14d892431ae8724da84e8b6cd425c1d9929bd22 - Sigstore transparency entry: 2157798042
- Sigstore integration time:
-
Permalink:
Londopy/timefuzz@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Londopy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Trigger Event:
push
-
Statement type:
File details
Details for the file timefuzz-0.3.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: timefuzz-0.3.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 319.2 kB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5323d53b8964029558ba834785d2a7dc706a6c342e81e025acd0210afb4da20
|
|
| MD5 |
a7825d68eee70f27fbd40460c5a3feba
|
|
| BLAKE2b-256 |
4a7b4b31648682ac26a4bcea65d9b6aedd4f53f83f2eab7246b5b6d702f70206
|
Provenance
The following attestation bundles were made for timefuzz-0.3.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on Londopy/timefuzz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timefuzz-0.3.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
a5323d53b8964029558ba834785d2a7dc706a6c342e81e025acd0210afb4da20 - Sigstore transparency entry: 2157797663
- Sigstore integration time:
-
Permalink:
Londopy/timefuzz@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Londopy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Trigger Event:
push
-
Statement type:
File details
Details for the file timefuzz-0.3.1-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: timefuzz-0.3.1-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 292.8 kB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8d1bc7ad88fdee42191b4d0fd8cd1ec7899798e5439e436db8743164871b475
|
|
| MD5 |
d2ccfc5e600a91ced9d1fba9dfa054e3
|
|
| BLAKE2b-256 |
8a2bbba3785c93bfbedbdb972ad5a7f3d838c99fd958edbb1e2e769725d0376e
|
Provenance
The following attestation bundles were made for timefuzz-0.3.1-cp310-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on Londopy/timefuzz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timefuzz-0.3.1-cp310-abi3-macosx_11_0_arm64.whl -
Subject digest:
a8d1bc7ad88fdee42191b4d0fd8cd1ec7899798e5439e436db8743164871b475 - Sigstore transparency entry: 2157797463
- Sigstore integration time:
-
Permalink:
Londopy/timefuzz@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Londopy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Trigger Event:
push
-
Statement type:
File details
Details for the file timefuzz-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: timefuzz-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 304.6 kB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1105fb79548446db48d001b6eb522836fea04ffe9abe891f014e82d07e067c0a
|
|
| MD5 |
1ad9612f0ebdc9e8ce6104d9612c08de
|
|
| BLAKE2b-256 |
27b3b0932dba80c5fd162398b468052aa40aaf34960b4bef62e319173f3f20e9
|
Provenance
The following attestation bundles were made for timefuzz-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on Londopy/timefuzz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timefuzz-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl -
Subject digest:
1105fb79548446db48d001b6eb522836fea04ffe9abe891f014e82d07e067c0a - Sigstore transparency entry: 2157797244
- Sigstore integration time:
-
Permalink:
Londopy/timefuzz@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Londopy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0ed95facab1cd67b4a56e6b16ee878b1be76f73e -
Trigger Event:
push
-
Statement type: