1use crate::{
12 display::Quotable,
13 extendedbigdecimal::ExtendedBigDecimal,
14 parser::num_parser::{self, ExtendedParserError, ParseTarget},
15};
16use num_traits::ToPrimitive;
17use num_traits::Zero;
18use num_traits::{FromPrimitive, Signed};
19use std::time::Duration;
20
21pub fn from_str(string: &str, allow_suffixes: bool) -> Result<Duration, String> {
57 const NANOSECOND_DURATION: Duration = Duration::from_nanos(1);
60
61 let len = string.len();
62 if len == 0 {
63 return Err(format!("invalid time interval {}", string.quote()));
64 }
65 let num = match num_parser::parse(
66 string,
67 ParseTarget::Duration,
68 if allow_suffixes {
69 &[('s', 1), ('m', 60), ('h', 60 * 60), ('d', 60 * 60 * 24)]
70 } else {
71 &[]
72 },
73 ) {
74 Ok(ebd) | Err(ExtendedParserError::Overflow(ebd)) => ebd,
75 Err(ExtendedParserError::Underflow(_)) => return Ok(NANOSECOND_DURATION),
76 _ => {
77 return Err(format!("invalid time interval {}", string.quote()));
78 }
79 };
80
81 let num = match num {
83 ExtendedBigDecimal::BigDecimal(bd) if !bd.is_negative() => {
84 if bd.fractional_digit_count() <= -20 {
85 return Ok(Duration::MAX);
88 }
89 if !bd.is_zero() && bd < bigdecimal::BigDecimal::from_f64(0.0000000001).unwrap() {
91 return Ok(NANOSECOND_DURATION);
92 }
93 bd
94 }
95 ExtendedBigDecimal::MinusZero => 0.into(),
96 ExtendedBigDecimal::Infinity => return Ok(Duration::MAX),
97 _ => return Err(format!("invalid time interval {}", string.quote())),
98 };
99
100 let (nanos_bi, _) = num.with_scale(9).into_bigint_and_scale();
102
103 if nanos_bi.is_zero() && !num.is_zero() {
105 return Ok(NANOSECOND_DURATION);
106 }
107
108 const NANOS_PER_SEC: u32 = 1_000_000_000;
109 let whole_secs: u64 = match (&nanos_bi / NANOS_PER_SEC).try_into() {
110 Ok(whole_secs) => whole_secs,
111 Err(_) => return Ok(Duration::MAX),
112 };
113 let nanos: u32 = (&nanos_bi % NANOS_PER_SEC).to_u32().unwrap();
114 Ok(Duration::new(whole_secs, nanos))
115}
116
117#[cfg(test)]
118mod tests {
119
120 use crate::parser::parse_time::from_str;
121 use std::time::Duration;
122
123 #[test]
124 fn test_no_units() {
125 assert_eq!(from_str("123", true), Ok(Duration::from_secs(123)));
126 assert_eq!(from_str("123", false), Ok(Duration::from_secs(123)));
127 }
128
129 #[test]
130 fn test_units() {
131 assert_eq!(
132 from_str("2d", true),
133 Ok(Duration::from_secs(60 * 60 * 24 * 2))
134 );
135 assert!(from_str("2d", false).is_err());
136 }
137
138 #[test]
139 fn test_overflow() {
140 assert_eq!(from_str("9223372036854775808d", true), Ok(Duration::MAX));
142 assert_eq!(from_str("1e6666666666668320", true), Ok(Duration::MAX));
143 assert_eq!(from_str("1e92233720368547758080", false), Ok(Duration::MAX));
145 assert_eq!(from_str("1e92233720368547758080", false), Ok(Duration::MAX));
146 }
147
148 #[test]
149 fn test_underflow() {
150 const NANOSECOND_DURATION: Duration = Duration::from_nanos(1);
153
154 assert_eq!(
156 from_str("1e-92233720368547758080", true),
157 Ok(NANOSECOND_DURATION)
158 );
159 assert_eq!(from_str("0.0000000001", true), Ok(NANOSECOND_DURATION));
161 assert_eq!(from_str("1e-10", true), Ok(NANOSECOND_DURATION));
162 assert_eq!(from_str("9e-10", true), Ok(NANOSECOND_DURATION));
163 assert_eq!(from_str("1e-9", true), Ok(NANOSECOND_DURATION));
164 assert_eq!(from_str("1.9e-9", true), Ok(NANOSECOND_DURATION));
165 assert_eq!(from_str("2e-9", true), Ok(Duration::from_nanos(2)));
166
167 assert_eq!(
169 from_str("1e-92233720368547758080", false),
170 Ok(NANOSECOND_DURATION)
171 );
172 assert_eq!(
173 from_str("0x6p-4376646810043701", false),
174 Ok(NANOSECOND_DURATION)
175 );
176 assert_eq!(from_str("0.0000000001", false), Ok(NANOSECOND_DURATION));
178 assert_eq!(from_str("1e-10", false), Ok(NANOSECOND_DURATION));
179 assert_eq!(from_str("9e-10", false), Ok(NANOSECOND_DURATION));
180 assert_eq!(from_str("1e-9", false), Ok(NANOSECOND_DURATION));
181 assert_eq!(from_str("1.9e-9", false), Ok(NANOSECOND_DURATION));
182 assert_eq!(from_str("2e-9", false), Ok(Duration::from_nanos(2)));
183 }
184
185 #[test]
186 fn test_zero() {
187 assert_eq!(from_str("0e-9", true), Ok(Duration::ZERO));
188 assert_eq!(from_str("0e-100", true), Ok(Duration::ZERO));
189 assert_eq!(
190 from_str("0e-92233720368547758080", true),
191 Ok(Duration::ZERO)
192 );
193 assert_eq!(
194 from_str("0.000000000000000000000", true),
195 Ok(Duration::ZERO)
196 );
197
198 assert_eq!(from_str("0e-9", false), Ok(Duration::ZERO));
199 assert_eq!(from_str("0e-100", false), Ok(Duration::ZERO));
200 assert_eq!(
201 from_str("0e-92233720368547758080", false),
202 Ok(Duration::ZERO)
203 );
204 assert_eq!(
205 from_str("0.000000000000000000000", false),
206 Ok(Duration::ZERO)
207 );
208 }
209
210 #[test]
211 fn test_hex_float() {
212 assert_eq!(
213 from_str("0x1.1p-1", true),
214 Ok(Duration::from_secs_f64(0.53125f64))
215 );
216 assert_eq!(
217 from_str("0x1.1p-1", false),
218 Ok(Duration::from_secs_f64(0.53125f64))
219 );
220 assert_eq!(
221 from_str("0x1.1p-1d", true),
222 Ok(Duration::from_secs_f64(0.53125f64 * 3600.0 * 24.0))
223 );
224 assert_eq!(from_str("0xfh", true), Ok(Duration::from_secs(15 * 3600)));
225 }
226
227 #[test]
228 fn test_error_empty() {
229 assert!(from_str("", true).is_err());
230 assert!(from_str("", false).is_err());
231 }
232
233 #[test]
234 fn test_error_invalid_unit() {
235 assert!(from_str("123X", true).is_err());
236 assert!(from_str("123X", false).is_err());
237 }
238
239 #[test]
240 fn test_error_multi_bytes_characters() {
241 assert!(from_str("10€", true).is_err());
242 assert!(from_str("10€", false).is_err());
243 }
244
245 #[test]
246 fn test_error_invalid_magnitude() {
247 assert!(from_str("12abc3s", true).is_err());
248 assert!(from_str("12abc3s", false).is_err());
249 }
250
251 #[test]
252 fn test_error_only_point() {
253 assert!(from_str(".", true).is_err());
254 assert!(from_str(".", false).is_err());
255 }
256
257 #[test]
258 fn test_negative() {
259 assert!(from_str("-1", true).is_err());
260 assert!(from_str("-1", false).is_err());
261 }
262
263 #[test]
264 fn test_infinity() {
265 assert_eq!(from_str("inf", true), Ok(Duration::MAX));
266 assert_eq!(from_str("infinity", true), Ok(Duration::MAX));
267 assert_eq!(from_str("infinityh", true), Ok(Duration::MAX));
268 assert_eq!(from_str("INF", true), Ok(Duration::MAX));
269 assert_eq!(from_str("INFs", true), Ok(Duration::MAX));
270
271 assert_eq!(from_str("inf", false), Ok(Duration::MAX));
272 assert_eq!(from_str("infinity", false), Ok(Duration::MAX));
273 assert_eq!(from_str("INF", false), Ok(Duration::MAX));
274 }
275
276 #[test]
277 fn test_nan() {
278 assert!(from_str("nan", true).is_err());
279 assert!(from_str("nans", true).is_err());
280 assert!(from_str("-nanh", true).is_err());
281 assert!(from_str("NAN", true).is_err());
282 assert!(from_str("-NAN", true).is_err());
283
284 assert!(from_str("nan", false).is_err());
285 assert!(from_str("NAN", false).is_err());
286 assert!(from_str("-NAN", false).is_err());
287 }
288
289 #[test]
291 fn test_no_capital_letters() {
292 assert!(from_str("1S", true).is_err());
293 assert!(from_str("1M", true).is_err());
294 assert!(from_str("1H", true).is_err());
295 assert!(from_str("1D", true).is_err());
296 assert!(from_str("INFD", true).is_err());
297 }
298}