You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
47 lines
1.7 KiB
47 lines
1.7 KiB
/// Ingest time-anomaly detection (#285, child of #280).
|
|
///
|
|
/// A received message carries the SENDER's claimed timestamp in its payload;
|
|
/// nothing guarantees that clock is sane. These helpers compare the claimed
|
|
/// time against the local arrival time so the ingest log can prove, after the
|
|
/// fact, whether an odd date came in on the wire.
|
|
library;
|
|
|
|
/// Claimed-vs-arrival gap beyond which a received message is logged as a
|
|
/// time anomaly. Generous enough for mesh store-and-forward delays and
|
|
/// timezone-free radio clocks; months-off RTCs blow well past it.
|
|
const Duration rxTimeAnomalyThreshold = Duration(hours: 24);
|
|
|
|
/// True when the sender-claimed [claimed] time is further than [threshold]
|
|
/// from the local [arrival] time in either direction.
|
|
bool isRxTimeAnomaly(
|
|
DateTime arrival,
|
|
DateTime claimed, {
|
|
Duration threshold = rxTimeAnomalyThreshold,
|
|
}) {
|
|
return claimed.difference(arrival).abs() > threshold;
|
|
}
|
|
|
|
/// Compact human delta of claimed relative to arrival, e.g. `-64d5h` (claimed
|
|
/// 64 days in the past) or `+3m` (claimed 3 minutes ahead). `0s` when equal.
|
|
String formatRxTimeDelta(DateTime arrival, DateTime claimed) {
|
|
final delta = claimed.difference(arrival);
|
|
if (delta == Duration.zero) return '0s';
|
|
final sign = delta.isNegative ? '-' : '+';
|
|
var d = delta.abs();
|
|
final parts = <String>[];
|
|
if (d.inDays > 0) {
|
|
parts.add('${d.inDays}d');
|
|
d -= Duration(days: d.inDays);
|
|
}
|
|
if (d.inHours > 0) {
|
|
parts.add('${d.inHours}h');
|
|
d -= Duration(hours: d.inHours);
|
|
}
|
|
if (parts.length < 2 && d.inMinutes > 0) {
|
|
parts.add('${d.inMinutes}m');
|
|
d -= Duration(minutes: d.inMinutes);
|
|
}
|
|
if (parts.isEmpty) parts.add('${d.inSeconds}s');
|
|
return '$sign${parts.take(2).join()}';
|
|
}
|