micromegas_analytics/
time.rs1use std::sync::Arc;
2
3use anyhow::Result;
4use chrono::{DateTime, TimeDelta, Utc};
5use datafusion::scalar::ScalarValue;
6use micromegas_telemetry::types::block::BlockMetadata;
7
8use crate::metadata::ProcessMetadata;
9
10const NANOS_PER_SEC: f64 = 1000.0 * 1000.0 * 1000.0;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct TimeRange {
15 pub begin: DateTime<Utc>,
16 pub end: DateTime<Utc>,
17}
18
19impl TimeRange {
20 pub fn new(begin: DateTime<Utc>, end: DateTime<Utc>) -> Self {
21 Self { begin, end }
22 }
23}
24
25pub fn make_time_converter_from_block_meta(
27 process: &ProcessMetadata,
28 block: &BlockMetadata,
29) -> Result<ConvertTicks> {
30 if process.tsc_frequency > 0 {
31 return ConvertTicks::from_meta_data(
33 process.start_ticks,
34 process.start_time.timestamp_nanos_opt().unwrap_or_default(),
35 process.tsc_frequency,
36 );
37 }
38 let delta_time = block.end_time - process.start_time;
39 let nb_seconds = delta_time.num_nanoseconds().unwrap_or_default() as f64 / 1_000_000_000.0;
40 let ticks_per_second = block.end_ticks as f64 / nb_seconds;
41 ConvertTicks::from_meta_data(
42 process.start_ticks,
43 process.start_time.timestamp_nanos_opt().unwrap_or_default(),
44 ticks_per_second.round() as i64,
45 )
46}
47
48pub fn make_time_converter_from_latest_timing(
52 process: &ProcessMetadata,
53 last_block_end_ticks: i64,
54 last_block_end_time: chrono::DateTime<chrono::Utc>,
55) -> Result<ConvertTicks> {
56 if process.tsc_frequency > 0 {
57 return ConvertTicks::from_meta_data(
59 process.start_ticks,
60 process.start_time.timestamp_nanos_opt().unwrap_or_default(),
61 process.tsc_frequency,
62 );
63 }
64 let delta_time = last_block_end_time - process.start_time;
66 let nb_seconds = delta_time.num_nanoseconds().unwrap_or_default() as f64 / 1_000_000_000.0;
67 let ticks_per_second = last_block_end_ticks as f64 / nb_seconds;
68 ConvertTicks::from_meta_data(
69 process.start_ticks,
70 process.start_time.timestamp_nanos_opt().unwrap_or_default(),
71 ticks_per_second.round() as i64,
72 )
73}
74
75#[derive(Debug, Clone)]
77pub struct ConvertTicks {
78 tick_offset: i64,
79 process_start_ns: i64,
80 frequency: i64, inv_tsc_frequency_ns: f64,
82 inv_tsc_frequency_ms: f64,
83}
84
85impl ConvertTicks {
86 pub fn from_meta_data(start_ticks: i64, process_start_ns: i64, frequency: i64) -> Result<Self> {
87 if frequency <= 0 {
88 anyhow::bail!("invalid frequency")
89 }
90 Ok(Self {
91 tick_offset: start_ticks,
92 process_start_ns,
93 frequency,
94 inv_tsc_frequency_ns: get_tsc_frequency_inverse_ns(frequency),
95 inv_tsc_frequency_ms: get_tsc_frequency_inverse_ms(frequency),
96 })
97 }
98
99 pub fn get_frequency(&self) -> i64 {
101 self.frequency
102 }
103
104 pub fn to_ticks(&self, delta: TimeDelta) -> i64 {
106 let mut seconds = delta.num_seconds() as f64;
107 seconds += delta.subsec_nanos() as f64 / NANOS_PER_SEC;
108 let freq = self.frequency as f64;
109 (seconds * freq).round() as i64
110 }
111
112 pub fn ticks_to_nanoseconds(&self, ticks: i64) -> i64 {
114 let delta = (ticks - self.tick_offset) as f64;
115 let ns_since_process_start = (delta * self.inv_tsc_frequency_ns).round() as i64;
116 self.process_start_ns + ns_since_process_start
117 }
118
119 pub fn delta_ticks_to_time(&self, delta: i64) -> DateTime<Utc> {
121 let ns_since_process_start = (delta as f64 * self.inv_tsc_frequency_ns).round() as i64;
122 DateTime::from_timestamp_nanos(self.process_start_ns + ns_since_process_start)
123 }
124
125 pub fn delta_ticks_to_ns(&self, delta: i64) -> i64 {
127 let ns_since_process_start = (delta as f64 * self.inv_tsc_frequency_ns).round() as i64;
128 self.process_start_ns + ns_since_process_start
129 }
130
131 pub fn delta_ticks_to_ms(&self, delta_ticks: i64) -> f64 {
133 let delta = delta_ticks as f64;
134 delta * self.inv_tsc_frequency_ms
135 }
136
137 pub fn time_to_delta_ticks(&self, time: DateTime<Utc>) -> i64 {
139 self.to_ticks(time - DateTime::from_timestamp_nanos(self.process_start_ns))
140 }
141}
142
143#[allow(clippy::cast_precision_loss)]
145pub fn get_tsc_frequency_inverse_ms(tsc_frequency: i64) -> f64 {
146 1000.0 / tsc_frequency as f64
147}
148
149#[allow(clippy::cast_precision_loss)]
151pub fn get_tsc_frequency_inverse_ns(tsc_frequency: i64) -> f64 {
152 NANOS_PER_SEC / tsc_frequency as f64
153}
154
155pub fn datetime_to_scalar(v: DateTime<Utc>) -> ScalarValue {
157 lazy_static::lazy_static! {
158 static ref UTC_OFFSET: Arc<str> = Arc::from("+00:00");
159 }
160 ScalarValue::TimestampNanosecond(v.timestamp_nanos_opt(), Some(UTC_OFFSET.clone()))
161}