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