egui_charts/scales/
countdown.rs1use crate::model::{BarClock, Timeframe};
4use chrono::{DateTime, Utc};
5
6pub fn bar_countdown(
10 open: DateTime<Utc>,
11 timeframe: Timeframe,
12 now: DateTime<Utc>,
13 close: Option<DateTime<Utc>>,
14) -> Option<String> {
15 if timeframe.is_trade_based() {
16 return None;
17 }
18 let end = close.or_else(|| {
19 let clock = BarClock::new(timeframe);
20 if timeframe.duration_ms() < 86_400_000 {
21 let midnight = BarClock::new(Timeframe::Day1);
22 Some(
23 open.checked_add_signed(timeframe.duration())?
24 .min(midnight.next_open(midnight.bar_open(open))?),
25 )
26 } else {
27 clock.next_open(clock.bar_open(open))
28 }
29 })?;
30 if now < open || now >= end {
31 return None;
32 }
33 let remaining = end - now;
34 let seconds = remaining.num_seconds() + i64::from(remaining.subsec_nanos() > 0);
35 let clock = if seconds >= 3600 {
36 format!(
37 "{:02}:{:02}:{:02}",
38 seconds / 3600 % 24,
39 seconds / 60 % 60,
40 seconds % 60
41 )
42 } else {
43 format!("{:02}:{:02}", seconds / 60, seconds % 60)
44 };
45 Some(if seconds >= 86_400 {
46 format!("{}d {clock}", seconds / 86_400)
47 } else {
48 clock
49 })
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn countdown_uses_calendar_boundaries_and_exact_closes() {
58 for (open, timeframe, now, close, expected) in [
59 (
60 "2024-03-01T00:00:00Z",
61 "10t".parse().unwrap(),
62 "2024-03-01T00:00:00Z",
63 Some("2024-03-01T00:01:00Z"),
64 None,
65 ),
66 (
67 "2024-02-01T00:00:00Z",
68 Timeframe::Month1,
69 "2024-02-29T23:59:59Z",
70 None,
71 Some("00:01"),
72 ),
73 (
74 "2024-02-29T23:55:00Z",
75 "7min".parse().unwrap(),
76 "2024-02-29T23:59:00Z",
77 None,
78 Some("01:00"),
79 ),
80 (
81 "2024-03-01T00:00:00Z",
82 Timeframe::Min1,
83 "2024-03-01T00:00:59.999999999Z",
84 None,
85 Some("00:01"),
86 ),
87 (
88 "2024-03-01T00:00:00Z",
89 Timeframe::Min1,
90 "2024-03-01T00:01:00Z",
91 None,
92 None,
93 ),
94 (
95 "2024-03-01T00:00:00Z",
96 Timeframe::Min1,
97 "2024-02-29T23:59:59Z",
98 None,
99 None,
100 ),
101 (
102 "2024-03-11T00:00:00Z",
103 Timeframe::Day1,
104 "2024-03-11T19:59:00Z",
105 Some("2024-03-11T20:00:00Z"),
106 Some("01:00"),
107 ),
108 (
109 "2024-03-11T00:00:00Z",
110 Timeframe::Week1,
111 "2024-03-14T19:00:00Z",
112 Some("2024-03-15T20:00:00Z"),
113 Some("1d 01:00:00"),
114 ),
115 ] {
116 assert_eq!(
117 bar_countdown(
118 open.parse().unwrap(),
119 timeframe,
120 now.parse().unwrap(),
121 close.map(|value| value.parse().unwrap())
122 )
123 .as_deref(),
124 expected,
125 "{timeframe}: {now}"
126 );
127 }
128 }
129}