Skip to content

Latest commit

 

History

History
310 lines (228 loc) · 9.19 KB

README.md

File metadata and controls

310 lines (228 loc) · 9.19 KB

fasttime for Python

License

fasttime is a fast, lightweight UTC-focused date/time library for Python, powered by Rust. It provides simple, ergonomic APIs for working with dates, times, and durations with nanosecond precision.

Features

  • Fast: Built with Rust using Ben Joffe's constant-time 64-bit days→date algorithm
  • Simple: Clean, Pythonic API for common date/time operations
  • Precise: Nanosecond precision for all time values
  • UTC-focused: All datetime operations are in UTC by default
  • RFC 3339 support: Parse and format timestamps with fixed offsets
  • Type-safe: Full type hints for better IDE support and type checking

Installation

pip install fasttime-rs

Requires Python 3.10 or later.

Quick Start

import fasttime

# Create dates
date = fasttime.Date(2024, 1, 15)
print(date)  # 2024-01-15
print(date.weekday())  # Weekday.Monday
print(date.ordinal())  # 15

# Work with times
time = fasttime.Time(14, 30, 45, nanosecond=123_456_789)
print(time)  # 14:30:45.123456789

# Combine into UTC datetimes
dt = fasttime.DateTime(date, time)
print(dt)  # 2024-01-15T14:30:45.123456789Z

# Get current UTC time
now = fasttime.DateTime.now_utc()
print(f"Current UTC time: {now}")

# Work with durations
later = now.add_duration(fasttime.Duration.seconds(3600))
diff = later.difference(now)
print(f"Difference: {diff.total_seconds()} seconds")

# Parse from strings
parsed_date = fasttime.Date.parse("2024-12-25")
parsed_time = fasttime.Time.parse("23:59:59.999")
parsed_dt = fasttime.DateTime.parse("2024-12-25T23:59:59.999Z")

# Work with time zones (fixed offsets)
offset = fasttime.UtcOffset.from_hours_minutes(True, 5, 30)  # +05:30
local_dt = fasttime.OffsetDateTime.from_local(date, time, offset)
print(local_dt)  # 2024-01-15T14:30:45.123456789+05:30

# Unix timestamps
timestamp = dt.unix_timestamp()  # seconds
timestamp_ns = dt.unix_timestamp_nanos()  # nanoseconds
dt_from_ts = fasttime.DateTime.from_unix_timestamp(timestamp, 123_456_789)

Pickle serialization

DateTime and Duration support pickle.dumps() and pickle.loads() with all pickle protocols supported by your Python version. Integer nanoseconds preserve full precision, including negative values and the complete supported value ranges.

import pickle
import fasttime

before_epoch = fasttime.DateTime.from_unix_timestamp(0, -1)
restored = pickle.loads(pickle.dumps(before_epoch))
assert restored == before_epoch
assert restored.unix_timestamp_nanos() == -1
assert hash(restored) == hash(before_epoch)

duration = fasttime.Duration.nanoseconds(-1_000_000_001)
assert pickle.loads(pickle.dumps(duration)).total_nanos() == -1_000_000_001

Reconstruction uses the public Duration.nanoseconds() and DateTime.from_unix_timestamp() methods. The other fasttime types do not yet support pickling. As with any pickle data, only deserialize trusted inputs.

API Reference

Date

A Gregorian calendar date (year, month, day).

# Constructor
date = fasttime.Date(year: int, month: int, day: int)

# Properties
date.year: int        # Year (i32 range)
date.month: int       # Month (1-12)
date.day: int         # Day (1-31)

# Methods
date.weekday() -> Weekday              # Get day of week
date.ordinal() -> int                  # Day of year (1-366)
date.add_days(days: int) -> Date       # Add/subtract days
date.days_since_unix_epoch() -> int    # Days since 1970-01-01

# Class methods
Date.from_days_since_unix_epoch(days: int) -> Date
Date.parse(s: str) -> Date             # Parse "YYYY-MM-DD"

Time

A time of day with nanosecond precision.

# Constructor
time = fasttime.Time(hour: int, minute: int, second: int, nanosecond: int = 0)

# Properties
time.hour: int         # Hour (0-23)
time.minute: int       # Minute (0-59)
time.second: int       # Second (0-59)
time.nanosecond: int   # Nanosecond (0-999,999,999)

# Methods
time.seconds_since_midnight() -> int   # Total seconds (ignoring nanos)
time.nanos_since_midnight() -> int     # Total nanoseconds

# Class methods
Time.parse(s: str) -> Time             # Parse "HH:MM:SS[.fffffffff]"

DateTime

A UTC date and time.

# Constructor
dt = fasttime.DateTime(date: Date, time: Time)

# Properties
dt.date: Date
dt.time: Time

# Methods
dt.unix_timestamp() -> int                      # Seconds since Unix epoch
dt.unix_timestamp_nanos() -> int                # Nanoseconds since Unix epoch
dt.add_duration(dur: Duration) -> DateTime      # Add a duration
dt.difference(other: DateTime) -> Duration      # Calculate difference

# Class methods
DateTime.from_unix_timestamp(secs: int, nanos: int = 0) -> DateTime
DateTime.now_utc() -> DateTime                  # Current UTC time
DateTime.parse(s: str) -> DateTime              # Parse "YYYY-MM-DDTHH:MM:SS[.fff]Z"

Duration

A signed duration with nanosecond precision.

# Class methods (constructors)
Duration.seconds(secs: int) -> Duration
Duration.milliseconds(ms: int) -> Duration
Duration.microseconds(us: int) -> Duration
Duration.nanoseconds(ns: int) -> Duration

# Methods
dur.total_seconds() -> float      # Total seconds as float
dur.total_nanos() -> int          # Total nanoseconds as int

# Operators
dur1 + dur2                       # Add durations
dur1 - dur2                       # Subtract durations
-dur                              # Negate duration
dur1 < dur2                       # Compare durations

UtcOffset

A fixed offset from UTC.

# Class methods
UtcOffset.from_seconds(seconds: int) -> UtcOffset
UtcOffset.from_hours_minutes(sign_positive: bool, hours: int, minutes: int) -> UtcOffset

# Methods
offset.as_seconds() -> int        # Total offset in seconds
offset.is_utc() -> bool           # True if offset is zero

OffsetDateTime

A datetime with a fixed UTC offset (RFC 3339 style).

# Class methods
OffsetDateTime.from_utc(utc: DateTime, offset: UtcOffset) -> OffsetDateTime
OffsetDateTime.from_local(date: Date, time: Time, offset: UtcOffset) -> OffsetDateTime
OffsetDateTime.parse(s: str) -> OffsetDateTime  # Parse RFC 3339

# Properties
odt.utc: DateTime                 # UTC datetime
odt.offset: UtcOffset             # Offset from UTC

# Methods
odt.to_local() -> DateTime                          # Convert to local datetime
odt.unix_timestamp() -> int                         # Seconds since Unix epoch
odt.unix_timestamp_nanos() -> int                   # Nanoseconds since Unix epoch
odt.add_duration(dur: Duration) -> OffsetDateTime   # Add a duration
odt.difference(other: OffsetDateTime) -> Duration   # Calculate difference

Weekday

An enumeration of weekdays (ISO order, Monday = 1).

# Constants
Weekday.MONDAY
Weekday.TUESDAY
Weekday.WEDNESDAY
Weekday.THURSDAY
Weekday.FRIDAY
Weekday.SATURDAY
Weekday.SUNDAY

# Methods
weekday.number_from_monday() -> int  # 1-7

Examples

See the python/examples/ directory for more examples:

Comparison with Other Libraries

Feature fasttime datetime (stdlib) pendulum arrow
Speed ⚡ Very Fast (Rust) Fast (C) Moderate Moderate
Nanosecond precision ✅ ❌ (microsecond) ❌ (microsecond) ❌ (microsecond)
UTC-focused ✅ Partial ❌ ❌
RFC 3339 parsing ✅ ✅ ✅ ✅
Timezone database ❌ (fixed offset only) ❌ ✅ ✅
Dependencies None (after build) None Many Many
Memory footprint Small Small Large Large

When to use fasttime:

  • You need UTC timestamps and simple date/time operations
  • You want nanosecond precision
  • You prefer a minimal, fast library without heavy dependencies
  • You're working with logs, metrics, or APIs using ISO 8601/RFC 3339

When to use other libraries:

  • You need full timezone database support (use pendulum or zoneinfo)
  • You need complex calendar/business day calculations
  • You're already using the standard library and don't need extra precision

Performance

fasttime is built with Rust and uses highly optimized algorithms for date/time conversions:

  • Days ↔ Date conversion: Uses Ben Joffe's constant-time algorithm (no division/modulo)
  • Unix timestamp conversion: O(1) operations
  • Parsing: Minimal allocations, direct byte processing

Typical operations complete in microseconds, making it suitable for high-throughput applications.

Development

Building from Source

# Install maturin
pip install maturin

# Build the package
maturin develop

# Run tests
pytest python/tests/

Running Examples

python python/examples/basic_usage.py

License

Licensed under the Apache License, Version 2.0. See LICENSE for details.

Contributing

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