Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_datime/src/index/time_basis.rs

10.7 KiB, 78 runs

created by r1870400018:8464, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Time coordinate systems.
2//!
3//! A basis pairs an epoch with a unit, which is what lets a value in one
4//! system be converted to another. Unix, Java and nanosecond bases are
5//! provided, and a custom basis can be built from any epoch and unit.
6//!
7//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
8//! Anthropic Claude
9
10use oxedyne_fe2o3_core::prelude::*;
11use crate::{
12 time::{CalClock, CalClockZone, CalClockDuration},
13 clock::{ClockSecond, ClockMilliSecond, ClockNanoSecond, PerSecondRated},
14 index::time_integer::{TimeInteger, TimeLong},
15};
16
17pub struct TimeIndexBasis {
18 epoch: CalClock,
19 unit: Box<dyn PerSecondRated>,
20}
21
22impl TimeIndexBasis {
23 pub fn new(epoch: CalClock, unit: Box<dyn PerSecondRated>) -> Self {
24 Self { epoch, unit }
25 }
26
27 pub fn epoch(&self) -> &CalClock {
28 &self.epoch
29 }
30
31 pub fn zone(&self) -> &CalClockZone {
32 self.epoch.zone()
33 }
34
35 pub fn unit(&self) -> &dyn PerSecondRated {
36 self.unit.as_ref()
37 }
38
39 pub fn convert<F: TimeInteger, T: TimeInteger>(
40 &self,
41 time_integer: F,
42 to_basis: &TimeIndexBasis,
43 ) -> Outcome<T> {
44 // Calculate the difference between epochs in seconds
45 let epoch_diff = res!(self.epoch.duration_until(&to_basis.epoch));
46 let epoch_diff_seconds = epoch_diff.total_seconds() as f64;
47
48 // Get the scale factors for both units
49 let from_scale = self.unit.per_second();
50 let to_scale = to_basis.unit.per_second();
51
52 // Convert the time integer to seconds since this epoch
53 let time_in_seconds = time_integer.long_value() as f64 / from_scale as f64;
54
55 // Adjust for epoch difference
56 let adjusted_seconds = time_in_seconds + epoch_diff_seconds;
57
58 // Convert to target basis units
59 let target_value = adjusted_seconds * to_scale as f64;
60
61 // Create the target TimeInteger
62 let long_value = target_value.round() as i64;
63 T::from_string(&long_value.to_string())
64 }
65
66 pub fn get_zone_offset_duration(&self, zone: &CalClockZone, unix_time: i64) -> Outcome<CalClockDuration> {
67 // Convert Unix timestamp to CalClock
68 let time = res!(CalClock::from_unix_timestamp_seconds(unix_time, zone.clone()));
69
70 // Calculate offset from UTC
71 let utc_time = res!(time.to_utc_zone());
72 time.duration_until(&utc_time)
73 }
74}
75
76impl PartialEq for TimeIndexBasis {
77 fn eq(&self, other: &Self) -> bool {
78 self.epoch == other.epoch && self.unit.per_second() == other.unit.per_second()
79 }
80}
81
82impl std::fmt::Debug for TimeIndexBasis {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.debug_struct("TimeIndexBasis")
85 .field("epoch", &self.epoch)
86 .field("unit_per_second", &self.unit.per_second())
87 .finish()
88 }
89}
90
91pub struct UnixTime; // 1970-01-01 00:00:00 UTC, seconds
92
93impl UnixTime {
94 pub fn basis() -> Outcome<TimeIndexBasis> {
95 let epoch = res!(CalClock::new(1970, 1, 1, 0, 0, 0, 0, CalClockZone::utc()));
96 let unit = Box::new(res!(ClockSecond::new(1)));
97 Ok(TimeIndexBasis::new(epoch, unit))
98 }
99
100 pub fn from_timestamp(timestamp: i64) -> TimeIndex<TimeLong> {
101 TimeIndex::new(TimeLong::new(timestamp))
102 }
103}
104
105pub struct JavaTime; // 1970-01-01 00:00:00 UTC, milliseconds
106
107impl JavaTime {
108 pub fn basis() -> Outcome<TimeIndexBasis> {
109 let epoch = res!(CalClock::new(1970, 1, 1, 0, 0, 0, 0, CalClockZone::utc()));
110 let unit = Box::new(res!(ClockMilliSecond::new(1)));
111 Ok(TimeIndexBasis::new(epoch, unit))
112 }
113
114 pub fn from_timestamp(timestamp: i64) -> TimeIndex<TimeLong> {
115 TimeIndex::new(TimeLong::new(timestamp))
116 }
117}
118
119pub struct NanoTime; // 1970-01-01 00:00:00 UTC, nanoseconds
120
121impl NanoTime {
122 pub fn basis() -> Outcome<TimeIndexBasis> {
123 let epoch = res!(CalClock::new(1970, 1, 1, 0, 0, 0, 0, CalClockZone::utc()));
124 let unit = Box::new(res!(ClockNanoSecond::new(1)));
125 Ok(TimeIndexBasis::new(epoch, unit))
126 }
127
128 pub fn from_timestamp(timestamp: i64) -> TimeIndex<TimeLong> {
129 TimeIndex::new(TimeLong::new(timestamp))
130 }
131}
132
133pub struct CustomTime {
134 basis: TimeIndexBasis,
135}
136
137impl CustomTime {
138 pub fn new(epoch: CalClock, unit: Box<dyn PerSecondRated>) -> Self {
139 Self {
140 basis: TimeIndexBasis::new(epoch, unit),
141 }
142 }
143
144 pub fn basis(&self) -> &TimeIndexBasis {
145 &self.basis
146 }
147}
148
149#[derive(Debug, Clone)]
150pub struct TimeIndex<I: TimeInteger> {
151 time: I,
152 zone: Option<CalClockZone>,
153}
154
155impl<I: TimeInteger> TimeIndex<I> {
156 pub fn new(time: I) -> Self {
157 Self { time, zone: None }
158 }
159
160 pub fn new_with_zone(time: I, zone: CalClockZone) -> Self {
161 Self {
162 time,
163 zone: Some(zone),
164 }
165 }
166
167 pub fn time(&self) -> &I {
168 &self.time
169 }
170
171 pub fn zone(&self) -> Option<&CalClockZone> {
172 self.zone.as_ref()
173 }
174
175 pub fn make_from_long(value: i64) -> Outcome<Self>
176 where
177 I: From<i64>,
178 {
179 Ok(Self::new(I::from(value)))
180 }
181
182 pub fn make_from_bytes(bytes: Vec<u8>) -> Outcome<Self> {
183 let time = res!(I::from_bytes(&bytes));
184 Ok(Self::new(time))
185 }
186
187 pub fn make_from_string(s: &str) -> Outcome<Self> {
188 let time = res!(I::from_string(s));
189 Ok(Self::new(time))
190 }
191
192 pub fn get_start(&self) -> Outcome<Self> {
193 Ok(self.clone())
194 }
195
196 pub fn get_finish(&self) -> Outcome<Self> {
197 Ok(self.clone())
198 }
199
200 pub fn to_zone(&self, zone: CalClockZone) -> Self {
201 Self {
202 time: self.time.clone(),
203 zone: Some(zone),
204 }
205 }
206
207 // The i64 forms saturate where the TimeIndex forms report overflow.
208 pub fn plus(&self, other: &Self) -> Outcome<Self> {
209 let result_time = res!(self.time.clone().add_to(other.time.clone()));
210 Ok(Self::new(result_time))
211 }
212
213 pub fn plus_long(&self, value: i64) -> Self {
214 let result_time = self.time.clone().add_to_long(value);
215 Self::new(result_time)
216 }
217
218 pub fn minus(&self, other: &Self) -> Outcome<TimeIndexDuration<I>> {
219 let result_time = res!(self.time.clone().subtract_it(other.time.clone()));
220 Ok(TimeIndexDuration::new(result_time))
221 }
222
223 pub fn multiply_by(&self, other: &Self) -> Outcome<Self> {
224 let result_time = res!(self.time.clone().multiply_by(other.time.clone()));
225 Ok(Self::new(result_time))
226 }
227
228 pub fn divide_by(&self, other: &Self) -> Outcome<Self> {
229 let result_time = res!(self.time.clone().divide_by(other.time.clone()));
230 Ok(Self::new(result_time))
231 }
232
233 pub fn subtract_it(&self, other: &Self) -> Outcome<Self> {
234 let result_time = res!(self.time.clone().subtract_it(other.time.clone()));
235 Ok(Self::new(result_time))
236 }
237
238 pub fn add_to(&self, other: &Self) -> Outcome<Self> {
239 let result_time = res!(self.time.clone().add_to(other.time.clone()));
240 Ok(Self::new(result_time))
241 }
242
243 pub fn is_before(&self, other: &Self) -> Outcome<bool> {
244 Ok(self.time < other.time)
245 }
246
247 pub fn is_after(&self, other: &Self) -> Outcome<bool> {
248 Ok(self.time > other.time)
249 }
250
251 pub fn or_earlier(&self, other: &Self) -> Outcome<Self> {
252 if self.time <= other.time {
253 Ok(self.clone())
254 } else {
255 Ok(other.clone())
256 }
257 }
258
259 pub fn or_later(&self, other: &Self) -> Outcome<Self> {
260 if self.time >= other.time {
261 Ok(self.clone())
262 } else {
263 Ok(other.clone())
264 }
265 }
266
267 pub fn to_calclock(&self, basis: &TimeIndexBasis) -> Outcome<CalClock> {
268 // Convert time integer to seconds since epoch
269 let time_in_seconds = self.time.long_value() as f64 / basis.unit.per_second() as f64;
270
271 // Add to epoch
272 let duration = CalClockDuration::from_seconds(time_in_seconds as i64);
273 basis.epoch.add_duration(&duration)
274 }
275
276 pub fn from_calclock(calclock: &CalClock, basis: &TimeIndexBasis) -> Outcome<Self> {
277 // Calculate duration since epoch
278 let duration = res!(basis.epoch.duration_until(calclock));
279 let seconds = duration.total_seconds() as f64;
280
281 // Convert to basis units
282 let time_value = seconds * basis.unit.per_second() as f64;
283 let time_integer = res!(I::from_string(&(time_value.round() as i64).to_string()));
284
285 Ok(Self::new_with_zone(time_integer, calclock.zone().clone()))
286 }
287}
288
289impl<I: TimeInteger> PartialEq for TimeIndex<I> {
290 fn eq(&self, other: &Self) -> bool {
291 self.time == other.time
292 }
293}
294
295impl<I: TimeInteger> Eq for TimeIndex<I> {}
296
297impl<I: TimeInteger> PartialOrd for TimeIndex<I> {
298 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
299 Some(self.cmp(other))
300 }
301}
302
303impl<I: TimeInteger> Ord for TimeIndex<I> {
304 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
305 self.time.cmp(&other.time)
306 }
307}
308
309impl<I: TimeInteger> std::fmt::Display for TimeIndex<I> {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 write!(f, "TimeIndex({})", self.time.to_string_with_commas())
312 }
313}
314
315#[derive(Debug, Clone)]
316pub struct TimeIndexDuration<I: TimeInteger> {
317 value: I,
318}
319
320impl<I: TimeInteger> TimeIndexDuration<I> {
321 pub fn new(value: I) -> Self {
322 Self { value }
323 }
324
325 pub fn from_indices(start: &TimeIndex<I>, finish: &TimeIndex<I>) -> Outcome<Self> {
326 let duration = res!(finish.time.clone().subtract_it(start.time.clone()));
327 Ok(Self::new(duration))
328 }
329
330 pub fn value(&self) -> &I {
331 &self.value
332 }
333
334 pub fn plus(&self, other: &Self) -> Outcome<Self> {
335 let result = res!(self.value.clone().add_to(other.value.clone()));
336 Ok(Self::new(result))
337 }
338
339 pub fn minus(&self, other: &Self) -> Outcome<Self> {
340 let result = res!(self.value.clone().subtract_it(other.value.clone()));
341 Ok(Self::new(result))
342 }
343
344 pub fn multiply_by(&self, scale: i32) -> Self {
345 let result = self.value.clone().multiply_by_long(scale as i64);
346 Self::new(result)
347 }
348
349 pub fn divide_by(&self, divisor: i32) -> Outcome<Self> {
350 if divisor == 0 {
351 return Err(err!("Division by zero"; Invalid, Input));
352 }
353 let result = self.value.clone().divide_by_long(divisor as i64);
354 Ok(Self::new(result))
355 }
356
357 pub fn is_zero(&self) -> bool {
358 self.value.is_zero()
359 }
360
361 pub fn is_positive(&self) -> bool {
362 self.value.is_positive()
363 }
364
365 pub fn negate(&self) -> Self {
366 Self::new(self.value.clone().negate())
367 }
368}
369
370impl<I: TimeInteger> std::fmt::Display for TimeIndexDuration<I> {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 write!(f, "Duration({})", self.value.to_string_with_commas())
373 }
374}
375
376#[derive(Debug, Clone)]
377pub struct TimeIndexInterval<I: TimeInteger> {
378 start: TimeIndex<I>,
379 finish: TimeIndex<I>,
380 duration: TimeIndexDuration<I>,
381}
382
383impl<I: TimeInteger> TimeIndexInterval<I> {
384 pub fn new(start: TimeIndex<I>, finish: TimeIndex<I>) -> Outcome<Self> {
385 let duration = res!(TimeIndexDuration::from_indices(&start, &finish));
386 Ok(Self { start, finish, duration })
387 }
388
389 pub fn start(&self) -> &TimeIndex<I> {
390 &self.start
391 }
392
393 pub fn finish(&self) -> &TimeIndex<I> {
394 &self.finish
395 }
396
397 pub fn duration(&self) -> &TimeIndexDuration<I> {
398 &self.duration
399 }
400
401 /// Both bounds are inclusive.
402 pub fn contains(&self, time: &TimeIndex<I>) -> bool {
403 time.time >= self.start.time && time.time <= self.finish.time
404 }
405
406 pub fn overlaps(&self, other: &Self) -> bool {
407 self.start.time <= other.finish.time && self.finish.time >= other.start.time
408 }
409}
410
411impl<I: TimeInteger> std::fmt::Display for TimeIndexInterval<I> {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 write!(f, "Interval[{} to {}]", self.start, self.finish)
414 }
415}