Skip to main content

Elegant epoch timestamp handling with timezone-aware operations, fluent API, and smart defaults for Python developers who hate datetime complexity.

Project description

Bear Epoch Time

A lightweight Python library for working with epoch timestamps. Bear Epoch Time provides timezone-aware conversion utilities and a fluent API for manipulating times without wrestling with datetime quirks.

Key Features:

  • EpochTimestamp inherits from int - use it anywhere you'd use an integer
  • Milliseconds by default (configurable to seconds)
  • UTC-first with explicit timezone conversions
  • Fluent, chainable API
  • Full comparison support with datetime, date, time, int, and float

Requirements: Python 3.11+

Installation

Install from PyPI:

pip install bear-epoch-time
# or with uv
uv pip install bear-epoch-time

Quick Start

from bear_epoch_time import EpochTimestamp

# Current UTC timestamp in milliseconds
now = EpochTimestamp.now()
print(now)              # 1734567890123
print(now.to_seconds)   # 1734567890
print(now.date_str())   # "12-18-2025"

Creating Timestamps

From Various Sources

from datetime import datetime, date
from bear_epoch_time import EpochTimestamp

# Current time
now = EpochTimestamp.now()
now_in_seconds = EpochTimestamp.now(milliseconds=False)

# From datetime
dt = datetime(2025, 12, 25, 10, 30, 0)
ts = EpochTimestamp.from_datetime(dt)

# From date (creates timestamp at midnight)
d = date(2025, 12, 25)
ts = EpochTimestamp.from_date(d)

# From seconds
ts = EpochTimestamp.from_seconds(1734567890)

# From ISO string
ts = EpochTimestamp.from_iso_string("2025-12-25T10:30:00+00:00")

# From custom format string
ts = EpochTimestamp.from_dt_string("12-25-2025 10:30 AM", fmt="%m-%d-%Y %I:%M %p")

Time Arithmetic

Adding and Subtracting Time

from datetime import timedelta
from bear_epoch_time import EpochTimestamp

now = EpochTimestamp.now()

# Add time components
future = now.add(days=7, hours=3, minutes=30)

# Subtract time
past = now.subtract(days=1, hours=12)

# Use timedelta
delta = timedelta(weeks=2)
two_weeks_later = now.add(delta=delta)

# Use negative values for subtraction via add()
yesterday = now.add(days=-1)

Calculating Differences

ts1 = EpochTimestamp.now()
ts2 = ts1.add(days=5, hours=3)

# Get difference as int (milliseconds by default)
diff_ms = ts1.diff(ts2)

# Get difference in seconds
diff_s = ts1.diff(ts2, milliseconds=False)

# Get difference as timedelta
delta = ts1.diff(ts2, as_timedelta=True)

# Get difference in specific units
days = ts1.time_since(ts2, unit="d")      # days
hours = ts1.time_since(ts2, unit="h")     # hours
minutes = ts1.time_since(ts2, unit="m")   # minutes

Day Boundaries

import pytz
from bear_epoch_time import EpochTimestamp

now = EpochTimestamp.now()

# Get start/end of day (UTC by default)
start = now.start_of_day()
end = now.end_of_day()

# With specific timezone
pacific = pytz.timezone("America/Los_Angeles")
start_pacific = now.start_of_day(tz=pacific)
end_pacific = now.end_of_day(tz=pacific)

String Formatting

Output Formats

from bear_epoch_time import EpochTimestamp

ts = EpochTimestamp.now()

# Default formatted strings
ts.to_string()     # "12-18-2025 03:30 PM PST"
ts.date_str()      # "12-18-2025"
ts.time_str()      # "03:30 PM"

# Custom format
ts.to_string(fmt="%A, %B %d, %Y")  # "Thursday, December 18, 2025"

# Ordinal day format (special %Do code)
ts.to_string(fmt="%B %Do, %Y")     # "December 18th, 2025"

# ISO format
ts.to_iso                           # "2025-12-18T23:30:00+00:00"
ts.get_iso_string(sep=" ")          # "2025-12-18 23:30:00+00:00"

# Template formatting with $variables
ts.format("$month_name $day, $year")     # "December 18, 2025"
ts.format("$day_name at $time")          # "Thursday at 03:30 PM"

Available Template Variables

$epoch, $seconds, $milliseconds, $iso, $date, $time, $datetime, $year, $month, $day, $hour, $minute, $week, $isoweekday, $day_of_week, $day_of_year, $month_name, $day_name

Accessing Components

from bear_epoch_time import EpochTimestamp

ts = EpochTimestamp.now()

# Date components
ts.year          # 2025
ts.month         # 12
ts.day           # 18
ts.week          # ISO week number (1-53)

# Time components
ts.hour          # 15 (0-23)
ts.minute        # 30 (0-59)
ts.second        # 45 (0-59)
ts.microsecond   # 123456 (0-999999)

# Day information
ts.day_of_week   # 0-6 (Monday=0, Sunday=6)
ts.iso_weekday   # 1-7 (Monday=1, Sunday=7)
ts.day_of_year   # 1-366
ts.day_name      # "Thursday"
ts.month_name    # "December"

# Conversions
ts.to_datetime   # datetime object (UTC)
ts.to_seconds    # int (epoch seconds)
ts.to_milliseconds  # int (epoch milliseconds)
ts.date          # date object
ts.time          # time object
ts.to_iso        # ISO 8601 string
ts.to_duration   # "675M 28d 2h 12m 28s" (human-readable)

Replacing Components

Create a new timestamp with specific components changed:

from bear_epoch_time import EpochTimestamp

ts = EpochTimestamp.now()

# Change specific components
new_year = ts.replace(year=2026)
noon = ts.replace(hour=12, minute=0, second=0)
first_of_month = ts.replace(day=1)

Finding Future Times

from bear_epoch_time import EpochTimestamp

now = EpochTimestamp.now()

# Get next occurrence of a weekday
next_monday = now.next_weekday("Monday")
next_friday = now.next_weekday("friday")  # case-insensitive

# Get next occurrence of a specific time
next_9am = now.next_time_of_day(hour=9)
next_meeting = now.next_time_of_day(hour=14, minute=30)

Comparisons

EpochTimestamp supports comparison with multiple types:

from datetime import datetime, date, time
from bear_epoch_time import EpochTimestamp

ts = EpochTimestamp.now()

# Compare with other EpochTimestamp
ts1 < ts2
ts1 == ts2

# Compare with datetime
ts < datetime.now()

# Compare with date (compares date portion only)
ts == date.today()

# Compare with int/float (raw epoch value)
ts > 1734567890000

# Boolean evaluation (False if zero/placeholder)
if ts:
    print("Valid timestamp")

Class Configuration

Configure defaults for all instances:

import pytz
from bear_epoch_time import EpochTimestamp

# Set default timezone
EpochTimestamp.set_timezone(pytz.timezone("America/New_York"))

# Set default formats
EpochTimestamp.set_date_format("%Y-%m-%d")
EpochTimestamp.set_time_format("%H:%M:%S")
EpochTimestamp.set_full_format("%Y-%m-%d %H:%M:%S %Z")

# Set repr style: "int", "object", or "datetime"
EpochTimestamp.set_repr_style("datetime")

Placeholder Values

Use zero as a placeholder for "no timestamp set":

from bear_epoch_time import EpochTimestamp

placeholder = EpochTimestamp(0)

if placeholder.is_default:
    print("No timestamp set")

# Boolean evaluation also works
if not placeholder:
    print("No timestamp set")

TimeTools Helper

For common timezone-aware operations:

from bear_epoch_time import TimeTools, EpochTimestamp

tools = TimeTools()  # Uses local timezone

# Get start and end of today
start, end = tools.get_day_range()

# Check if timestamps are on the same day
tools.is_same_day(ts1, ts2)

# Check if a date range spans multiple days
tools.is_multi_day("12-18-2025 11:00 PM", "12-19-2025 01:00 AM")

# Quick conversions
tools.dt_to_ts(datetime_obj)
tools.str_to_ts("12-18-2025 03:30 PM")
tools.ts_to_str(timestamp)

TimeConverter Utilities

Parse and format time strings:

from bear_epoch_time import TimeConverter

# Parse duration strings to seconds
TimeConverter.parse_to_seconds("2d 3h 15m")      # 183300.0
TimeConverter.parse_to_seconds("1M 5d")          # ~35 days in seconds

# Format seconds as human-readable
TimeConverter.format_seconds(183300)             # "2d 3h 15m"
TimeConverter.format_seconds(90061.5, show_subseconds=True)  # "1d 1h 1m 1s 500ms"

Timer Utilities

Context managers and decorators for timing:

from bear_epoch_time import timer, create_timer, TimerData

# Context manager
with timer(name="my_operation", console=print) as t:
    # do work
    pass
print(f"Took {t.seconds:.2f}s")

# Decorator factory
@create_timer(console=print)
def my_function():
    pass

# Async support
from bear_epoch_time import async_timer, create_async_timer

async with async_timer(name="async_op") as t:
    await some_async_work()

Limitations

The EpochTimestamp class is designed for UTC timestamps in the recent past and future. It is not intended for:

  • Historical dates before the Unix epoch (1970-01-01)
  • High-precision timing (use time.perf_counter() instead)
  • Dates far in the future where leap second handling becomes significant

This is a utility library for working with epoch timestamps in a more human-friendly way.

License

MIT License - see LICENSE file for details.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

bear_epoch_time-1.2.13-py3-none-any.whl (30.4 kB view details)

Uploaded Python 3

File details

Details for the file bear_epoch_time-1.2.13-py3-none-any.whl.

File metadata

File hashes

Hashes for bear_epoch_time-1.2.13-py3-none-any.whl
Algorithm Hash digest
SHA256 7207eee17cef90ae09c28a87e9920e75c52b1d6d14c0e6f0d0973d4400f0a73e
MD5 8d7342a4fb1e227ed92a84354e29ba6f
BLAKE2b-256 f145aa14bd3fd8942922fcca743b2f43c57160d5d232150e9bd20990a1d488dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for bear_epoch_time-1.2.13-py3-none-any.whl:

Publisher: build-wheels.yml on sicksubroutine/bear-epoch-time

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