nu_protocol/value/
duration.rs1use chrono::Duration;
2use std::{
3 borrow::Cow,
4 fmt::{Display, Formatter},
5};
6
7#[derive(Clone, Copy)]
8pub enum TimePeriod {
9 Nanos(i64),
10 Micros(i64),
11 Millis(i64),
12 Seconds(i64),
13 Minutes(i64),
14 Hours(i64),
15 Days(i64),
16 Weeks(i64),
17 Months(i64),
18 Years(i64),
19}
20
21impl TimePeriod {
22 pub fn to_text(self) -> Cow<'static, str> {
23 match self {
24 Self::Nanos(n) => format!("{n} ns").into(),
25 Self::Micros(n) => format!("{n} µs").into(),
26 Self::Millis(n) => format!("{n} ms").into(),
27 Self::Seconds(n) => format!("{n} sec").into(),
28 Self::Minutes(n) => format!("{n} min").into(),
29 Self::Hours(n) => format!("{n} hr").into(),
30 Self::Days(n) => format!("{n} day").into(),
31 Self::Weeks(n) => format!("{n} wk").into(),
32 Self::Months(n) => format!("{n} month").into(),
33 Self::Years(n) => format!("{n} yr").into(),
34 }
35 }
36}
37
38impl Display for TimePeriod {
39 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
40 write!(f, "{}", self.to_text())
41 }
42}
43
44pub fn format_duration(duration: i64) -> String {
45 let (sign, periods) = format_duration_as_timeperiod(duration);
46
47 let text = periods
48 .into_iter()
49 .map(|p| p.to_text().to_string().replace(' ', ""))
50 .collect::<Vec<String>>();
51
52 format!(
53 "{}{}",
54 if sign == -1 { "-" } else { "" },
55 text.join(" ").trim()
56 )
57}
58
59pub fn format_duration_as_timeperiod(duration: i64) -> (i32, Vec<TimePeriod>) {
60 let (sign, duration) = if duration >= 0 {
66 (1, duration)
67 } else {
68 (-1, -duration)
69 };
70
71 let dur = Duration::nanoseconds(duration);
72
73 fn split_weeks(duration: Duration) -> (Option<i64>, Duration) {
75 let weeks = duration.num_weeks();
76 normalize_split(weeks, Duration::try_weeks(weeks), duration)
77 }
78
79 fn split_days(duration: Duration) -> (Option<i64>, Duration) {
81 let days = duration.num_days();
82 normalize_split(days, Duration::try_days(days), duration)
83 }
84
85 fn split_hours(duration: Duration) -> (Option<i64>, Duration) {
87 let hours = duration.num_hours();
88 normalize_split(hours, Duration::try_hours(hours), duration)
89 }
90
91 fn split_minutes(duration: Duration) -> (Option<i64>, Duration) {
93 let minutes = duration.num_minutes();
94 normalize_split(minutes, Duration::try_minutes(minutes), duration)
95 }
96
97 fn split_seconds(duration: Duration) -> (Option<i64>, Duration) {
99 let seconds = duration.num_seconds();
100 normalize_split(seconds, Duration::try_seconds(seconds), duration)
101 }
102
103 fn split_milliseconds(duration: Duration) -> (Option<i64>, Duration) {
105 let millis = duration.num_milliseconds();
106 normalize_split(millis, Duration::try_milliseconds(millis), duration)
107 }
108
109 fn split_microseconds(duration: Duration) -> (Option<i64>, Duration) {
111 let micros = duration.num_microseconds().unwrap_or_default();
112 normalize_split(micros, Duration::microseconds(micros), duration)
113 }
114
115 fn split_nanoseconds(duration: Duration) -> (Option<i64>, Duration) {
117 let nanos = duration.num_nanoseconds().unwrap_or_default();
118 normalize_split(nanos, Duration::nanoseconds(nanos), duration)
119 }
120
121 fn normalize_split(
122 wholes: i64,
123 wholes_duration: impl Into<Option<Duration>>,
124 total_duration: Duration,
125 ) -> (Option<i64>, Duration) {
126 match wholes_duration.into() {
127 Some(wholes_duration) if wholes != 0 => {
128 (Some(wholes), total_duration - wholes_duration)
129 }
130 _ => (None, total_duration),
131 }
132 }
133
134 let mut periods = vec![];
135
136 let (weeks, remainder) = split_weeks(dur);
137 if let Some(weeks) = weeks {
138 periods.push(TimePeriod::Weeks(weeks));
139 }
140
141 let (days, remainder) = split_days(remainder);
142 if let Some(days) = days {
143 periods.push(TimePeriod::Days(days));
144 }
145
146 let (hours, remainder) = split_hours(remainder);
147 if let Some(hours) = hours {
148 periods.push(TimePeriod::Hours(hours));
149 }
150
151 let (minutes, remainder) = split_minutes(remainder);
152 if let Some(minutes) = minutes {
153 periods.push(TimePeriod::Minutes(minutes));
154 }
155
156 let (seconds, remainder) = split_seconds(remainder);
157 if let Some(seconds) = seconds {
158 periods.push(TimePeriod::Seconds(seconds));
159 }
160
161 let (millis, remainder) = split_milliseconds(remainder);
162 if let Some(millis) = millis {
163 periods.push(TimePeriod::Millis(millis));
164 }
165
166 let (micros, remainder) = split_microseconds(remainder);
167 if let Some(micros) = micros {
168 periods.push(TimePeriod::Micros(micros));
169 }
170
171 let (nanos, _remainder) = split_nanoseconds(remainder);
172 if let Some(nanos) = nanos {
173 periods.push(TimePeriod::Nanos(nanos));
174 }
175
176 if periods.is_empty() {
177 periods.push(TimePeriod::Seconds(0));
178 }
179
180 (sign, periods)
181}