Skip to content

Epoch

The Epoch class is the fundamental time representation in Brahe. It encapsulates a specific instant in time, defined by both a time representation and a time scale. The Epoch class provides methods for converting between different time representations and time scales, as well as for performing arithmetic operations on time instances.

There are even more capabilities and features of the Epoch class beyond what is covered in this guide. For a complete reference of all available methods and properties, please refer to the Epoch API Reference.

Initialization

There are all sorts of ways you can initialize an Epoch instance. The most common methods are described below.

Date Time

The most common way to create an Epoch is from date and time components. You can specify just a date (which defaults to midnight), or provide the full date and time including fractional seconds.

import brahe as bh

bh.initialize_eop()

# Create epoch from date only (midnight)
epc1 = bh.Epoch(2024, 1, 1)
print(f"Date only: {epc1}")
# Date only: 2024-01-01 00:00:00.000 UTC

# Create epoch from full datetime components
epc2 = bh.Epoch(2024, 6, 15, 14, 30, 45.5, 0.0)
print(f"Full datetime: {epc2}")
# Full datetime: 2024-06-15 14:30:45.500 UTC

# Create epoch with different time system
epc3 = bh.Epoch(2024, 12, 25, 18, 0, 0.0, 0.0, time_system=bh.TimeSystem.GPS)
print(f"GPS time system: {epc3}")
# GPS time system: 2024-12-25 18:00:00.000 GPS

# In Python you can also use the direct datetime constant
epc4 = bh.Epoch(2024, 12, 25, 18, 0, 0.0, 0.0, time_system=bh.TAI)
print(f"GPS time system: {epc4}")
# GPS time system: 2024-12-25 18:00:00.000 TAI
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create epoch from date only (midnight)
    let epc1 = bh::Epoch::from_date(2024, 1, 1, bh::TimeSystem::UTC);
    println!("Date only: {}", epc1);

    // Create epoch from full datetime components
    let epc2 = bh::Epoch::from_datetime(2024, 6, 15, 14, 30, 45.5, 0.0, bh::TimeSystem::UTC);
    println!("Full datetime: {}", epc2);

    // Create epoch with different time system
    let epc3 = bh::Epoch::from_datetime(2024, 12, 25, 18, 0, 0.0, 0.0, bh::TimeSystem::GPS);
    println!("GPS time system: {}", epc3);
}

MJD

Modified Julian Date (MJD) is a commonly used time representation in astronomy and astrodynamics. MJD is defined as JD - 2400000.5, which makes it more convenient for modern dates.

import brahe as bh

bh.initialize_eop()

# Create epoch from MJD
mjd = 61041.5
epc2 = bh.Epoch.from_mjd(mjd, bh.UTC)
print(f"MJD {mjd}: {epc2}")
# MJD 61041.5: 2026-01-01 12:00:00.000 UTC

# Verify round-trip conversion
mjd_out = epc2.mjd()
print(f"Round-trip MJD: {mjd_out:.6f}")
# Round-trip MJD: 61041.500000
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create epoch from MJD
    let mjd = 61041.5; // 2024-01-01 12:00:00 UTC
    let epc2 = bh::Epoch::from_mjd(mjd, bh::TimeSystem::UTC);
    println!("MJD {}: {}", mjd, epc2);
    // MJD 61041.5: 2026-01-01 12:00:00.000 UTC

    // Verify round-trip conversion
    let mjd_out = epc2.mjd();
    println!("Round-trip MJD: {:.6}", mjd_out);
    // Round-trip MJD: 61041.500000
}

JD

Julian Date (JD) is a continuous count of days since the beginning of the Julian Period. It's widely used in astronomy for precise time calculations.

import brahe as bh

bh.initialize_eop()

# Create epoch from JD
jd = 2460310.5
epc = bh.Epoch.from_jd(jd, bh.UTC)
print(f"JD {jd}: {epc}")
# JD 2460310.5: 2024-01-01 00:00:00.000 UTC

# Verify round-trip conversion
jd_out = epc.jd()
print(f"Round-trip JD: {jd_out:.10f}")
# Round-trip JD: 2460310.5000000000
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create epoch from JD
    let jd = 2460310.5;
    let epc = bh::Epoch::from_jd(jd, bh::TimeSystem::UTC);
    println!("JD {}: {}", jd, epc);
    // JD 2460310.5: 2024-01-01 00:00:00.000 UTC

    // Verify round-trip conversion
    let jd_out = epc.jd();
    println!("Round-trip JD: {:.10}", jd_out);
    // Round-trip JD: 2460310.5000000000
}

String

Epoch instances can be created from ISO 8601 formatted strings or simple date-time strings. The time system can be specified in the string.

import brahe as bh

bh.initialize_eop()

# The string can be an ISO 8601 format
epc1 = bh.Epoch.from_string("2025-01-02T04:56:54.123Z")
print(f"ISO 8601: {epc1}")

# It can be a simple space-separated format with a time system
epc2 = bh.Epoch.from_string("2024-06-15 14:30:45.500 GPS")
print(f"Simple format: {epc2}")

# It can be a datetime without a time system (defaults to UTC)
epc3 = bh.Epoch.from_string("2023-12-31 23:59:59")
print(f"Datetime without time system: {epc3}")

# Or it can just be a date
epc4 = bh.Epoch.from_string("2022-07-04")
print(f"Date only: {epc4}")
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // The string can be an ISO 8601 format
    let epc1 = bh::Epoch::from_string("2025-01-02T04:56:54.123Z").unwrap();
    println!("ISO 8601: {}", epc1);

    // It can be a simple space-separated format with a time system
    let epc2 = bh::Epoch::from_string("2024-06-15 14:30:45.500 GPS").unwrap();
    println!("Simple format: {}", epc2);

    // It can be a datetime without a time system (defaults to UTC)
    let epc3 = bh::Epoch::from_string("2023-12-31 23:59:59").unwrap();
    println!("Datetime without time system: {}", epc3);

    // Or it can just be a date
    let epc4 = bh::Epoch::from_string("2022-07-04").unwrap();
    println!("Date only: {}", epc4);
}

GPS Week and Seconds

For GPS applications, you can create epochs from GPS week number and seconds into the week, or from GPS seconds since the GPS epoch (January 6, 1980).

import brahe as bh

bh.initialize_eop()

# Create epoch from GPS week and seconds
# Week 2390, day 2 (October 28, 2025)
week = 2390
seconds = 2 * 86400.0
epc1 = bh.Epoch.from_gps_date(week, seconds)
print(f"GPS Week {week}, Seconds {seconds}: {epc1}")
# GPS Week 2390, Seconds 172800.0: 2025-10-28 00:00:00.000 GPS

# Verify round-trip conversion
week_out, sec_out = epc1.gps_date()
print(f"Round-trip: Week {week_out}, Seconds {sec_out:.1f}")
# Round-trip: Week 2390, Seconds 172800.0

# Create from GPS seconds since GPS epoch
gps_seconds = week * 7 * 86400.0 + seconds
epc2 = bh.Epoch.from_gps_seconds(gps_seconds)
print(f"GPS Seconds {gps_seconds}: {epc2}")
# GPS Seconds 1445644800.0: 2025-10-28 00:00:00.000 GPS
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create epoch from GPS week and seconds
    // Week 2390, day 2 (October 28, 2025)
    let week = 2390;
    let seconds = 2.0 * 86400.0; // 3 days + 12 hours
    let epc1 = bh::Epoch::from_gps_date(week, seconds);
    println!("GPS Week {}, Seconds {}: {}", week, seconds, epc1);
    // GPS Week 2390, Seconds 172800: 2025-10-28 00:00:00.000 GPS

    // Verify round-trip conversion
    let (week_out, sec_out) = epc1.gps_date();
    println!("Round-trip: Week {}, Seconds {:.1}", week_out, sec_out);
    // Round-trip: Week 2390, Seconds 172800.0

    // Create from GPS seconds since GPS epoch
    let gps_seconds = week as f64 * 7.0 * 86400.0 + seconds;
    let epc2 = bh::Epoch::from_gps_seconds(gps_seconds);
    println!("GPS Seconds {}: {}", gps_seconds, epc2);
    // 1445644800: 2025-10-28 00:00:00.000 GPS
}

Operations

Once you have an epoch class instance you can add and subtract time as you would expect.

Info

When performing arithmetic the other operand is always interpreted as a time duration in seconds.

Addition

You can add a time duration (in seconds) to an Epoch to get a new Epoch at a later time.

import brahe as bh

bh.initialize_eop()

# Create an epoch
epc = bh.Epoch(2025, 1, 1, 12, 0, 0.0, 0.0)
print(f"Original epoch: {epc}")
# Original epoch: 2025-01-01 12:00:00.000 UTC

# You can add time in seconds to an Epoch and get a new Epoch back

# Add 1 hour (3600 seconds)
epc_plus_hour = epc + 3600.0
print(f"Plus 1 hour: {epc_plus_hour}")
# Plus 1 hour: 2025-01-01 13:00:00.000 UTC

# Add 1 day (86400 seconds)
epc_plus_day = epc + 86400.0
print(f"Plus 1 day: {epc_plus_day}")
# Plus 1 day: 2025-01-02 12:00:00.000 UTC

# You can also do in-place addition

# Add 1 second in-place
epc += 1.0
print(f"In-place plus 1 second: {epc}")
# In-place plus 1 second: 2025-01-01 12:00:01.000 UTC

# Add 1 milisecond in-place
epc += 0.001
print(f"In-place plus 1 millisecond: {epc}")
# In-place plus 1 millisecond: 2025-01-01 12:00:01.001 UTC
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create an epoch
    let epc = bh::Epoch::from_datetime(2025, 1, 1, 12, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    println!("Original epoch: {}", epc);
    // Original epoch: 2025-01-01 12:00:00.000 UTC

    // You can add time in seconds to an Epoch and get a new Epoch back

    // Add 1 hour (3600 seconds)
    let epc_plus_hour = epc + 3600.0;
    println!("Plus 1 hour: {}", epc_plus_hour);
    // Plus 1 hour: 2025-01-01 13:00:00.000 UTC

    // Add 1 day (86400 seconds)
    let epc_plus_day = epc + 86400.0;
    println!("Plus 1 day: {}", epc_plus_day);
    // Plus 1 day: 2025-01-02 12:00:00.000 UTC

    // You can also do in-place addition

    // Add 1 second in-place
    let mut epc = epc;
    epc += 1.0;
    println!("In-place plus 1 second: {}", epc);
    // In-place plus 1 second: 2025-01-01 12:00:01.000 UTC

    // Add 1 millisecond in-place
    epc += 0.001;
    println!("In-place plus 1 millisecond: {}", epc);
    // In-place plus 1 millisecond: 2025-01-01 12:00:01.001 UTC
}

Subtraction

Subtracting two Epoch instances returns the time difference between them in seconds.

import brahe as bh

bh.initialize_eop()

# You can subtract two Epoch instances to get the time difference in seconds
epc1 = bh.Epoch(2024, 1, 1, 12, 0, 0.0, 0.0)
epc2 = bh.Epoch(2024, 1, 2, 12, 1, 1.0, 0.0)

dt = epc2 - epc1
print(f"Time difference: {dt:.1f} seconds")


# You can also subtract a float (in seconds) from an Epoch to get a new Epoch
epc = bh.Epoch(2024, 6, 15, 10, 30, 0.0, 0.0)

# Subtract 1 hour (3600 seconds)
epc_minus_hour = epc - 3600.0
print(f"Minus 1 hour: {epc_minus_hour}")

# You can also update an Epoch in-place by subtracting seconds
epc = bh.Epoch(2024, 1, 1, 0, 0, 0.0, 0.0)
epc -= 61.0  # Subtract 61 seconds
print(f"In-place minus 61 seconds: {epc}")
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create two epochs
    let epc1 = bh::Epoch::from_datetime(2024, 1, 1, 12, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    let epc2 = bh::Epoch::from_datetime(2024, 1, 2, 13, 1, 1.0, 0.0, bh::TimeSystem::UTC);

    // Compute time difference in seconds
    let dt = epc2 - epc1;
    println!("Time difference: {:.1} seconds", dt);
    // Time difference: 90061.0 seconds

    // You can also subtract a float (in seconds) from an Epoch to get a new Epoch
    let epc = bh::Epoch::from_datetime(2024, 6, 15, 10, 30, 0.0, 0.0, bh::TimeSystem::UTC);
    let epc_minus_hour = epc - 3600.0;
    println!("Minus 1 hour: {}", epc_minus_hour);
    // Minus 1 hour: 2024-06-15 09:30:00.000 UTC

    // You can also update an Epoch in-place by subtracting seconds
    let mut epc = bh::Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    epc -= 61.0; // Subtract 61 seconds
    println!("In-place minus 61 seconds: {}", epc);
    // In-place minus 61 seconds: 2023-12-31 23:58:59.000 UTC
}

Other Operations

The Epoch class also supports comparison operations (e.g., equality, less than, greater than) to compare different time instances. It also supports methods for getting string representations using language-specific formatting options.

import brahe as bh

bh.initialize_eop()

# Create an epoch
epc_1 = bh.Epoch(2024, 1, 1, 12, 0, 0.0, 0.0)
epc_2 = bh.Epoch(2024, 1, 1, 12, 0, 0.0, 1.0)
epc_3 = bh.Epoch(2024, 1, 1, 12, 0, 0.0, 0.0)

# You can compare two Epoch instances for equality
print(f"epc_1 == epc_2: {epc_1 == epc_2}")
# epc_1 == epc_2: False
print(f"epc_1 == epc_3: {epc_1 == epc_3}")
# epc_1 == epc_3: True

# You can also use inequality and comparison operators
print(f"epc_1 != epc_2: {epc_1 != epc_2}")
# epc_1 != epc_2: True
print(f"epc_1 < epc_2: {epc_1 < epc_2}")
# epc_1 < epc_2: True
print(f"epc_2 < epc_1: {epc_2 < epc_1}")
# epc_2 > epc_1: False
print(f"epc_2 > epc_1: {epc_2 > epc_1}")
# epc_2 > epc_1: True
print(f"epc_1 <= epc_3: {epc_1 <= epc_3}")
# epc_1 <= epc_3: True
print(f"epc_2 >= epc_1: {epc_2 >= epc_1}")
# epc_2 >= epc_1: True
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create an epoch
    let epc_1 = bh::Epoch::from_datetime(2024, 1, 1, 12, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    let epc_2 = bh::Epoch::from_datetime(2024, 1, 1, 12, 0, 0.0, 1.0, bh::TimeSystem::UTC);
    let epc_3 = bh::Epoch::from_datetime(2024, 1, 1, 12, 0, 0.0, 0.0, bh::TimeSystem::UTC);

    // You can compare two Epoch instances for equality
    println!("epc_1 == epc_2: {}", epc_1 == epc_2);
    // epc_1 == epc_2: false
    println!("epc_1 == epc_3: {}", epc_1 == epc_3);
    // epc_1 == epc_3: true

    // You can also use inequality and comparison operators
    println!("epc_1 != epc_2: {}", epc_1 != epc_2);
    // epc_1 != epc_2: true
    println!("epc_1 < epc_2: {}", epc_1 < epc_2);
    // epc_1 < epc_2: true
    println!("epc_2 < epc_1: {}", epc_2 < epc_1);
    // epc_2 > epc_1: false
    println!("epc_2 > epc_1: {}", epc_2 > epc_1);
    // epc_2 > epc_1: true
    println!("epc_1 <= epc_3: {}", epc_1 <= epc_3);
    // epc_1 <= epc_3: true
    println!("epc_2 >= epc_1: {}", epc_2 >= epc_1);
    // epc_2 >= epc_1: true
}

Output and Formatting

Finally, you can take any Epoch instance and then output it in different representations.

Date Time

You can extract the date and time components from an Epoch, optionally converting to a different time system.

import brahe as bh

bh.initialize_eop()

# Create an epoch
epc = bh.Epoch(2024, 6, 15, 14, 30, 45.5, 0.0)
print(f"Epoch: {epc}")
# Epoch: 2024-06-15 14:30:45.500 UTC

# Output the equivalent Julian Date
jd = epc.jd()
print(f"Julian Date: {jd:.6f}")
# Julian Date: 2460477.104693

# Get the Julian Date in a different time system (e.g., TT)
jd_tt = epc.jd_as_time_system(time_system=bh.TT)
print(f"Julian Date (TT): {jd_tt:.6f}")
# Julian Date (TT): 2460477.105494

# Output the equivalent Modified Julian Date
mjd = epc.mjd()
print(f"Modified Julian Date: {mjd:.6f}")
# Modified Julian Date: 60476.604693

# Get the Modified Julian Date in a different time system (e.g., GPS)
mjd_gps = epc.mjd_as_time_system(time_system=bh.GPS)
print(f"Modified Julian Date (GPS): {mjd_gps:.6f}")
# Modified Julian Date (GPS): 60476.604902

# Get the GPS Week and Seconds of Week
gps_week, gps_sow = epc.gps_date()
print(f"GPS Week: {gps_week}, Seconds of Week: {gps_sow:.3f}")
# GPS Week: 2318, Seconds of Week: 570663.500

# The Epoch as GPS seconds since the GPS epoch
gps_seconds = epc.gps_seconds()
print(f"GPS Seconds since epoch: {gps_seconds:.3f}")
# GPS Seconds since epoch: 1402497063.500
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create an epoch
    let epc = bh::Epoch::from_datetime(2024, 6, 15, 14, 30, 45.5, 0.0, bh::TimeSystem::UTC);
    println!("Epoch: {}", epc);
    // Epoch: 2024-06-15 14:30:45.500 UTC

    // Output the equivalent Julian Date
    let jd = epc.jd();
    println!("Julian Date: {:.6}", jd);
    // Julian Date: 2460477.104693

    // Get the Julian Date in a different time system (e.g., TT)
    let jd_tt = epc.jd_as_time_system(bh::TimeSystem::TT);
    println!("Julian Date (TT): {:.6}", jd_tt);
    // Julian Date (TT): 2460477.105494

    // Output the equivalent Modified Julian Date
    let mjd = epc.mjd();
    println!("Modified Julian Date: {:.6}", mjd);
    // Modified Julian Date: 60476.604693

    // Get the Modified Julian Date in a different time system (e.g., GPS)
    let mjd_gps = epc.mjd_as_time_system(bh::TimeSystem::GPS);
    println!("Modified Julian Date (GPS): {:.6}", mjd_gps);
    // Modified Julian Date (GPS): 60476.604902

    // Get the GPS Week and Seconds of Week
    let (gps_week, gps_sow) = epc.gps_date();
    println!("GPS Week: {}, Seconds of Week: {:.3}", gps_week, gps_sow);
    // GPS Week: 2318, Seconds of Week: 570663.500

    // The Epoch as GPS seconds since the GPS epoch
    let gps_seconds = epc.gps_seconds();
    println!("GPS Seconds since epoch: {:.3}", gps_seconds);
    // GPS Seconds since epoch: 1402497063.500
}

String Representation

Epochs can be converted to human-readable strings in various formats and time systems.

import brahe as bh

bh.initialize_eop()

# Create an epoch
epc = bh.Epoch(2024, 6, 15, 14, 30, 45.123456789, 0.0)

# Default string representation
print(f"Default: {epc}")
# Default: 2024-06-15 14:30:45.123 UTC

# Explicit string conversion
print(f"String: {str(epc)}")
# String: 2024-06-15 14:30:45.123 UTC

# Debug representation
print(f"Debug: {repr(epc)}")
# Debug: Epoch<2460477, 9082, 123456788.98545027, 0, UTC>

# Get string in a different time system
print(f"TT: {epc.to_string_as_time_system(bh.TimeSystem.TT)}")
# TT: 2024-06-15 14:31:54.307 TT

# Get as ISO 8601 formatted string
print(f"ISO 8601: {epc.isostring()}")
# ISO 8601: 2024-06-15T14:30:45Z

# Get as ISO 8601 with different number of decimal places
print(f"ISO 8601 (0 decimal places): {epc.isostring_with_decimals(0)}")
print(f"ISO 8601 (3 decimal places): {epc.isostring_with_decimals(3)}")
print(f"ISO 8601 (6 decimal places): {epc.isostring_with_decimals(6)}")
# ISO 8601 (0 decimal places): 2024-06-15T14:30:45Z
# ISO 8601 (3 decimal places): 2024-06-15T14:30:45.123Z
# ISO 8601 (6 decimal places): 2024-06-15T14:30:45.123456Z
use brahe as bh;

fn main() {
    bh::initialize_eop().unwrap();

    // Create an epoch
    let epc = bh::Epoch::from_datetime(2024, 6, 15, 14, 30, 45.123456789, 0.0, bh::TimeSystem::UTC);

    // Default string representation
    println!("Default: {}", epc);
    // String: 2024-06-15 14:30:45.123 UTC

    // Explicit string conversion (same as default in Rust)
    println!("String: {}", epc.to_string());
    // String: 2024-06-15 14:30:45.123 UTC

    // Debug representation
    println!("Debug: {:?}", epc);
    // Debug: Epoch<2460477, 9082, 123456788.98545027, 0, UTC>

    // Get string in a different time system
    println!("TT: {}", epc.to_string_as_time_system(bh::TimeSystem::TT));
    // TT: 2024-06-15 14:31:54.307 TT

    // Get as ISO 8601 formatted string
    println!("ISO 8601: {}", epc.isostring());
    // ISO 8601: 2024-06-15T14:30:45Z

    // Get as ISO 8601 with different number of decimal places
    println!("ISO 8601 (0 decimal places): {}", epc.isostring_with_decimals(0));
    println!("ISO 8601 (3 decimal places): {}", epc.isostring_with_decimals(3));
    println!("ISO 8601 (6 decimal places): {}", epc.isostring_with_decimals(6));
    // ISO 8601 (0 decimal places): 2024-06-15T14:30:45Z
    // ISO 8601 (3 decimal places): 2024-06-15T14:30:45.123Z
    // ISO 8601 (6 decimal places): 2024-06-15T14:30:45.123456Z
}

See Also