Skip to main content

Fast natural-language date parser for Python, with an optional C-accelerated scanner

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

metadate-0.6.2.tar.gz (55.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

metadate-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (123.2 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

metadate-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (123.2 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

metadate-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (121.6 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

File details

Details for the file metadate-0.6.2.tar.gz.

File metadata

  • Download URL: metadate-0.6.2.tar.gz
  • Upload date:
  • Size: 55.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for metadate-0.6.2.tar.gz
Algorithm Hash digest
SHA256 0780056ca92d285ed3e678b8b7b39590aac5fe836f4ee3cd40e85dd1a34c64e2
MD5 b5c3ff3436dd1cb6f96fa190f653ad7e
BLAKE2b-256 ab0c0e2f78f9bc1c257e5e5df12c8bc11ce9170a4231a8e128cc596cbd575891

See more details on using hashes here.

Provenance

The following attestation bundles were made for metadate-0.6.2.tar.gz:

Publisher: publish.yml on kootenpv/metadate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file metadate-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for metadate-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f4e63cffa01c7cd450793873d463b8517c379bbe61145853dfd0197eff151e9f
MD5 13689f2fe2648eba79f6cbd66d318687
BLAKE2b-256 955bd25ee9edafb246356bba29521f1842da5b60d159799144e1e8cfa706fe1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for metadate-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on kootenpv/metadate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file metadate-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for metadate-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0e2348a6233c009df33752ff69afe5a7530a754f2ebef04db81f03d04b88c8bc
MD5 6ebfd88375ea4e50023a958328a11523
BLAKE2b-256 1c84be74b3d7172071ae8d9d84c2ac41429de14e7971dff6b3a3827c984b8c64

See more details on using hashes here.

Provenance

The following attestation bundles were made for metadate-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on kootenpv/metadate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file metadate-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for metadate-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4fe8e579826f344b8aea521994cba366832fbf13a153149e7d81d7caeb242490
MD5 2183f18f2805a7caf22ca235470e3914
BLAKE2b-256 39b925d97978d4054a08e24a3d0e55a5ced15292d02513531e985a76753c0de5

See more details on using hashes here.

Provenance

The following attestation bundles were made for metadate-0.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on kootenpv/metadate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page