A Python CDF reader toolkit for reading and writing CDF files without requiring NASA CDF library installation
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive time handling system supporting all three CDF epoch formats with conversion between CDF epochs, Unix timestamps, numpy datetime64, and human-readable strings. The CDFepoch class provides static methods for working with time data in scientific applications.
CDF supports three time formats, each optimized for different precision and range requirements:
Automatic format detection and conversion operations that work with any CDF epoch type.
class CDFepoch:
@staticmethod
def encode(epochs, iso_8601=True):
"""
Encode CDF epochs to human-readable strings.
Parameters:
- epochs (int | float | complex | array-like): CDF epoch values of any type
- iso_8601 (bool): Use ISO 8601 format if True, CDF format if False
Returns:
str | list[str]: Encoded time string(s)
"""
@staticmethod
def breakdown(epochs):
"""
Break down CDF epochs into date/time components.
Parameters:
- epochs (int | float | complex | array-like): CDF epoch values of any type
Returns:
numpy.ndarray: Array with shape (..., 9) containing:
[year, month, day, hour, minute, second, millisecond,
microsecond, nanosecond] for each epoch
"""
@staticmethod
def to_datetime(cdf_time):
"""
Convert CDF time to numpy datetime64 array.
Parameters:
- cdf_time (int | float | complex | array-like): CDF epoch values
Returns:
numpy.ndarray[datetime64]: NumPy datetime64 array
"""
@staticmethod
def unixtime(cdf_time):
"""
Convert CDF time to Unix timestamp (seconds since 1970-01-01).
Parameters:
- cdf_time (int | float | complex | array-like): CDF epoch values
Returns:
float | numpy.ndarray: Unix timestamp(s)
"""
@staticmethod
def compute(datetimes):
"""
Auto-detect appropriate CDF epoch type and compute from date components.
Parameters:
- datetimes (array-like): Date/time components as nested lists/arrays
[[year, month, day, hour, min, sec, ms], ...]
Returns:
int | float | complex | numpy.ndarray: Appropriate CDF epoch type
"""Usage Examples:
import cdflib
# Auto-detect and encode different epoch types
epoch_float = 63731251200000.0 # CDF_EPOCH
epoch_tt2000 = 725846462184000000 # TT2000
epoch_16 = complex(63731251200, 0) # CDF_EPOCH16
# Encode to human-readable strings
print(cdflib.cdfepoch.encode(epoch_float)) # "01-Jan-2021 00:00:00.000"
print(cdflib.cdfepoch.encode(epoch_tt2000)) # "01-Jan-2023 00:01:02.184000000"
print(cdflib.cdfepoch.encode(epoch_16)) # "01-Jan-2021 00:00:00.000000000000"
# Break down to components
components = cdflib.cdfepoch.breakdown([epoch_float, epoch_tt2000])
print(components) # [[2021, 1, 1, 0, 0, 0, 0, 0, 0], [2023, 1, 1, 0, 1, 2, 184, 0, 0]]
# Convert to numpy datetime64
dt_array = cdflib.cdfepoch.to_datetime([epoch_float, epoch_tt2000])
print(dt_array) # ['2021-01-01T00:00:00.000' '2023-01-01T00:01:02.184']
# Convert to Unix timestamp
unix_times = cdflib.cdfepoch.unixtime([epoch_float, epoch_tt2000])
print(unix_times) # [1609459200.0, 1672531262.184]Convert Unix timestamps to different CDF epoch formats.
@staticmethod
def timestamp_to_cdfepoch(unixtime_data):
"""
Convert Unix timestamp to CDF_EPOCH format.
Parameters:
- unixtime_data (float | array-like): Unix timestamp(s) in seconds
Returns:
numpy.ndarray: CDF_EPOCH values (milliseconds since Year 0)
"""
@staticmethod
def timestamp_to_cdfepoch16(unixtime_data):
"""
Convert Unix timestamp to CDF_EPOCH16 format.
Parameters:
- unixtime_data (float | array-like): Unix timestamp(s) in seconds
Returns:
numpy.ndarray: CDF_EPOCH16 values (picoseconds since Year 0)
"""
@staticmethod
def timestamp_to_tt2000(unixtime_data):
"""
Convert Unix timestamp to TT2000 format.
Parameters:
- unixtime_data (float | array-like): Unix timestamp(s) in seconds
Returns:
numpy.ndarray: TT2000 values (nanoseconds since J2000)
"""Usage Examples:
import cdflib
import time
# Current Unix timestamp
current_time = time.time() # e.g., 1672531262.184
# Convert to different CDF epoch formats
cdf_epoch = cdflib.cdfepoch.timestamp_to_cdfepoch(current_time)
cdf_epoch16 = cdflib.cdfepoch.timestamp_to_cdfepoch16(current_time)
tt2000 = cdflib.cdfepoch.timestamp_to_tt2000(current_time)
print(f"CDF_EPOCH: {cdf_epoch[0]}") # Milliseconds since Year 0
print(f"CDF_EPOCH16: {cdf_epoch16[0]}") # Complex number (picoseconds)
print(f"TT2000: {tt2000[0]}") # Nanoseconds since J2000
# Convert array of timestamps
timestamps = [1672531200.0, 1672531262.184, 1672531300.0]
epochs = cdflib.cdfepoch.timestamp_to_cdfepoch(timestamps)
print(f"Converted epochs: {epochs}")Operations specific to CDF_EPOCH format (milliseconds since Year 0).
@staticmethod
def encode_epoch(epochs, iso_8601=True):
"""
Encode CDF_EPOCH values to strings.
Parameters:
- epochs (float | array-like): CDF_EPOCH values
- iso_8601 (bool): Use ISO 8601 format if True
Returns:
str | list[str]: Encoded time string(s)
"""
@staticmethod
def compute_epoch(dates):
"""
Compute CDF_EPOCH from date/time components.
Parameters:
- dates (array-like): Date components as [year, month, day, hour, min, sec, ms]
or nested array for multiple dates
Returns:
float | numpy.ndarray: CDF_EPOCH value(s)
"""
@staticmethod
def breakdown_epoch(epochs):
"""
Break down CDF_EPOCH to date/time components.
Parameters:
- epochs (float | array-like): CDF_EPOCH values
Returns:
numpy.ndarray: Components [year, month, day, hour, min, sec, ms]
"""
@staticmethod
def epochrange_epoch(start_time, end_time, epochs):
"""
Find CDF_EPOCH values within a time range.
Parameters:
- start_time (float): Start CDF_EPOCH value
- end_time (float): End CDF_EPOCH value
- epochs (array-like): CDF_EPOCH array to search
Returns:
tuple: (indices, values) of epochs within range
"""Usage Examples:
import cdflib
# Compute CDF_EPOCH from date components
date_components = [2023, 6, 15, 14, 30, 45, 123]
epoch = cdflib.cdfepoch.compute_epoch(date_components)
print(f"CDF_EPOCH: {epoch}") # Single epoch value
# Multiple dates
dates = [
[2023, 1, 1, 0, 0, 0, 0],
[2023, 6, 15, 12, 0, 0, 0],
[2023, 12, 31, 23, 59, 59, 999]
]
epochs = cdflib.cdfepoch.compute_epoch(dates)
print(f"Multiple epochs: {epochs}")
# Encode to strings
time_strings = cdflib.cdfepoch.encode_epoch(epochs)
print(f"Time strings: {time_strings}")
# ISO 8601 format
iso_strings = cdflib.cdfepoch.encode_epoch(epochs, iso_8601=True)
print(f"ISO format: {iso_strings}")
# Break down to components
components = cdflib.cdfepoch.breakdown_epoch(epochs)
print(f"Components shape: {components.shape}") # (3, 7) for 3 epochs
# Find epochs within range
start_epoch = cdflib.cdfepoch.compute_epoch([2023, 3, 1, 0, 0, 0, 0])
end_epoch = cdflib.cdfepoch.compute_epoch([2023, 9, 1, 0, 0, 0, 0])
indices, values = cdflib.cdfepoch.epochrange_epoch(start_epoch, end_epoch, epochs)
print(f"Epochs in range: {values}")Operations for CDF_EPOCH16 format (picoseconds since Year 0, highest precision).
@staticmethod
def encode_epoch16(epochs, iso_8601=True):
"""
Encode CDF_EPOCH16 values to strings.
Parameters:
- epochs (complex | array-like): CDF_EPOCH16 values
- iso_8601 (bool): Use ISO 8601 format if True
Returns:
str | list[str]: Encoded time string(s) with picosecond precision
"""
@staticmethod
def compute_epoch16(datetimes):
"""
Compute CDF_EPOCH16 from date/time components.
Parameters:
- datetimes (array-like): Components [year, month, day, hour, min, sec, ms, us, ns, ps]
or nested array for multiple dates
Returns:
complex | numpy.ndarray: CDF_EPOCH16 value(s)
"""
@staticmethod
def breakdown_epoch16(epochs):
"""
Break down CDF_EPOCH16 to date/time components.
Parameters:
- epochs (complex | array-like): CDF_EPOCH16 values
Returns:
numpy.ndarray: Components [year, month, day, hour, min, sec, ms, us, ns, ps]
"""
@staticmethod
def epochrange_epoch16(start_time, end_time, epochs):
"""
Find CDF_EPOCH16 values within a time range.
Parameters:
- start_time (complex): Start CDF_EPOCH16 value
- end_time (complex): End CDF_EPOCH16 value
- epochs (array-like): CDF_EPOCH16 array to search
Returns:
tuple: (indices, values) of epochs within range
"""Operations for TT2000 format (nanoseconds since J2000 with leap seconds).
@staticmethod
def encode_tt2000(tt2000, iso_8601=True):
"""
Encode TT2000 values to strings.
Parameters:
- tt2000 (int | array-like): TT2000 values
- iso_8601 (bool): Use ISO 8601 format if True
Returns:
str | list[str]: Encoded time string(s) with nanosecond precision
"""
@staticmethod
def compute_tt2000(datetimes):
"""
Compute TT2000 from date/time components.
Parameters:
- datetimes (array-like): Components [year, month, day, hour, min, sec, ms, us, ns]
or nested array for multiple dates
Returns:
int | numpy.ndarray: TT2000 value(s)
"""
@staticmethod
def breakdown_tt2000(tt2000):
"""
Break down TT2000 to date/time components.
Parameters:
- tt2000 (int | array-like): TT2000 values
Returns:
numpy.ndarray: Components [year, month, day, hour, min, sec, ms, us, ns]
"""
@staticmethod
def epochrange_tt2000(start_time, end_time, tt2000):
"""
Find TT2000 values within a time range.
Parameters:
- start_time (int): Start TT2000 value
- end_time (int): End TT2000 value
- tt2000 (array-like): TT2000 array to search
Returns:
tuple: (indices, values) of TT2000 values within range
"""Usage Examples:
import cdflib
# TT2000 with nanosecond precision
date_with_ns = [2023, 6, 15, 14, 30, 45, 123, 456, 789]
tt2000_value = cdflib.cdfepoch.compute_tt2000(date_with_ns)
print(f"TT2000: {tt2000_value}")
# Encode with nanosecond precision
tt2000_string = cdflib.cdfepoch.encode_tt2000(tt2000_value)
print(f"TT2000 string: {tt2000_string}")
# CDF_EPOCH16 with picosecond precision
date_with_ps = [2023, 6, 15, 14, 30, 45, 123, 456, 789, 123]
epoch16_value = cdflib.cdfepoch.compute_epoch16(date_with_ps)
print(f"CDF_EPOCH16: {epoch16_value}")
# Break down TT2000
tt2000_components = cdflib.cdfepoch.breakdown_tt2000(tt2000_value)
print(f"TT2000 components: {tt2000_components}") # Includes nanosecond componentFind epochs within specified time ranges for data filtering.
@staticmethod
def findepochrange(start_time, end_time, epochs):
"""
Generic function to find epoch range regardless of format.
Parameters:
- start_time: Start time in same format as epochs
- end_time: End time in same format as epochs
- epochs (array-like): Epoch array to search
Returns:
tuple: (start_index, end_index) of range boundaries
"""Usage Example:
import cdflib
# Create sample epoch array
dates = [[2023, i, 1, 0, 0, 0, 0] for i in range(1, 13)] # Monthly data
epochs = cdflib.cdfepoch.compute_epoch(dates)
# Find summer months (June to August)
start_summer = cdflib.cdfepoch.compute_epoch([2023, 6, 1, 0, 0, 0, 0])
end_summer = cdflib.cdfepoch.compute_epoch([2023, 8, 31, 23, 59, 59, 999])
start_idx, end_idx = cdflib.cdfepoch.findepochrange(start_summer, end_summer, epochs)
summer_epochs = epochs[start_idx:end_idx+1]
summer_strings = cdflib.cdfepoch.encode_epoch(summer_epochs)
print(f"Summer months: {summer_strings}")Parse time strings back to date components.
@staticmethod
def parse(value):
"""
Parse time strings into date/time components.
Parameters:
- value (str | tuple[str] | list[str]): Time string(s) to parse
Returns:
numpy.ndarray: Date/time components array
"""Usage Example:
import cdflib
# Parse various time string formats
time_strings = [
"2023-06-15T14:30:45.123",
"15-Jun-2023 14:30:45.123",
"2023-12-31T23:59:59.999"
]
parsed_dates = cdflib.cdfepoch.parse(time_strings)
print(f"Parsed components: {parsed_dates}")
# Convert parsed dates to epochs
epochs = cdflib.cdfepoch.compute_epoch(parsed_dates)
print(f"Computed epochs: {epochs}")import cdflib
import numpy as np
# Create hourly measurements for one day
base_date = [2023, 6, 15, 0, 0, 0, 0]
hourly_dates = [[2023, 6, 15, hour, 0, 0, 0] for hour in range(24)]
# Convert to different epoch formats for comparison
cdf_epochs = cdflib.cdfepoch.compute_epoch(hourly_dates)
tt2000_epochs = cdflib.cdfepoch.compute_tt2000(hourly_dates)
# Create synthetic temperature data
temperatures = 20 + 10 * np.sin(np.linspace(0, 2*np.pi, 24)) + np.random.normal(0, 1, 24)
# Find daytime hours (6 AM to 6 PM)
morning = cdflib.cdfepoch.compute_epoch([2023, 6, 15, 6, 0, 0, 0])
evening = cdflib.cdfepoch.compute_epoch([2023, 6, 15, 18, 0, 0, 0])
start_idx, end_idx = cdflib.cdfepoch.findepochrange(morning, evening, cdf_epochs)
daytime_temps = temperatures[start_idx:end_idx+1]
daytime_epochs = cdf_epochs[start_idx:end_idx+1]
print(f"Daytime temperature range: {np.min(daytime_temps):.1f} to {np.max(daytime_temps):.1f}°C")
print(f"Daytime period: {cdflib.cdfepoch.encode_epoch(daytime_epochs[0])} to {cdflib.cdfepoch.encode_epoch(daytime_epochs[-1])}")import cdflib
import datetime
# Convert between different time representations
python_datetime = datetime.datetime(2023, 6, 15, 14, 30, 45, 123456)
# Convert to Unix timestamp
unix_time = python_datetime.timestamp()
# Convert Unix time to CDF formats
cdf_epoch = cdflib.cdfepoch.timestamp_to_cdfepoch(unix_time)[0]
tt2000 = cdflib.cdfepoch.timestamp_to_tt2000(unix_time)[0]
# Convert back to verify
unix_from_cdf = cdflib.cdfepoch.unixtime(cdf_epoch)
unix_from_tt2000 = cdflib.cdfepoch.unixtime(tt2000)
print(f"Original Unix time: {unix_time}")
print(f"From CDF_EPOCH: {unix_from_cdf}")
print(f"From TT2000: {unix_from_tt2000}")
print(f"Differences (microseconds): CDF={abs(unix_time-unix_from_cdf)*1e6:.1f}, TT2000={abs(unix_time-unix_from_tt2000)*1e6:.1f}")
# Display in human-readable format
print(f"CDF_EPOCH string: {cdflib.cdfepoch.encode_epoch(cdf_epoch)}")
print(f"TT2000 string: {cdflib.cdfepoch.encode_tt2000(tt2000)}")# Type aliases used in the epochs module
epoch_scalar_types = Union[int, np.int64, float, np.float64, complex, np.complex128]
epoch_list_types = Union[List[int], List[np.int64], List[float], List[np.float64],
List[complex], List[np.complex128]]
epoch_types = Union[epoch_scalar_types, epoch_list_types, numpy.ndarray]
cdf_epoch_type = Union[float, np.float64, List[float], numpy.ndarray]
cdf_epoch16_type = Union[complex, np.complex128, List[complex], numpy.ndarray]
cdf_tt2000_type = Union[int, np.int64, List[int], numpy.ndarray]
encoded_type = Union[str, List[str]]Install with Tessl CLI
npx tessl i tessl/pypi-cdflib