Skip to content

bissli/opendate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

129 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OpenDate

Date/time library built on Pendulum with business day support and financial calculations. Parsing is 1.2-6x faster than pendulum thanks to a Rust-based dateutil port.

pip install opendate

Quick Reference

# Preferred import (using canonical module name)
import opendate
from opendate import Date, DateTime, Time, Interval
from opendate import EST, UTC, LCL, WeekDay
from opendate import get_calendar, set_default_calendar

# Basics
today = Date.today()
now = DateTime.now()
parsed = Date.parse('T-3b')  # 3 business days ago

# Business days
today.b.add(days=5)                    # 5 business days forward
today.calendar('LSE').b.subtract(days=3)  # Using London calendar

# Change default calendar globally
set_default_calendar('LSE')

Module Functions

Function Description
date(y, m, d) Create Date
datetime(y, m, d, h, m, s, tz=UTC) Create DateTime (defaults to UTC)
time(h, m, s, tz=UTC) Create Time (defaults to UTC)
interval(start, end) Create Interval
parse(s, calendar=None) Parse to DateTime (calendar optional, uses module default)
instance(obj) Convert datetime/date/time (preserves obj's tz if present, else UTC)
now(tz=None) Current DateTime (local tz if None)
today(tz=None) Today at 00:00:00 (local tz if None)
get_calendar(name) Get calendar instance
set_default_calendar(name) Set module default calendar
get_default_calendar() Get current default calendar name
available_calendars() List available calendars

Date

Constructors

Method Description
Date(y, m, d) Create from components
Date.today() Current date (uses local tz for current day)
Date.parse(s, calendar='NYSE') Parse string. Calendar used for business day codes (T-3b, P), defaults to NYSE
Date.instance(obj) From datetime.date, Timestamp, datetime64
Date.fromordinal(n) From ordinal
Date.fromtimestamp(ts, tz=None) From Unix timestamp (UTC if None)

Properties

Inherited from pendulum: year, month, day, day_of_week, day_of_year, week_of_month, week_of_year, days_in_month, quarter

Arithmetic

Method Description
add(years, months, weeks, days) Add time units
subtract(years, months, weeks, days) Subtract time units
diff(other) Get Interval between dates

Period Boundaries

Method Description
start_of(unit) Start of day/week/month/quarter/year
end_of(unit) End of day/week/month/quarter/year
first_of(unit, day_of_week=None) First (or first weekday) of period
last_of(unit, day_of_week=None) Last (or last weekday) of period
nth_of(unit, n, day_of_week) Nth occurrence of weekday in period

Navigation

Method Description
next(day_of_week=None) Next occurrence of weekday
previous(day_of_week=None) Previous occurrence of weekday
closest(d1, d2) Closer of two dates
farthest(d1, d2) Further of two dates
average(other=None) Midpoint between dates
lookback(unit) Go back by unit: day/week/month/quarter/year

Business Day

Method Description
business() or .b Enable business day mode
calendar(name) Set calendar (NYSE, LSE, etc.)
is_business_day() Check if business day
business_hours() Returns (open, close) or (None, None)

Extras

Method Description
isoweek() ISO week number (1-53)
nearest_start_of_month() Nearest month start (threshold: day 15)
nearest_end_of_month() Nearest month end (threshold: day 15)
weekday_or_previous_friday() Snap weekend to Friday
to_string(fmt) Format (handles Windows %-%#)

DateTime

Constructors

Method Description
DateTime(y, m, d, h, m, s, us, tzinfo) Create from components
DateTime.now(tz=None) Current time (local tz if None)
DateTime.today(tz=None) Today at 00:00:00 (local tz if None, differs from pendulum)
DateTime.parse(s, calendar='NYSE') Parse string or timestamp. Strings: preserve explicit tz, else naive. Timestamps: local tz. Calendar used for business day codes, defaults to NYSE
DateTime.instance(obj, tz=None) From datetime, Timestamp, datetime64. Preserves obj's tz if present, else uses tz param, else UTC
DateTime.combine(date, time, tzinfo=None) Combine Date and Time (uses time's tz if tzinfo=None)
DateTime.fromtimestamp(ts, tz=None) From Unix timestamp (UTC if None)
DateTime.utcfromtimestamp(ts) From timestamp as UTC
DateTime.utcnow() Current UTC time
DateTime.strptime(s, fmt) Parse with format
DateTime.fromordinal(n) From ordinal

Properties

Inherited from pendulum: year, month, day, hour, minute, second, microsecond, day_of_week, day_of_year, week_of_month, week_of_year, days_in_month, quarter, timestamp, timezone, tz, timezone_name, offset, offset_hours

Extraction

Method Description
date() Extract Date
time() Extract Time (preserves tz)
epoch() Unix timestamp (alias for timestamp())

Timezone

Method Description
in_timezone(tz) Convert to timezone
in_tz(tz) Alias for in_timezone
astimezone(tz) Alias for in_timezone

Arithmetic & Navigation

Same as Date: add, subtract, diff, start_of, end_of, first_of, last_of, nth_of, next, previous

Business Day

Same as Date: business(), .b, calendar(name), is_business_day(), business_hours()

Formatting

Method Description
format(fmt, locale=None) Format with tokens
isoformat() ISO 8601 string
rfc3339() RFC 3339 (same as isoformat)
to_date_string() YYYY-MM-DD
to_datetime_string() YYYY-MM-DD HH:MM:SS
to_time_string() HH:MM:SS
to_iso8601_string() Full ISO 8601

Time

Constructors

Method Description
Time(h, m, s, us, tzinfo) Create from components
Time.parse(s, fmt=None) Parse string (always UTC)
Time.instance(obj, tz=None) From datetime.time, datetime. Preserves obj's tz if present, else uses tz param, else UTC

Properties

hour, minute, second, microsecond, tzinfo

Methods

Method Description
in_timezone(tz) Convert to timezone
in_tz(tz) Alias

Interval

Constructor

Interval(start, end)  # start/end are Date or DateTime

Properties

Property Description
days Calendar days (business days if .b)
months Float with fractional months (differs from pendulum)
years Complete years (floors)
quarters Approximate (days/365*4)
start Start date
end End date

Methods

Method Description
business() or .b Enable business day mode
calendar(name) Set calendar
range(unit, amount=1) Iterate by unit (days/weeks/months/years)
is_business_day_range() Yields bool for each day
start_of(unit) List of period starts in interval
end_of(unit) List of period ends in interval
yearfrac(basis) Year fraction (Excel-compatible)

yearfrac Basis

Basis Convention Use
0 US (NASD) 30/360 Corporate bonds
1 Actual/actual Treasury bonds
2 Actual/360 Money market
3 Actual/365 Some bonds
4 European 30/360 Eurobonds
5 Actual/365.25 Avg year length

Parsing

OpenDate uses a dateutil-compatible Rust parser that handles virtually any date/time format. The parser supports fuzzy matching, multiple locales, and automatic format detection.

Special Codes

Code Meaning
T or N Today
Y Yesterday
P Previous business day
M Last day of previous month

Business Day Offsets

Pattern Example Meaning
{code}±{n} T-5 5 calendar days ago
{code}±{n}b T-3b 3 business days ago

Parser Capabilities

  • Dates: ISO 8601, US/European formats, compact, natural language
  • Times: 12/24-hour, with/without seconds, AM/PM, timezones
  • Combined: Any date + time combination, Unix timestamps (auto-detects ms/s)
  • Fuzzy: Extracts dates from text containing other content
# All of these work
Date.parse('2024-01-15')
Date.parse('Jan 15, 2024')
Date.parse('15/01/2024')
DateTime.parse('2024-01-15T09:30:00Z')
DateTime.parse(1640995200)  # Unix timestamp
DateTime.parse('meeting on Jan 15 at 3pm')  # Fuzzy

Business Days

Default Calendar

from opendate import set_default_calendar, get_default_calendar

get_default_calendar()      # 'NYSE' initially
set_default_calendar('LSE') # Change globally

Per-Operation Calendar

date.calendar('NYSE').b.add(days=5)
date.calendar('LSE').is_business_day()

Available Calendars

All calendars from exchange-calendars: NYSE, LSE, XLON, XPAR, XFRA, XJPX, XHKG, etc.

from opendate import available_calendars
print(available_calendars())

Business Day Examples

# Add/subtract business days
Date(2024, 3, 29).b.add(days=1)  # Skips Good Friday + weekend

# Period boundaries
Date(2024, 7, 1).b.start_of('month')  # First business day of July
Date(2024, 4, 30).b.end_of('month')   # Last business day of April

# Iterate business days only
for d in Interval(start, end).b.range('days'):
    print(d)

# Count business days
Interval(start, end).b.days

Timezones

Built-in

Constant Zone
UTC UTC
GMT GMT
EST America/New_York
LCL System local

Custom

from opendate import Timezone
tokyo = Timezone('Asia/Tokyo')
dt = DateTime(2024, 1, 15, 9, 30, tzinfo=tokyo)

Conversion

dt.in_timezone(UTC)   # or in_tz() or astimezone()

Decorators

Decorator Effect
@expect_date Converts arg to Date
@expect_datetime Converts arg to DateTime
@expect_time Converts arg to Time
@expect_date_or_datetime Converts to Date or DateTime
@prefer_utc_timezone Adds UTC if no tz
@expect_utc_timezone Converts result to UTC
@prefer_native_timezone Adds local tz if no tz
@expect_native_timezone Converts result to local

Extras Module

Legacy standalone functions:

from opendate import is_business_day, is_within_business_hours, overlap_days

is_business_day()              # Is today a business day?
is_within_business_hours()     # Is current time in market hours?

overlap_days(int1, int2)           # Do intervals overlap? (bool)
overlap_days(int1, int2, days=True) # Day count of overlap (int)

Pendulum Differences

Feature Pendulum OpenDate
DateTime.today() Current time Start of day (00:00:00)
DateTime.instance() default tz None Preserves obj's tz if present, else UTC
Time.parse() default tz None UTC
Interval.months int float (fractional)
Business day support No Yes
Default calendar N/A set_default_calendar()

Examples

Month-End Dates

interval = Interval(Date(2024, 1, 1), Date(2024, 12, 31))
month_ends = interval.end_of('month')
business_month_ends = interval.b.end_of('month')

Third Friday (Options Expiration)

Date.today().add(months=1).start_of('month').nth_of('month', 3, WeekDay.FRIDAY)

Interest Accrual

days_fraction = Interval(issue_date, settlement_date).yearfrac(2)  # Actual/360
accrued = coupon_rate * days_fraction

Market Hours Check

now = DateTime.now(tz=get_calendar('NYSE').tz)
if now.calendar('NYSE').is_business_day():
    open_time, close_time = now.business_hours()
    if open_time and open_time <= now <= close_time:
        print("Market open")

Development

See docs/developer-guide.md

License

MIT - Built on Pendulum and pandas-market-calendars

About

Python business datetimes

Resources

License

Stars

Watchers

Forks

Packages

No packages published