Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions test/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,32 @@ def test_timescale():
assert (ATLAS.asec2rad == np.pi/648000.0)
assert (ATLAS.masec2rad == np.pi/0.648e12)

def test_string_formatting():
"""Test that the string representations match expected outputs
"""
# J2000 epoch
ts = timescale.from_calendar(2000, 1, 1, 12, 0, 0)
# default units case is seconds
exp = f'2000-01-01T12:00:00'
assert ts.to_string().item() == exp
# check all numpy datetime compatible units
exp = {}
exp['D'] = f'2000-01-01'
exp['h'] = f'2000-01-01T12'
exp['m'] = f'2000-01-01T12:00'
exp['s'] = f'2000-01-01T12:00:00'
exp['ms'] = f'2000-01-01T12:00:00.000'
exp['us'] = f'2000-01-01T12:00:00.000000'
for key, val in exp.items():
assert ts.to_string(unit=key).item() == val
# check strftime formatting
exp = f'2000.01.01'
assert ts.strftime(r'%Y.%m.%d').item() == exp
exp = f'2000-01-01T12:00:00.000000'
assert ts.strftime(r'%Y-%m-%dT%H:%M:%S.%f').item() == exp
exp = f'Sat Jan 01 2000'
assert ts.strftime(r'%a %b %d %Y').item() == exp

def test_earth_rotation_angle():
"""Test that the Earth rotation angle (ERA) matches expected outputs
"""
Expand Down
40 changes: 34 additions & 6 deletions timescale/time.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
"""
time.py
Written by Tyler Sutterley (07/2026)
Written by Tyler Sutterley (08/2026)
Utilities for calculating time operations

PYTHON DEPENDENCIES:
Expand All @@ -16,8 +16,11 @@
utilities.py: download and management utilities for syncing files

UPDATE HISTORY:
Updated 08/2026: add microsecond as option to from_calendar
Updated 07/2026: add HTML representations of Timescale and Calendar
added attributes for tai_utc and loran_utc
added timezone option to to_string to allow local time outputs
added strftime function to output customized datetime strings
Updated 05/2026: added functions to update delta time files from project
updated CDDIS ftp login options for encrypted connections
Updated 04/2026: added endpoint option (defaults to True) to date_range
Expand Down Expand Up @@ -117,6 +120,7 @@
"minute": 60.0,
"min": 60.0,
"mins": 60.0,
"m": 60.0,
"hours": 3600.0,
"hour": 3600.0,
"hr": 3600.0,
Expand Down Expand Up @@ -892,6 +896,7 @@ def from_calendar(
hour: np.ndarray | float = 0.0,
minute: np.ndarray | float = 0.0,
second: np.ndarray | float = 0.0,
microsecond: np.ndarray | float = 0.0,
):
"""
Converts calendar date arrays into a ``Timescale`` object
Expand All @@ -910,6 +915,8 @@ def from_calendar(
minute of the hour
second: np.ndarray or float, default 0.0
second of the minute
microsecond: np.ndarray or float, default 0.0
microsecond of the second
"""
# verify input data types
year = np.array(year, dtype=np.float64)
Expand All @@ -918,6 +925,7 @@ def from_calendar(
hour = np.array(hour, dtype=np.float64)
minute = np.array(minute, dtype=np.float64)
second = np.array(second, dtype=np.float64)
microsecond = np.array(microsecond, dtype=np.float64)
# calculate date in Modified Julian Days (MJD) from calendar date
# MJD: days since November 17, 1858 (1858-11-17T00:00:00)
MJD = (
Expand All @@ -931,6 +939,7 @@ def from_calendar(
+ hour / 24.0
+ minute / 1440.0
+ second / 86400.0
+ microsecond / 86400e6
+ 1721028.5
- _jd_mjd
)
Expand Down Expand Up @@ -1001,7 +1010,7 @@ def to_deltatime(
# return the date in time (default days) since epoch
return scale * np.array(self.MJD - delta_time_epochs, dtype=np.float64)

def to_datetime(self, unit="ns"):
def to_datetime(self, unit="ns", **kwargs):
"""
Convert a ``Timescale`` object to a ``datetime`` array

Expand All @@ -1020,20 +1029,39 @@ def to_datetime(self, unit="ns"):
# return the datetime array
return np.array(epoch + delta_time.astype(f"timedelta64[{unit}]"))

def to_string(self, unit: str = "s", **kwargs):
def to_string(self, unit: str = "s", timezone="naive", **kwargs):
"""
Convert a ``Timescale`` object to a formatted string array

Parameters
----------
unit: str, default 's'
datetime unit for output string array
timezone: str, default 'naive'
timezone for output string array
**kwargs: dict
keyword arguments for datetime formatting
"""
return np.datetime_as_string(
self.to_datetime(unit=unit), unit=unit, **kwargs
)
# convert to datetime objects
dtime = self.to_datetime(unit=unit, **kwargs)
return np.datetime_as_string(dtime, unit=unit, timezone=timezone)

def strftime(self, format: str, **kwargs):
"""
Convert a ``Timescale`` object to a custom formatted string array

Parameters
----------
format: str
formatting string for output string array
**kwargs: dict
keyword arguments for datetime formatting
"""
# set default keyword arguments
kwargs.setdefault("unit", "us")
# convert to datetime objects
dtime = self.to_datetime(**kwargs).astype(datetime.datetime)
return np.array([d.strftime(format) for d in dtime])
Comment on lines +1032 to +1064

# PURPOSE: calculate the sum of a polynomial function of time
def polynomial_sum(self, coefficients: list | np.ndarray, t: np.ndarray):
Expand Down
Loading