fix(std): snap time-bucket/session reads to the nominal minute
Provider M1 stamps carry sub-second jitter around minute boundaries (~55% of days stamp the 09:00 Berlin bar at 08:59:59.xxx), so any consumer dividing or truncating the raw stamp mis-buckets boundary bars: Resample and VolTfStop fold the bar into the previous bucket instead of rolling over, Session demotes the first in-session bar to at-open. Fix per the decision logged on the issue: one shared Timestamp::snap_to_nearest_minute in aura-core (round-half-up, rem_euclid so negative epochs snap correctly), applied consumer-side at the three membership computations. The recorded stream stays byte-verbatim — no ingestion rewrite, record-then-replay untouched; an ingestion-time rewrite was rejected because it would hide the raw time axis from every consumer and could reorder the k-way merge around boundaries. Verified: the three formerly-RED tests pass individually; full workspace suite green; clippy clean. closes #280
This commit is contained in:
@@ -11,6 +11,32 @@ use crate::cell::Cell;
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Timestamp(pub i64);
|
||||
|
||||
impl Timestamp {
|
||||
/// Nanoseconds per minute — the one scale factor `snap_to_nearest_minute`
|
||||
/// rounds against.
|
||||
const NS_PER_MINUTE: i64 = 60 * 1_000_000_000;
|
||||
|
||||
/// Snap this instant to the nearest whole-minute boundary (round-half-up,
|
||||
/// ties toward the later minute) — the sub-second-provider-jitter
|
||||
/// correction (#280): a bar's nominal minute can be a bucket/session
|
||||
/// boundary while the raw provider stamp lands a fraction of a second
|
||||
/// early or late. Consumers deriving bucket membership or wall-clock
|
||||
/// minutes from `ctx.now()` snap the read here, once, rather than
|
||||
/// dividing/truncating the raw jittered stamp — the recorded stream
|
||||
/// itself stays byte-verbatim (record-then-replay, C6); only the
|
||||
/// consumer-side read is corrected.
|
||||
pub fn snap_to_nearest_minute(self) -> Timestamp {
|
||||
let m = Self::NS_PER_MINUTE;
|
||||
let rem = self.0.rem_euclid(m);
|
||||
let floor = self.0 - rem;
|
||||
if rem * 2 >= m {
|
||||
Timestamp(floor + m)
|
||||
} else {
|
||||
Timestamp(floor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind tag of a scalar / column, used for the edge-time type check (C7).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ScalarKind {
|
||||
|
||||
Reference in New Issue
Block a user