Skip to main content

metadate logo

metadate

Fast natural-language date parsing for Python — with an optional C-accelerated scanner.

PyPI Python License


metadate parses human-written date expressions into structured datetime ranges. It handles everything from "tomorrow at 3 pm" to "the last 2 weeks of March 2024" — and does it fast, with an optional C extension that provides up to 50x speedup over pure-Python regex scanning.

Features

  • Natural language — understands relative dates ("next tuesday", "3 days ago"), absolute dates ("June 25", "2024-01-15"), times ("at 3pm"), ranges, and combinations
  • Fast C scanner — optional C extension using a FNV-1a hash table for sub-millisecond parsing
  • ISO 8601 fast-path — optional ciso8601 integration for near-instant ISO date parsing
  • Granularity levels — know whether a result has year, month, day, hour, minute, or second precision
  • Start & end dates — every result is a period with start_date and end_date
  • Multi-date extraction — pull all dates out of a block of text
  • Timezone support — recognizes timezone names and abbreviations
  • Locale support — English and Dutch built-in, extensible to other languages
  • CLI included — parse dates from the command line

Installation

pip install metadate

For faster ISO 8601 parsing:

pip install metadate[fastciso]

The C extension is compiled automatically when a C compiler is available. If compilation fails (e.g. no compiler installed), metadate falls back to the pure-Python scanner — no functionality is lost, only speed.

Quick start

from metadate import parse_date

# Simple dates
r = parse_date("tomorrow at 3pm")
print(r.start_date)  # 2026-03-13 15:00:00
print(r.end_date)    # 2026-03-13 16:00:00

# Relative dates
r = parse_date("last 2 weeks")
print(r.start_date)  # 2 weeks ago
print(r.end_date)    # now

# Absolute dates
r = parse_date("June 25, 2024")
print(r.start_date)  # 2024-06-25 00:00:00
print(r.end_date)    # 2024-06-26 00:00:00

# Extract multiple dates from text
results = parse_date(
    "The meeting moved from March 5 to March 12 at 2pm",
    multi=True
)
for r in results:
    print(r.start_date, "—", r.end_date)

Using the C scanner

The C scanner is used automatically when available. To explicitly enable or disable it:

# Force C scanner
r = parse_date("next friday", use_c_scanner=True)

# Force pure-Python scanner
r = parse_date("next friday", use_c_scanner=False)

API reference

parse_date(text, **kwargs) -> MetaPeriod | list[MetaPeriod] | None

Parameter Type Default Description
text str — The text to parse
reference_date datetime now() Anchor for relative dates
future relativedelta 30 years How far ahead to search
past relativedelta 100 years How far back to search
lang str "en" Locale ("en" or "nl")
multi bool False Return all dates found
use_c_scanner bool False Use C-accelerated scanner
min_level Units None Minimum granularity filter
max_level Units None Maximum granularity filter
verbose bool False Print debug info

MetaPeriod

Property Type Description
start_date datetime Start of the parsed period
end_date datetime End of the parsed period
levels set[Units] Granularity levels present
matches list[str] Matched text fragments
has_time bool Whether time was specified
has_day bool Whether day was specified
has_month bool Whether month was specified
has_year bool Whether year was specified
is_in_past bool Whether the date is in the past
tz tzinfo|None Timezone if detected
to_dict() dict Serialize to dictionary

Units

Granularity levels from coarsest to finest:

YEAR (9) > SEASON (8) > QUARTER (7) > MONTH (6) > WEEK (5) > DAY (4) > HOUR (3) > MINUTE (2) > SECOND (1) > MICROSECOND (0)

Every MetaPeriod carries a levels set that tells you exactly which components were specified in the input. Use min_level and max_level on parse_date to filter results by granularity:

from metadate import parse_date, Units

# "June 2024" has levels {YEAR, MONTH} — no day or time
r = parse_date("June 2024")
print(r.levels)     # {<Units.YEAR: 9>, <Units.MONTH: 6>}
print(r.has_month)  # True
print(r.has_day)    # False
print(r.has_time)   # False

# "tomorrow at 3pm" has levels {DAY, HOUR, MINUTE}
r = parse_date("tomorrow at 3pm")
print(r.levels)     # {<Units.DAY: 4>, <Units.HOUR: 3>, <Units.MINUTE: 2>}
print(r.has_time)   # True
print(r.min_level)  # Units.MINUTE (finest level present)
print(r.max_level)  # Units.DAY    (coarsest level present)

# Filter: only accept results that include at least HOUR precision
# min_level=Units.HOUR means "the finest level must be HOUR or finer"
r = parse_date("June 2024", min_level=Units.HOUR)
print(r)  # None — "June 2024" only has MONTH precision, no time

r = parse_date("tomorrow at 3pm", min_level=Units.HOUR)
print(r.start_date)  # matches — has HOUR

# Filter: only accept results no coarser than MONTH
# max_level=Units.MONTH means "the coarsest level must be MONTH or finer"
r = parse_date("2024", max_level=Units.MONTH)
print(r)  # None — "2024" has YEAR as its coarsest level

r = parse_date("June 2024", max_level=Units.MONTH)
print(r.start_date)  # matches — coarsest level is MONTH

# Multi-date extraction with level filtering
text = "Sometime in 2024, specifically June 15 at 10am"
results = parse_date(text, multi=True, min_level=Units.DAY)
# Only returns "June 15 at 10am", skips "2024" (no day precision)

CLI

# Parse a single expression
metadate tomorrow at 3pm

# Parse with a specific locale
metadate --lang nl overmorgen om 15 uur

# Distance between two dates
metadate "January 1" "March 15"

Supported expressions

Category Examples
Today/tomorrow today, tomorrow, yesterday, day after tomorrow
Weekdays next tuesday, last friday, this wednesday
Months June, June 2024, June 25, June 25, 2024
Relative 3 days ago, in 2 weeks, next month, last year
ISO dates 2024-01-15, 2024-01-15T10:30:00
Times at 3pm, 10:30, 15:00:00
Ranges last 2 weeks, first 3 days of March
Seasons winter, next spring, last summer
Quarters Q1, Q3 2024, last quarter
Combined next tuesday at 3pm, June 25 2024 10:30am

License

MIT

Metadata

Release files for metadate 0.6.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for metadate 0.6.2
File Size Uploaded
metadate-0.6.2.tar.gz 55.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for metadate 0.6.2
File Interpreter ABI Platform
metadate-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ x86-64, Linux glibc 2.5+ x86-64 Details
metadate-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ x86-64, Linux glibc 2.5+ x86-64 Details
metadate-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.5+ x86-64, Linux glibc 2.17+ x86-64 Details

Total release size: 423.4 kB

Release files / metadate-0.6.2.tar.gz

Download URL metadate-0.6.2.tar.gz
Size 55.5 kB
Tags Source
SHA-256 checksum
How to use checksums
0780056ca92d285ed3e678b8b7b39590aac5fe836f4ee3cd40e85dd1a34c64e2
BLAKE2b-256 checksum
How to use checksums
ab0c0e2f78f9bc1c257e5e5df12c8bc11ce9170a4231a8e128cc596cbd575891
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 13, 2026.

Transparency log

Release files / metadate-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL metadate-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 123.2 kB
Tags CPython 3.13 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
f4e63cffa01c7cd450793873d463b8517c379bbe61145853dfd0197eff151e9f
BLAKE2b-256 checksum
How to use checksums
955bd25ee9edafb246356bba29521f1842da5b60d159799144e1e8cfa706fe1f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 13, 2026.

Transparency log

Release files / metadate-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL metadate-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 123.2 kB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
0e2348a6233c009df33752ff69afe5a7530a754f2ebef04db81f03d04b88c8bc
BLAKE2b-256 checksum
How to use checksums
1c84be74b3d7172071ae8d9d84c2ac41429de14e7971dff6b3a3827c984b8c64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 13, 2026.

Transparency log

Release files / metadate-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL metadate-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 121.6 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
4fe8e579826f344b8aea521994cba366832fbf13a153149e7d81d7caeb242490
BLAKE2b-256 checksum
How to use checksums
39b925d97978d4054a08e24a3d0e55a5ced15292d02513531e985a76753c0de5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.2 This release

4 release files

0.6.1

1 release file

0.6.0

1 release file

0.5.77

2 release files

0.5.73

2 release files

0.5.72

2 release files

0.5.70

2 release files

0.5.68

2 release files

0.5.67

2 release files

0.5.66

2 release files

0.5.65

2 release files

0.5.64

2 release files

0.5.0

2 release files

0.4.64

2 release files

0.4.63

2 release files

0.3.62

2 release files

0.2.60

2 release files

0.2.27

2 release files

0.0.31

2 release files

0.0.26

2 release files

0.0.22

2 release files

0.0.7

2 release files

0.0.0

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page