NsEpoch (Nanosecond Epoch Time)
Nanosecond-accurate time representation with embedded epochs, arithmetic operations, and flexible string parsing.
Overview
gri-nsepoch provides three classes -- Time, Delta, and Epoch -- for working with time at nanosecond precision. Unlike datetime (microsecond resolution) or floating-point seconds (which resolve only ~240 ns at current dates), gri-nsepoch stores all durations as integer nanoseconds internally, eliminating floating-point accumulation errors.
Times are always relative to an Epoch (defaulting to Unix epoch, 1970-01-01). Subtracting two Time objects produces a Delta. Adding a Delta to a Time produces a new Time. The library interoperates with datetime, timedelta, and raw numeric types.
The package also installs ssep, a command-line tool for converting between time formats.
Requires Python 3.12+.
Mathematical Background
Standard IEEE 754 double-precision floats provide ~15-17 significant digits. A Unix timestamp for the year 2025 is approximately 1.74 x 10^9 seconds. Representing this with nanosecond precision requires 18+ digits -- exceeding float64 capacity. By storing nanoseconds as arbitrary-precision Python integers, gri-nsepoch avoids this limitation entirely:
internal storage: int nanoseconds (no precision loss)
float seconds: ~240 ns resolution at current dates
datetime: limited to microsecond resolution
Documentation
The wheel ships its documentation inside the package, in gri_nsepoch/docs/,
so it is available wherever the package is installed:
overview.md-- concepts, construction, arithmetic, and gotchastime_formats.md-- every stringTime.from_strparses, and every formatted outputapi_summary.md-- every public class, method, and signature (generated)
Print the directory with
python -c "import gri_nsepoch, pathlib; print(pathlib.Path(gri_nsepoch.__file__).parent / 'docs')".
Every example in those files is run by the test suite.
Installation
pip install gri-nsepoch
For development:
git clone https://gitlab.com/geosol-foss/python/gri-nsepoch.git
cd gri-nsepoch
uv sync
Quick Start
from gri_nsepoch import Time, Delta, Epoch
# Current time
now = Time()
print(now.iso) # 2025-06-15T14:30:22.123+00:00
# From a string
t = Time.from_str("2025-01-15 08:30:00")
print(t.date_pp()) # 2025-01-15
print(t.time_pp(d=3)) # 08:30:00.000
# Arithmetic
t2 = t + Delta(s=3600) # Add one hour
delta = t2 - t
print(delta.hms_pp()) # 01:00:00
# Nanosecond precision
t3 = Time(ns=1_000_000_123)
print(t3.time_pp(d=9)) # 00:00:01.000000123
Time Class
Time represents an absolute point in time as nanoseconds since an epoch.
Constructors:
t = Time() # Current time (UTC)
t = Time(ns=0) # Unix epoch exactly
t = Time(s=1e9) # From seconds (some ns precision loss)
t = Time(ns=0, epoch=86400) # Midnight Jan 2, 1970 as epoch
t = Time.from_str("2025-01-15") # Parse from string
t = Time.from_str("2025-01-15 08:30:00", pytz_tz="America/Denver")
Properties and formatting:
t = Time.from_str("2025-06-15T14:30:22.123")
t.ns # Integer nanoseconds since epoch (no precision loss)
t.secs # Float seconds since epoch (some precision loss)
t.dt # datetime object (microsecond resolution)
t.epoch # The Epoch object
t.jd # Julian Date as (jd, fractional_day) tuple
t.iso # "2025-06-15T14:30:22.123+00:00"
t.date_pp() # "2025-06-15"
t.time_pp(d=6) # "14:30:22.123000"
t.dt_pp(d=3) # "2025-06-15 14:30:22.123"
t.ssep_pp(s=True) # "1,749,997,822" (with thousands separator)
t.mjd_pp(d=3) # "60841-14:30:22.123"
Arithmetic:
Time + Delta->TimeTime - Time->DeltaTime - Delta->TimeTime + Time-> TypeError
Timer usage:
start = Time()
# ... do work ...
elapsed = start.delta_now()
print(elapsed.hms_pp(d=3)) # "00:00:01.234"
Delta Class
Delta represents a time duration with nanosecond precision.
d = Delta(ns=5_500_000_000) # 5.5 seconds
d = Delta(s=5.5) # Same, with possible ns precision loss
d.ns # 5500000000
d.secs # 5.5
d.timedelta # datetime.timedelta(seconds=5, microseconds=500000)
d.hms_pp(d=3) # "00:00:05.500"
d.secs_pp(s=True) # "5"
# Component access
d.delta_ints.SS # 5 (seconds component)
d.delta_ints.MS # 500 (milliseconds component)
Epoch Class
Epoch is a named reference point in time, stored as seconds since Unix epoch (1970-01-01). It subclasses int, so it works directly in arithmetic.
unix = Epoch() # Unix epoch (default)
gps = Epoch(315964800, "GPS") # GPS epoch (Jan 6, 1980)
custom = Epoch(946684800, "Y2K") # Year 2000
print(gps) # "GPS"
print(int(gps)) # 315964800
# Use with Time
t = Time(ns=0, epoch=gps) # GPS epoch start
String Parsing
Time.from_str() recognizes many common formats without requiring a format string:
- ISO 8601:
"2025-01-15T08:30:00","2025-01-15T08:30:00Z","2025-01-15T08:30:00+05:00" - UTC offsets on any date-time:
Z,z,UTC,GMT,+HH,+HHMM,+HH:MM(optionally after one space). Named abbreviations likeESTare ambiguous and not accepted; usepytz_tzfor a named zone. If both an offset andpytz_tzare given and they disagree at that instant,ValueErroris raised. - Date-time:
"2025-01-15 08:30:00","2025/01/15 08:30:00" - Date only:
"2025-01-15","2025/01/15" - Epoch seconds:
"1750000000","1750000000.123456789" - Modified Julian Day as days-seconds:
"60841-52222.5"
For non-standard formats, pass a strftime pattern:
t = Time.from_str("15-Jun-2025 14:30", "%d-%b-%Y %H:%M")
CLI Tool (ssep)
The ssep command converts any recognized time format to ISO and seconds-since-epoch:
$ ssep "2025-01-15 08:30:00"
ISO: 2025-01-15T08:30:00+00:00
Epoch: 1970-01-01
SSEP: 1736929800
$ ssep "2025-01-15 08:30:00" -e "2025-01-01"
ISO: 2025-01-15T08:30:00+00:00
Epoch: 2025-01-01
SSEP: 1240200
$ ssep # No argument prints current time
ISO: 2025-06-15T14:30:22+00:00
Epoch: 1970-01-01
SSEP: 1750000222
Dependencies
- gri-memoize: Per-instance caching of computed properties
- pytz: Timezone-aware string parsing
Other Projects
Current list of other GRI FOSS Projects we are building and maintaining.
License
MIT License. See LICENSE for details.
Release files for gri-nsepoch 0.2.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gri_nsepoch-0.2.5.tar.gz | 42.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gri_nsepoch-0.2.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 71.5 kB
Release files / gri_nsepoch-0.2.5.tar.gz
| Download URL | gri_nsepoch-0.2.5.tar.gz |
|---|---|
| Size | 42.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1bb8c0b0473e1564ef9bb2c80fb23c563c2c059f612bfba31977a6abc952e715
|
|
BLAKE2b-256 checksum How to use checksums |
31200012428a10b0319675b260769d8cc4a469430bda724238999844bc4740a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / gri_nsepoch-0.2.5-py3-none-any.whl
| Download URL | gri_nsepoch-0.2.5-py3-none-any.whl |
|---|---|
| Size | 29.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cadea68880c4b5d4ab530e6fb89e816e4bf38335dd50ff5dce3034671e8ee9b8
|
|
BLAKE2b-256 checksum How to use checksums |
b796a9e080af594d45786a382d24535a9087d6c506d289838245679148d60f69
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|