DateUtils Python
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
arroworpendulum - Extensive internationalization - Use
pendulumorbabel
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)
| File | Size | Uploaded | |
|---|---|---|---|
| dateutils_python-0.8.3.tar.gz | 64.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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