Skip to main content

DateUtils Python

PyPI version PyPI downloads Python versions CI Coverage License: MIT Typed

A comprehensive collection of date and time utilities for Python applications.

Features

  • UTC Operations: Easily work with UTC timestamps and dates
  • Date/Time Parsing: Parse dates and times from strings in various formats
  • Timezone Handling: Convert between timezones with robust error handling
  • Business Days: Calculate business days with support for holidays and validation
  • Quarter/Month/Week Operations: Helpers for working with larger time spans
  • Date Formatting: Convert dates to various string formats including ISO 8601
  • Human-readable Dates: Generate user-friendly date strings like "2 hours ago"
  • Additional Utilities: Age calculation, weekend detection, week numbers, and more
  • Memory Efficiency: Generator-based functions for large date ranges
  • Input Validation: Comprehensive validation with helpful error messages

Why dateutils-python?

There are several excellent date/time libraries in the Python ecosystem. Here's how dateutils-python compares:

Feature dateutils-python python-dateutil arrow pendulum
Zero dependencies Yes Yes No (1 dep) No (3 deps)
Business day calculations Built-in No No No
US Federal holidays Built-in No No No
Fully typed Yes Partial Partial Yes
Human-readable output Yes No Yes Yes
Timezone handling Yes Yes Yes Yes
Date parsing Multi-format Powerful/fuzzy ISO-focused ISO-focused
Relative date parsing No Yes ("next Friday") Yes Yes
Maintained Active Active Active Active

Choose dateutils-python if you want:

  • Zero dependency overhead - Uses only Python's standard library. No transitive dependencies to audit, no version conflicts, no supply chain concerns.
  • Business day logic out of the box - Calculate workdays, skip holidays, find next/previous business day without additional libraries.
  • US Federal holidays included - No need to maintain your own holiday list or pull in another package.
  • Full type coverage - Every function is fully typed for IDE autocompletion and static analysis.
  • Lightweight and focused - Does common date operations well without trying to be everything.

Consider alternatives if you need:

  • Fuzzy date parsing ("next tuesday", "in 2 weeks") - Use python-dateutil
  • Immutable date objects with method chaining - Use pendulum
  • Drop-in datetime replacement - Use arrow or pendulum
  • Extensive internationalization - Use pendulum or babel

Installation

pip install dateutils-python

Usage Examples

UTC and Timestamp Operations

from dateutils import utc_now_seconds, utc_today, epoch_s

# Get current UTC timestamp
timestamp = utc_now_seconds()  # e.g., 1717682345

# Get current UTC date
today = utc_today()  # e.g., datetime.date(2024, 6, 6)

# Convert a datetime to timestamp
from datetime import datetime

dt = datetime(2024, 6, 6, 12, 30, 0)
ts = epoch_s(dt)  # 1717677000

Business Day Calculations

from dateutils import workdays_between, add_business_days, next_business_day, previous_business_day, is_business_day
from datetime import date

# Calculate workdays between dates
start = date(2024, 6, 1)
end = date(2024, 6, 10)
workdays = workdays_between(start, end)  # Excludes weekends

# With holiday support
holidays = [date(2024, 6, 5)]  # Example holiday
workdays = workdays_between(start, end, holidays)  # Excludes weekends and holidays

# Add business days
new_date = add_business_days(start, 5)  # Skip weekends
new_date = add_business_days(start, 5, holidays)  # Skip weekends and holidays

# Check if a date is a business day
monday = date(2024, 6, 3)
is_business = is_business_day(monday)  # True
is_business_with_holiday = is_business_day(monday, holidays=[monday])  # False

# Get next/previous business day
next_bday = next_business_day(date(2024, 6, 7))  # Friday -> Monday
prev_bday = previous_business_day(date(2024, 6, 3))  # Monday -> Friday

Holiday Support

from datetime import date

from dateutils import get_us_federal_holidays, get_us_federal_holidays_list, workdays_between

# Get all US federal holidays for a year (cached for performance)
holidays_2024 = get_us_federal_holidays(2024)  # Returns 11 holidays

# Get observed holidays for business-day calculations (weekend holidays shift to Fri/Mon)
observed_2021 = get_us_federal_holidays(2021, observed=True)

# Get specific holiday types
fixed_holidays = get_us_federal_holidays(2024, ("NEW_YEARS_DAY", "CHRISTMAS"))

# Use with business day functions
start = date(2024, 6, 1)
end = date(2024, 6, 10)
workdays = workdays_between(start, end, holidays=holidays_2024)

US federal holiday calculations support 1971 and later. The calendar models rule changes within that range, including Veterans Day's fourth-Monday-in-October schedule from 1971 through 1977, MLK Day from 1986, and Juneteenth from 2021. Years before 1971 raise ValueError rather than projecting modern rules backward.

Date Parsing and Formatting

from dateutils import ParseError, parse_date, parse_datetime, parse_iso8601, to_iso8601

# Parse a date string (tries multiple formats, raises ParseError on failure)
dt = parse_date("2024-06-06")  # Returns date object
dt = parse_date("06/06/2024")  # Also works
dt = parse_date("June 6, 2024")  # Also works

# Ambiguous numeric dates follow the current locale by default. Use explicit
# ordering or reject ambiguous imports when the input source is untrusted.
dt = parse_date("03/04/2024", dayfirst=False)
try:
    dt = parse_date("03/04/2024", ambiguous="reject")
except ParseError:
    pass  # Rejected because the numeric order is ambiguous.

# Parse ISO 8601 datetime
dt = parse_iso8601("2024-06-06T12:30:45Z")  # With timezone
dt = parse_iso8601("2024-06-06")  # Date only

# Parse with explicit error handling
try:
    parse_datetime("not a datetime")
except ParseError as e:
    print(e)  # Includes parser name, bad value, and parse reason

# Format as ISO 8601
iso_str = to_iso8601(dt)  # e.g., "2024-06-06T12:30:45+00:00"

Timezone Handling

from dateutils import (
    now_in_timezone,
    localize_datetime,
    convert_timezone,
    get_available_timezones,
    get_timezone_offset,
    format_timezone_offset,
)
from datetime import datetime, timezone

# Get current time in a timezone
nyc_now = now_in_timezone("America/New_York")

# Timezone arguments also accept tzinfo instances
utc_now = now_in_timezone(timezone.utc)

# Convert between timezones
utc_dt = datetime(2024, 6, 6, 12, 0, 0, tzinfo=timezone.utc)
tokyo_dt = convert_timezone(utc_dt, "Asia/Tokyo")

# Attach a timezone to a naive local time. DST edge cases require a policy.
meeting = localize_datetime(datetime(2024, 11, 3, 1, 30), "America/New_York", ambiguous="latest")

# Get available timezones
timezones = get_available_timezones()  # List of all available timezone names

# Get timezone offset
offset = get_timezone_offset("America/New_York")  # Returns timedelta
offset_str = format_timezone_offset("America/New_York")  # Returns "-05:00" or "-04:00" (DST)

Quarter and Month Operations

from dateutils import (
    date_to_quarter,
    date_to_start_of_quarter,
    get_quarter_start_end,
    start_of_quarter,
    end_of_quarter,
    start_of_month,
    end_of_month,
)
from datetime import datetime

# Get quarter for a date
dt = datetime(2024, 5, 15)
quarter = date_to_quarter(dt)  # Returns 2 (Q2)

# Get start of quarter
start_date = date_to_start_of_quarter(dt)  # Returns 2024-04-01

# Get quarter date range
start, end = get_quarter_start_end(2024, 2)  # Q2 2024: (2024-04-01, 2024-06-30)

# Get precise quarter boundaries
q_start = start_of_quarter(2024, 2)  # 2024-04-01 00:00:00
q_end = end_of_quarter(2024, 2)  # 2024-06-30 23:59:59.999999

Additional Utility Functions

from dateutils import (
    age_in_years,
    days_until_weekend,
    days_since_weekend,
    get_week_number,
    next_occurrence,
    time_until_next_occurrence,
)
from datetime import date, datetime

# Calculate age in years
birth_date = date(1990, 6, 15)
age = age_in_years(birth_date, date(2024, 6, 20))  # 34 years old

# Weekend-related functions
monday = date(2024, 6, 3)
days_to_weekend = days_until_weekend(monday)  # 5 days until Saturday
days_from_weekend = days_since_weekend(monday)  # 1 day since Sunday

# Get ISO week number
week_num = get_week_number(date(2024, 6, 6))  # Week number (1-53)

# Calculate time until next occurrence
target_time = datetime(2024, 6, 6, 15, 30, 0)  # 3:30 PM today/tomorrow
time_delta = time_until_next_occurrence(target_time)  # Time until next 3:30 PM
next_run = next_occurrence(target_time)  # Datetime of the next 3:30 PM

Memory-Efficient Date Ranges

from dateutils import date_range, date_range_generator
from datetime import date

# Standard date range (good for small ranges)
start = date(2024, 1, 1)
end = date(2024, 1, 10)
dates = date_range(start, end)  # Returns list of dates

# Memory-efficient generator (good for large ranges)
large_end = date(2024, 12, 31)
date_gen = date_range_generator(start, large_end)  # Returns generator

# Process large ranges without memory issues
for dt in date_gen:
    if dt.month == 6:  # Only process June dates
        print(dt)
        break

Human-readable Dates

from dateutils import pretty_date
from datetime import datetime, timedelta

# Format a datetime as a human-readable string
now = datetime.now()
an_hour_ago = now - timedelta(hours=1)
a_day_ago = now - timedelta(days=1)

pretty_date(an_hour_ago)  # "an hour ago"
pretty_date(a_day_ago)  # "Yesterday"

Error Handling and Validation

The library now includes comprehensive input validation with helpful error messages:

Functions whose parameters and return values are dates treat a datetime input as its local calendar date (value.date()). They return plain date values and do not convert timezone-aware inputs to another timezone first.

from datetime import date
from zoneinfo import ZoneInfoNotFoundError

from dateutils import ParseError, now_in_timezone, parse_date, start_of_quarter, workdays_between

# Invalid quarter raises ValueError
try:
    start_of_quarter(2024, 5)  # Quarters must be 1-4
except ValueError as e:
    print(e)  # "Quarter must be between 1 and 4, got 5"

# Invalid date ordering raises ValueError
try:
    workdays_between(date(2024, 6, 10), date(2024, 6, 1))  # end < start
except ValueError as e:
    print(e)  # "start_date (2024-06-10) must be <= end_date (2024-06-01)"

# Invalid parse raises ParseError
try:
    parse_date("2024-99-99")
except ParseError as e:
    print(e)  # "Failed to parse date from '2024-99-99': ..."

# Invalid timezone names raise ZoneInfoNotFoundError
try:
    now_in_timezone("Invalid/Timezone")
except ZoneInfoNotFoundError as e:
    print(e)  # e.g., "No time zone found with key Invalid/Timezone"

Performance Features

  • Cached Holiday Calculations: US federal holidays are cached for better performance when called multiple times
  • Memory-Efficient Generators: Use date_range_generator() for large date ranges
  • Optimized UTC Operations: Improved performance for timestamp operations
  • Input Validation: Early validation prevents expensive operations on invalid data

Running Tests

Makefile

make init # ensures uv is installed
make deps
make check

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Metadata

Release files for dateutils-python 0.8.3

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

Source distribution (sdist)

Source distribution for dateutils-python 0.8.3
File Size Uploaded
dateutils_python-0.8.3.tar.gz 64.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dateutils-python 0.8.3
File Interpreter ABI Platform
dateutils_python-0.8.3-py3-none-any.whl Python 3 none any Details

Total release size: 96.7 kB

Release files / dateutils_python-0.8.3.tar.gz

Download URL dateutils_python-0.8.3.tar.gz
Size 64.6 kB
Tags Source
SHA-256 checksum
How to use checksums
b5ad043005f69ea34184ea914951ad54f0649e81af3e245d844bcd401074280b
BLAKE2b-256 checksum
How to use checksums
dfcbbd4275877416dc8e3e0657a222cf1b0d9ef1b4f4b9beac0f2054ba7c0d02
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 5, 2026.

Transparency log

Release files / dateutils_python-0.8.3-py3-none-any.whl

Download URL dateutils_python-0.8.3-py3-none-any.whl
Size 32.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6568ab6496784afcbb79f754ad1acea33cfea516bc21e38960512639f43eee55
BLAKE2b-256 checksum
How to use checksums
67d5c98cf5700b6c1facad95d6660b806226a34f51fee232ac1a73161b262f0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.3 This release

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.1

2 release files

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