Skip to content

Use consistent time unit names for our API #9343

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
126f2e4
Add section in `CODE_STYLE.md` about units
emilk Mar 21, 2025
eaccac1
Rust: use "nanos" instead of "ns"
emilk Mar 21, 2025
b154014
Rust: use "millis" instead of "milliseconds"
emilk Mar 21, 2025
93e4e27
Rust: "seconds" -> "secs"
emilk Mar 21, 2025
a91eef5
More _ns -> _nanos
emilk Mar 21, 2025
575dbf0
Update migration guide
emilk Mar 21, 2025
d5bb606
Update C++ API
emilk Mar 21, 2025
e55f671
codegen
emilk Mar 21, 2025
7ab50d7
Fix python bug
emilk Mar 21, 2025
e7b18c5
cargo fmt
emilk Mar 21, 2025
77276cd
Revert renaming of deprecated functions
emilk Mar 25, 2025
8d2fb7d
Merge branch 'main' into emilk/consistent-naming
emilk Mar 25, 2025
08c1f79
C++ build fix
emilk Mar 25, 2025
c43c5f1
Fix inconsistently named function
emilk Mar 26, 2025
864babd
Merge branch 'main' into emilk/consistent-naming
emilk Mar 26, 2025
ab89beb
read_frame_timestamps_ns -> read_frame_timestamps_nanos
emilk Mar 26, 2025
8856601
filter_range_seconds -> filter_range_secs
emilk Mar 26, 2025
85185de
nanoseconds -> nanos
emilk Mar 26, 2025
a2842eb
Add deprecated `read_frame_timestamps_ns` to all APIs
emilk Mar 27, 2025
42c3f22
Add old function name as deprecated
emilk Mar 27, 2025
e3cb523
Some more renaming and refactors
emilk Mar 27, 2025
b243f87
Update VideoTimestamp
emilk Mar 27, 2025
6a43bef
more of the same
emilk Mar 27, 2025
1b41e5c
Rename `VideoFrameReference.columns_*`
emilk Mar 27, 2025
50e920e
Add entry to migration guide
emilk Mar 27, 2025
59a9a39
Merge branch 'main' into emilk/consistent-naming
emilk Mar 27, 2025
ba20fc9
Fix double space
emilk Mar 27, 2025
09524cf
Merge branch 'main' into emilk/consistent-naming
emilk Mar 31, 2025
9803b38
Undo rename of deprecated function
emilk Mar 31, 2025
577cb94
Fix reference to non-existing function in C++ API
emilk Mar 31, 2025
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
13 changes: 13 additions & 0 deletions CODE_STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,19 @@ Avoid negations in names. A lot of people struggle with double negations, so thi

For UI functions (functions taking an `&mut egui::Ui` argument), we use the name `ui` or `_ui` suffix, e.g. `blueprint_ui(…)` or `blueprint.ui(…)`.

### Units
* When in doubt, be explicit (`duration_secs: f32` is better than `duration: f32`)
* All things being equal, prefer SI base units (seconds over milliseconds, Hz over RPM, etc)
* When precision matters, prefer `nanos: i64` over `secs: f64`
* Store angles in radians (but you may print/display them as degrees)

Follow the conventions set by Rust stdlib:
* `secs` instead of `seconds`
* `millis` instead of `ms` or `milliseconds`
* `micros` instead of `us` or `microseconds`
* `nanos` instead of `ns` or `nanoseconds`


### Spaces
Points, vectors, rays etc all live in different _spaces_. Whenever there is room for ambiguity, we explicitly state which space something is in, e.g. with `ray_in_world`.

Expand Down
14 changes: 7 additions & 7 deletions crates/store/re_chunk/src/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,7 @@ impl TimeColumn {
}

/// Creates a new [`TimeColumn`] of duration type, in seconds.
pub fn new_duration_seconds(
pub fn new_duration_secs(
name: impl Into<re_log_types::TimelineName>,
seconds: impl IntoIterator<Item = impl Into<f64>>,
) -> Self {
Expand All @@ -999,7 +999,7 @@ impl TimeColumn {
re_log::warn!(
illegal_value = nanos,
new_value = clamped.get(),
"TimeColumn::new_duration_seconds() called with out-of-range value. Clamped to valid range."
"TimeColumn::new_duration_secs() called with out-of-range value. Clamped to valid range."
);
}
clamped.get()
Expand All @@ -1013,7 +1013,7 @@ impl TimeColumn {
}

/// Creates a new [`TimeColumn`] of duration type, in seconds.
pub fn new_timestamp_seconds_since_epoch(
pub fn new_timestamp_secs_since_epoch(
name: impl Into<re_log_types::TimelineName>,
seconds: impl IntoIterator<Item = impl Into<f64>>,
) -> Self {
Expand All @@ -1025,7 +1025,7 @@ impl TimeColumn {
re_log::warn!(
illegal_value = nanos,
new_value = clamped.get(),
"TimeColumn::new_timestamp_seconds_since_epoch() called with out-of-range value. Clamped to valid range."
"TimeColumn::new_timestamp_secs_since_epoch() called with out-of-range value. Clamped to valid range."
);
}
clamped.get()
Expand All @@ -1039,12 +1039,12 @@ impl TimeColumn {
}

/// Creates a new [`TimeColumn`] of duration type, in seconds.
#[deprecated = "Use `TimeColumn::new_duration_seconds` or `new_timestamp_seconds_since_epoch` instead"]
pub fn new_seconds(
#[deprecated = "Use `TimeColumn::new_duration_secs` or `new_timestamp_secs_since_epoch` instead"]
pub fn new_secs(
name: impl Into<re_log_types::TimelineName>,
seconds: impl IntoIterator<Item = impl Into<f64>>,
) -> Self {
Self::new_duration_seconds(name, seconds)
Self::new_duration_secs(name, seconds)
}

/// Creates a new [`TimeColumn`] measuring duration in nanoseconds.
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_chunk_store/tests/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn format_chunk_store() -> anyhow::Result<()> {
row_id,
[
build_frame_nr(1),
build_log_time(Timestamp::from_ns_since_epoch(1_736_534_622_123_456_789)),
build_log_time(Timestamp::from_nanos_since_epoch(1_736_534_622_123_456_789)),
],
[indices1.try_serialized()?, colors1.try_serialized()?],
)
Expand Down
10 changes: 5 additions & 5 deletions crates/store/re_data_loader/src/loader_archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,16 +191,16 @@ fn load_video(

let video_asset = AssetVideo::new(contents);

let video_frame_reference_chunk = match video_asset.read_frame_timestamps_ns() {
Ok(frame_timestamps_ns) => {
let video_frame_reference_chunk = match video_asset.read_frame_timestamps_nanos() {
Ok(frame_timestamps_nanos) => {
// Time column.
let is_sorted = Some(true);
let frame_timestamps_ns: arrow::buffer::ScalarBuffer<i64> = frame_timestamps_ns.into();
let frame_timestamps_nanos: arrow::buffer::ScalarBuffer<i64> = frame_timestamps_nanos.into();
let time_column =
re_chunk::TimeColumn::new(is_sorted, video_timeline, frame_timestamps_ns.clone());
re_chunk::TimeColumn::new(is_sorted, video_timeline, frame_timestamps_nanos.clone());

// VideoTimestamp component column.
let video_timestamps = frame_timestamps_ns
let video_timestamps = frame_timestamps_nanos
.iter()
.copied()
.map(VideoTimestamp::from_nanoseconds)
Expand Down
8 changes: 4 additions & 4 deletions crates/store/re_data_loader/src/loader_lerobot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,11 @@ fn load_episode_video(
let video_asset = AssetVideo::new(contents.into_owned());
let entity_path = observation;

let video_frame_reference_chunk = match video_asset.read_frame_timestamps_ns() {
Ok(frame_timestamps_ns) => {
let frame_timestamps_ns: arrow::buffer::ScalarBuffer<i64> = frame_timestamps_ns.into();
let video_frame_reference_chunk = match video_asset.read_frame_timestamps_nanos() {
Ok(frame_timestamps_nanos) => {
let frame_timestamps_nanos: arrow::buffer::ScalarBuffer<i64> = frame_timestamps_nanos.into();

let video_timestamps = frame_timestamps_ns
let video_timestamps = frame_timestamps_nanos
.iter()
.take(time_column.num_rows())
.copied()
Expand Down
3 changes: 1 addition & 2 deletions crates/store/re_entity_db/src/entity_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -838,8 +838,7 @@ impl IngestionStatistics {
let nanos_since_epoch = duration_since_epoch.as_nanos() as u64;

// This only makes sense if the clocks are very good, i.e. if the recording was on the same machine!
if let Some(nanos_since_log) =
nanos_since_epoch.checked_sub(row_id.nanoseconds_since_epoch())
if let Some(nanos_since_log) = nanos_since_epoch.checked_sub(row_id.nanos_since_epoch())
{
let now = nanos_since_epoch as f64 / 1e9;
let sec_since_log = nanos_since_log as f32 / 1e9;
Expand Down
4 changes: 2 additions & 2 deletions crates/store/re_grpc_server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ mod tests {
re_chunk::RowId::new(),
re_log_types::TimePoint::default().with(
re_log_types::Timeline::new_sequence("blueprint"),
re_log_types::TimeInt::from_milliseconds(re_log_types::NonMinI64::MIN),
re_log_types::TimeInt::from_millis(re_log_types::NonMinI64::MIN),
),
&re_types::blueprint::archetypes::Background::new(
re_types::blueprint::components::BackgroundKind::SolidColor,
Expand Down Expand Up @@ -700,7 +700,7 @@ mod tests {
re_chunk::RowId::new(),
re_log_types::TimePoint::default().with(
re_log_types::Timeline::new_sequence("log_time"),
re_log_types::TimeInt::from_milliseconds(re_log_types::NonMinI64::MIN),
re_log_types::TimeInt::from_millis(re_log_types::NonMinI64::MIN),
),
&re_types::archetypes::Points2D::new([(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)]),
)
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_log_encoding/src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ mod tests {
re_chunk::RowId::new(),
re_log_types::TimePoint::default().with(
re_log_types::Timeline::new_sequence("blueprint"),
re_log_types::TimeInt::from_milliseconds(re_log_types::NonMinI64::MIN),
re_log_types::TimeInt::from_millis(re_log_types::NonMinI64::MIN),
),
&re_types::blueprint::archetypes::Background::new(
re_types::blueprint::components::BackgroundKind::SolidColor,
Expand Down
56 changes: 28 additions & 28 deletions crates/store/re_log_types/src/index/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Duration {
}

/// Format as seconds, approximately.
pub fn format_seconds(self) -> String {
pub fn format_secs(self) -> String {
let nanos = self.as_nanos();
let secs = nanos as f64 * 1e-9;

Expand All @@ -70,55 +70,55 @@ impl Duration {
self.0
};

let whole_seconds = total_nanos / Self::NANOS_PER_SEC;
let nanos = total_nanos - Self::NANOS_PER_SEC * whole_seconds;
let whole_secs = total_nanos / Self::NANOS_PER_SEC;
let nanos = total_nanos - Self::NANOS_PER_SEC * whole_secs;

let mut seconds_remaining = whole_seconds;
let mut secs_remaining = whole_secs;
let mut did_write = false;

let days = seconds_remaining / Self::SEC_PER_DAY;
let days = secs_remaining / Self::SEC_PER_DAY;
if days > 0 {
write!(f, "{days}d")?;
seconds_remaining -= days * Self::SEC_PER_DAY;
secs_remaining -= days * Self::SEC_PER_DAY;
did_write = true;
}

let hours = seconds_remaining / Self::SEC_PER_HOUR;
let hours = secs_remaining / Self::SEC_PER_HOUR;
if hours > 0 {
if did_write {
write!(f, " ")?;
}
write!(f, "{hours}h")?;
seconds_remaining -= hours * Self::SEC_PER_HOUR;
secs_remaining -= hours * Self::SEC_PER_HOUR;
did_write = true;
}

let minutes = seconds_remaining / Self::SEC_PER_MINUTE;
let minutes = secs_remaining / Self::SEC_PER_MINUTE;
if minutes > 0 {
if did_write {
write!(f, " ")?;
}
write!(f, "{minutes}m")?;
seconds_remaining -= minutes * Self::SEC_PER_MINUTE;
secs_remaining -= minutes * Self::SEC_PER_MINUTE;
did_write = true;
}

const MAX_MILLISECOND_ACCURACY: bool = true;
const MAX_MICROSECOND_ACCURACY: bool = true;

if seconds_remaining > 0 || nanos > 0 || !did_write {
if secs_remaining > 0 || nanos > 0 || !did_write {
if did_write {
write!(f, " ")?;
}

if nanos == 0 {
write!(f, "{seconds_remaining}s")?;
write!(f, "{secs_remaining}s")?;
} else if MAX_MILLISECOND_ACCURACY || nanos % 1_000_000 == 0 {
write!(f, "{}.{:03}s", seconds_remaining, nanos / 1_000_000)?;
write!(f, "{}.{:03}s", secs_remaining, nanos / 1_000_000)?;
} else if MAX_MICROSECOND_ACCURACY || nanos % 1_000 == 0 {
write!(f, "{}.{:06}s", seconds_remaining, nanos / 1_000)?;
write!(f, "{}.{:06}s", secs_remaining, nanos / 1_000)?;
} else {
write!(f, "{seconds_remaining}.{nanos:09}s")?;
write!(f, "{secs_remaining}.{nanos:09}s")?;
}
}

Expand All @@ -131,27 +131,27 @@ impl Duration {
pub fn format_subsecond_as_relative(self) -> String {
let ns = self.as_nanos();

let fractional_ns = ns % 1_000_000_000;
let is_whole_second = fractional_ns == 0;
let fractional_nanos = ns % 1_000_000_000;
let is_whole_second = fractional_nanos == 0;

if is_whole_second {
self.to_string()
} else {
// We are in the sub-second resolution.
// Showing the full time (HH:MM:SS.XXX or 3h 2m 6s …) becomes too long,
// so instead we switch to showing the time as milliseconds since the last whole second:
let ms = fractional_ns as f64 * 1e-6;
if fractional_ns % 1_000_000 == 0 {
let ms = fractional_nanos as f64 * 1e-6;
if fractional_nanos % 1_000_000 == 0 {
format!("{ms:+.0} ms")
} else if fractional_ns % 100_000 == 0 {
} else if fractional_nanos % 100_000 == 0 {
format!("{ms:+.1} ms")
} else if fractional_ns % 10_000 == 0 {
} else if fractional_nanos % 10_000 == 0 {
format!("{ms:+.2} ms")
} else if fractional_ns % 1_000 == 0 {
} else if fractional_nanos % 1_000 == 0 {
format!("{ms:+.3} ms")
} else if fractional_ns % 100 == 0 {
} else if fractional_nanos % 100 == 0 {
format!("{ms:+.4} ms")
} else if fractional_ns % 10 == 0 {
} else if fractional_nanos % 10 == 0 {
format!("{ms:+.5} ms")
} else {
format!("{ms:+.6} ms")
Expand Down Expand Up @@ -222,11 +222,11 @@ mod tests {

#[test]
fn test_formatting_duratuon() {
assert_eq!(&Duration::from_micros(42_000_000).format_seconds(), "+42s");
assert_eq!(&Duration::from_micros(69_000).format_seconds(), "+0.069s");
assert_eq!(&Duration::from_micros(69_900).format_seconds(), "+0.070s");
assert_eq!(&Duration::from_micros(42_000_000).format_secs(), "+42s");
assert_eq!(&Duration::from_micros(69_000).format_secs(), "+0.069s");
assert_eq!(&Duration::from_micros(69_900).format_secs(), "+0.070s");
assert_eq!(
&Duration::from_micros(42_123_000_000).format_seconds(),
&Duration::from_micros(42_123_000_000).format_secs(),
"+42 123s"
);
}
Expand Down
8 changes: 4 additions & 4 deletions crates/store/re_log_types/src/index/time_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ impl TimeCell {

/// Create a timestamp from number of seconds since the unix epoch, 1970-01-01 00:00:00 UTC.
#[inline]
pub fn from_timestamp_seconds_since_epoch(seconds_since_epoch: f64) -> Self {
Self::from_timestamp_nanos_since_epoch((1e9 * seconds_since_epoch).round() as i64)
pub fn from_timestamp_secs_since_epoch(secs_since_epoch: f64) -> Self {
Self::from_timestamp_nanos_since_epoch((1e9 * secs_since_epoch).round() as i64)
}

/// A timestamp of the current clock time.
Expand Down Expand Up @@ -168,7 +168,7 @@ impl TimeCell {
crate::Duration::from_nanos(value.into()).format_subsecond_as_relative()
}

TimeType::TimestampNs => crate::Timestamp::from_ns_since_epoch(value.into())
TimeType::TimestampNs => crate::Timestamp::from_nanos_since_epoch(value.into())
.format_time_compact(timestamp_format),

TimeType::Sequence => typ.format(value, timestamp_format),
Expand All @@ -182,7 +182,7 @@ impl std::fmt::Display for TimeCell {
// NOTE: we avoid special characters here so we can put these formats in an URI
TimeType::Sequence => write!(f, "{}", self.value),
TimeType::DurationNs => crate::Duration::from_nanos(self.value.get()).fmt(f),
TimeType::TimestampNs => crate::Timestamp::from_ns_since_epoch(self.value.get())
TimeType::TimestampNs => crate::Timestamp::from_nanos_since_epoch(self.value.get())
.format_iso()
.fmt(f),
}
Expand Down
4 changes: 2 additions & 2 deletions crates/store/re_log_types/src/index/time_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ impl TimeInt {

/// For time timelines.
#[inline]
pub fn from_milliseconds(millis: NonMinI64) -> Self {
pub fn from_millis(millis: NonMinI64) -> Self {
Self::new_temporal(millis.get().saturating_mul(1_000_000))
}

/// For time timelines.
#[inline]
pub fn from_seconds(seconds: NonMinI64) -> Self {
pub fn from_secs(seconds: NonMinI64) -> Self {
Self::new_temporal(seconds.get().saturating_mul(1_000_000_000))
}

Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_log_types/src/index/time_real.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl TimeReal {
}

#[inline]
pub fn from_seconds(v: f64) -> Self {
pub fn from_secs(v: f64) -> Self {
Self::from(v * 1_000_000_000f64)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_log_types/src/index/time_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl TimeType {
TimeInt::MAX => "+∞".into(),
_ => match self {
Self::Sequence => format!("#{}", re_format::format_int(time_int.as_i64())),
Self::DurationNs => super::Duration::from(time_int).format_seconds(),
Self::DurationNs => super::Duration::from(time_int).format_secs(),
Self::TimestampNs => super::Timestamp::from(time_int).format(timestamp_format),
},
}
Expand Down
Loading
Loading