Skip to content

Time scale and whitespace

The time scale places bars horizontally. Logical coordinates address this chart’s slots; UTC coordinates address instants that other charts can share. The scene uses the first chart’s 120 fixed one-minute bars.

Use Fit history, Back 30 bars, Live edge and Reset time. Compare Logical 20..59 with UTC minutes 20..59, then select Future space to extend past the last bar. Readouts describe the last layout; they also update when you pan or zoom directly. Call the scene’s show(ui, &mut chart) before chart.show(ui) on each frame.

Navigate a fixed one-minute historyOpen full size

Fit and scroll, set a logical or UTC range, then leave future whitespace.

The interactive example needs JavaScript and WebAssembly. You can still use the chart setup alongside it.

time_scale.rs
use chrono::TimeDelta;
use eframe::egui;
use egui_charts::{
Chart,
model::LogicalRange,
widget::{SeriesId, VisibleTimeRange},
};
/// Fit, scroll and address the same history in logical or UTC coordinates.
pub fn show(ui: &mut egui::Ui, chart: &mut Chart) {
// Range setters use the plot width from the last show, including after a resize.
let ready = chart.time_scale_size().x > 0.0;
ui.add_enabled_ui(ready, |ui| {
ui.horizontal_wrapped(|ui| {
if ui.button("Fit history").clicked() {
chart.fit_content();
}
if ui.button("Back 30 bars").clicked() {
chart.scroll_to_position(-30.0, true);
}
if ui.button("Live edge").clicked() {
chart.scroll_to_real_time(true);
}
if ui.button("Reset time").clicked() {
chart.reset_time_scale();
}
});
ui.horizontal_wrapped(|ui| {
if ui.button("Logical 20..59").clicked() {
chart.set_visible_logical_range(LogicalRange {
left: 20.0,
right: 59.0,
});
}
if ui.button("UTC minutes 20..59").clicked() {
let start = chart.bars(SeriesId::MAIN)[0].time;
chart.apply_synced_time_range(VisibleTimeRange {
from: start + TimeDelta::minutes(20),
to: start + TimeDelta::minutes(59),
});
}
if ui.button("Future space").clicked() {
chart.set_visible_logical_range(LogicalRange {
left: 80.0,
right: 139.0,
});
}
});
});
if ready {
if let Some(range) = chart.visible_logical_range() {
ui.label(format!("Logical: {:.2} .. {:.2}", range.left, range.right));
}
if let Some(range) = chart.visible_time_range() {
ui.label(format!(
"UTC: {} .. {}",
range.from.format("%F %T"),
range.to.format("%F %T")
));
}
if let Some(x) = chart.index_to_x(30.0) {
let time = chart.index_to_time(30.0).unwrap();
let index = chart.time_to_index(time).unwrap();
ui.label(format!(
"Index {index:.0} = {} UTC; x = {x:.1} pt",
time.format("%T")
));
}
} else {
ui.label("Waiting for the first plot layout.");
}
}

fit_content fits the stored history and keeps the configured right margin. scroll_to_position positions the latest bar relative to the right edge in bars: positive positions leave space on the right, negative positions scroll into history. scroll_to_real_time returns to the live edge while keeping zoom; it is a one-time scroll. Its animated flag, also used by scroll_to_position, animates the move. These scenes use fixed data; a host supplies historical and streaming updates.

reset_time_scale restores the configured spacing and right offset. It leaves the price ranges alone. reset_chart_view also returns every pane’s price scales to autoscale.

visible_logical_range returns a LogicalRange. Index 0 is the oldest slot in this chart; bounds can be fractional or outside the data. A range spans right - left + 1 bar widths, so 20..59 spans 40. set_visible_logical_range sets spacing and scroll together. It returns false for empty data, non-finite bounds or reversed bounds, keeping the view unchanged. Spacing and edge limits still apply. Logical indices depend on the chart’s history and interval; do not copy them between charts with different slots.

visible_time_range returns a VisibleTimeRange in UTC, including whitespace. Use apply_synced_time_range to fit those instants to another chart’s width. Check its boolean result: empty data, invalid or wholly disjoint ranges, and endpoints mapping to one element are rejected. Its maximum spacing is the configured limit, or 50 points when unlimited, so very short spans may be centred rather than filling the plot.

index_to_time and time_to_index convert between fractional slots and UTC. Times between bars are interpolated; outside the history they are extrapolated using the interval or observed edge spacing. Calendar month and year intervals follow calendar stepping, as detailed in the method documentation. Display time zones affect labels, while the data and shared time ranges remain UTC.

The last sample bar has logical index 119. The scene’s 80..139 range leaves 20 logical slots beyond it without inventing future OHLC values. To include a known empty timestamp in supplied history, use Bar::whitespace.

TimeScaleOptions is available through ChartOptions::time_scale. Edit a copy from chart_options() and apply it with set_chart_options.

Call Chart::show once at the intended size before fitting a range or converting screen positions. The range setters use the last drawn plot width; after resizing, show again before applying a width-dependent range. The scene enables its buttons once time_scale_size reports a positive width.

index_to_x and x_to_index use screen points in the chart’s egui Ui, with x at the bar centre. They return None before the first layout. For separate charts, choose one source and forward only its changed UTC range and hover_time to set_synced_crosshair_time. The builder guide and synchronized charts example show that integration.