1use alloc::borrow::ToOwned;
39use alloc::format;
40use alloc::string::{String, ToString};
41use core::fmt::Display;
42use core::str::FromStr;
43
44use crate::f64::consts::SECONDS_PER_ATTOSECOND;
45use crate::i64::consts::{
46 ATTOSECONDS_IN_FEMTOSECOND, ATTOSECONDS_IN_MICROSECOND, ATTOSECONDS_IN_MILLISECOND,
47 ATTOSECONDS_IN_NANOSECOND, ATTOSECONDS_IN_PICOSECOND, ATTOSECONDS_IN_SECOND,
48};
49use thiserror::Error;
50
51const FACTORS: [i64; 6] = [
52 ATTOSECONDS_IN_MILLISECOND,
53 ATTOSECONDS_IN_MICROSECOND,
54 ATTOSECONDS_IN_NANOSECOND,
55 ATTOSECONDS_IN_PICOSECOND,
56 ATTOSECONDS_IN_FEMTOSECOND,
57 1,
58];
59
60#[derive(Debug, Default, Clone, Copy)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70pub struct Subsecond([u32; 6]);
71
72impl Subsecond {
73 pub const ZERO: Self = Self::new();
75
76 pub const fn new() -> Self {
87 Self([0; 6])
88 }
89
90 pub const fn from_attoseconds(attoseconds: i64) -> Self {
111 let attoseconds_normalized = if attoseconds < 0 {
112 ATTOSECONDS_IN_SECOND + attoseconds
113 } else {
114 attoseconds
115 } as i128;
116 let mut this = Self::new();
117 this.0[0] = ((attoseconds_normalized / ATTOSECONDS_IN_MILLISECOND as i128) % 1000) as u32;
118 this.0[1] = ((attoseconds_normalized / ATTOSECONDS_IN_MICROSECOND as i128) % 1000) as u32;
119 this.0[2] = ((attoseconds_normalized / ATTOSECONDS_IN_NANOSECOND as i128) % 1000) as u32;
120 this.0[3] = ((attoseconds_normalized / ATTOSECONDS_IN_PICOSECOND as i128) % 1000) as u32;
121 this.0[4] = ((attoseconds_normalized / ATTOSECONDS_IN_FEMTOSECOND as i128) % 1000) as u32;
122 this.0[5] = (attoseconds_normalized % 1000) as u32;
123 this
124 }
125
126 pub const fn from_f64(value: f64) -> Option<Self> {
130 if !value.is_finite() {
131 return None;
132 }
133 let rem = value % 1.0;
134 let rem = if rem < 0.0 { rem + 1.0 } else { rem };
136 let scaled = rem / crate::f64::consts::SECONDS_PER_ATTOSECOND;
140 let i = scaled as i64 as f64;
141 let frac = scaled - i;
142 let rounded = if frac >= 0.5 {
143 i + 1.0
144 } else if frac <= -0.5 {
145 i - 1.0
146 } else {
147 i
148 };
149 Some(Self::from_attoseconds(rounded as i64))
150 }
151
152 pub const fn set_milliseconds(mut self, milliseconds: u32) -> Self {
166 debug_assert!(milliseconds < 1000);
167 self.0[0] = milliseconds % 1000;
168 self
169 }
170
171 pub const fn set_microseconds(mut self, microseconds: u32) -> Self {
176 debug_assert!(microseconds < 1000);
177 self.0[1] = microseconds % 1000;
178 self
179 }
180
181 pub const fn set_nanoseconds(mut self, nanoseconds: u32) -> Self {
186 debug_assert!(nanoseconds < 1000);
187 self.0[2] = nanoseconds % 1000;
188 self
189 }
190
191 pub const fn set_picoseconds(mut self, picoseconds: u32) -> Self {
196 debug_assert!(picoseconds < 1000);
197 self.0[3] = picoseconds % 1000;
198 self
199 }
200
201 pub const fn set_femtoseconds(mut self, femtoseconds: u32) -> Self {
206 debug_assert!(femtoseconds < 1000);
207 self.0[4] = femtoseconds % 1000;
208 self
209 }
210
211 pub const fn set_attoseconds(mut self, attoseconds: u32) -> Self {
216 debug_assert!(attoseconds < 1000);
217 self.0[5] = attoseconds % 1000;
218 self
219 }
220
221 pub const fn as_attoseconds(&self) -> i64 {
232 self.0[0] as i64 * FACTORS[0]
233 + self.0[1] as i64 * FACTORS[1]
234 + self.0[2] as i64 * FACTORS[2]
235 + self.0[3] as i64 * FACTORS[3]
236 + self.0[4] as i64 * FACTORS[4]
237 + self.0[5] as i64 * FACTORS[5]
238 }
239
240 pub const fn as_seconds_f64(&self) -> f64 {
251 self.as_attoseconds() as f64 * SECONDS_PER_ATTOSECOND
252 }
253
254 pub const fn milliseconds(&self) -> u32 {
258 self.0[0]
259 }
260
261 pub const fn microseconds(&self) -> u32 {
265 self.0[1]
266 }
267
268 pub const fn nanoseconds(&self) -> u32 {
272 self.0[2]
273 }
274
275 pub const fn picoseconds(&self) -> u32 {
279 self.0[3]
280 }
281
282 pub const fn femtoseconds(&self) -> u32 {
286 self.0[4]
287 }
288
289 pub const fn attoseconds(&self) -> u32 {
293 self.0[5]
294 }
295}
296
297impl Ord for Subsecond {
298 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
299 self.as_attoseconds().cmp(&other.as_attoseconds())
300 }
301}
302
303impl PartialOrd for Subsecond {
304 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
305 Some(self.cmp(other))
306 }
307}
308
309impl PartialEq for Subsecond {
310 fn eq(&self, other: &Self) -> bool {
311 self.as_attoseconds() == other.as_attoseconds()
312 }
313}
314
315impl Eq for Subsecond {}
316
317const DIGITS: usize = 18;
318
319impl Display for Subsecond {
320 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
321 "0.".fmt(f)?;
322 let mut s = self.as_attoseconds().to_string();
323 if s.len() < DIGITS {
324 s = format!("{:0>width$}", s, width = DIGITS);
325 }
326 let p = f.precision().unwrap_or(3).clamp(0, DIGITS);
327 s[0..p].fmt(f)
328 }
329}
330
331#[derive(Debug, Error)]
336#[error("could not parse subsecond from {0}")]
337pub struct SubsecondParseError(String);
338
339impl FromStr for Subsecond {
340 type Err = SubsecondParseError;
341
342 fn from_str(s: &str) -> Result<Self, Self::Err> {
343 let mut this = Self::default();
344
345 if s.is_empty() {
346 return Ok(this);
347 }
348
349 if s.chars().any(|c| !c.is_numeric()) {
350 return Err(SubsecondParseError(s.to_owned()));
351 }
352 let n = s.len();
353 if n > DIGITS {
354 return Err(SubsecondParseError(s.to_owned()));
355 }
356
357 let rem = n % 3;
358 let s = if rem != 0 {
359 let width = n + 3 - rem;
360 format!("{:0<width$}", s)
361 } else {
362 s.to_owned()
363 };
364
365 for i in (0..s.len()).step_by(3) {
366 this.0[i / 3] = s[i..i + 3].parse().unwrap();
367 }
368
369 Ok(this)
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn test_subsecond() {
379 let s = Subsecond::new()
380 .set_milliseconds(123)
381 .set_microseconds(456)
382 .set_nanoseconds(789)
383 .set_picoseconds(123)
384 .set_femtoseconds(456)
385 .set_attoseconds(789);
386
387 assert_eq!(s.as_attoseconds(), 123456789123456789);
388 assert_eq!(s.as_seconds_f64(), 0.1234567891234568);
389 assert_eq!(s.milliseconds(), 123);
390 assert_eq!(s.microseconds(), 456);
391 assert_eq!(s.nanoseconds(), 789);
392 assert_eq!(s.picoseconds(), 123);
393 assert_eq!(s.femtoseconds(), 456);
394 assert_eq!(s.attoseconds(), 789);
395 }
396
397 #[test]
398 fn test_subsecond_from_attoseconds() {
399 let s = Subsecond::from_attoseconds(123456789123456789);
400
401 assert_eq!(s.as_attoseconds(), 123456789123456789);
402 assert_eq!(s.as_seconds_f64(), 0.1234567891234568);
403 assert_eq!(s.milliseconds(), 123);
404 assert_eq!(s.microseconds(), 456);
405 assert_eq!(s.nanoseconds(), 789);
406 assert_eq!(s.picoseconds(), 123);
407 assert_eq!(s.femtoseconds(), 456);
408 assert_eq!(s.attoseconds(), 789);
409 }
410
411 #[test]
412 fn test_subsecond_display() {
413 let s = Subsecond::new()
414 .set_milliseconds(123)
415 .set_microseconds(456)
416 .set_nanoseconds(789)
417 .set_picoseconds(123)
418 .set_femtoseconds(456)
419 .set_attoseconds(789);
420
421 assert_eq!(format!("{}", s), "0.123");
422 assert_eq!(format!("{:.6}", s), "0.123456");
423 assert_eq!(format!("{:.18}", s), "0.123456789123456789");
424 }
425
426 #[test]
427 fn test_subsecond_parse() {
428 let exp = Subsecond::new().set_milliseconds(123).set_microseconds(400);
429 let act: Subsecond = "1234".parse().unwrap();
430 assert_eq!(act, exp);
431
432 let exp = Subsecond::new()
433 .set_milliseconds(123)
434 .set_microseconds(456)
435 .set_nanoseconds(789)
436 .set_picoseconds(123)
437 .set_femtoseconds(456)
438 .set_attoseconds(789);
439 let act: Subsecond = "123456789123456789".parse().unwrap();
440 assert_eq!(act, exp);
441 }
442
443 #[test]
444 #[should_panic]
445 fn test_subsecond_parse_error() {
446 "123foo".parse::<Subsecond>().unwrap();
447 }
448
449 #[test]
450 fn test_subsecond_from_attoseconds_negative() {
451 let s = Subsecond::from_attoseconds(-1);
453 assert_eq!(s.as_attoseconds(), 999999999999999999);
454 assert_eq!(s.milliseconds(), 999);
455 assert_eq!(s.microseconds(), 999);
456 assert_eq!(s.nanoseconds(), 999);
457 assert_eq!(s.picoseconds(), 999);
458 assert_eq!(s.femtoseconds(), 999);
459 assert_eq!(s.attoseconds(), 999);
460 }
461
462 #[test]
463 fn test_subsecond_from_attoseconds_zero() {
464 let s = Subsecond::from_attoseconds(0);
465 assert_eq!(s.as_attoseconds(), 0);
466 assert_eq!(s, Subsecond::ZERO);
467 }
468
469 #[test]
470 fn test_subsecond_from_attoseconds_max() {
471 let max = ATTOSECONDS_IN_SECOND - 1;
473 let s = Subsecond::from_attoseconds(max);
474 assert_eq!(s.as_attoseconds(), max);
475 assert_eq!(s.milliseconds(), 999);
476 assert_eq!(s.microseconds(), 999);
477 assert_eq!(s.nanoseconds(), 999);
478 assert_eq!(s.picoseconds(), 999);
479 assert_eq!(s.femtoseconds(), 999);
480 assert_eq!(s.attoseconds(), 999);
481 }
482
483 #[test]
484 fn test_subsecond_from_attoseconds_overflow() {
485 let s = Subsecond::from_attoseconds(ATTOSECONDS_IN_SECOND);
487 assert_eq!(s.as_attoseconds(), 0);
488
489 let s = Subsecond::from_attoseconds(ATTOSECONDS_IN_SECOND + 123);
490 assert_eq!(s.as_attoseconds(), 123);
491 }
492
493 #[test]
494 fn test_subsecond_set_methods_max_value() {
495 let s = Subsecond::new().set_milliseconds(999);
497 assert_eq!(s.milliseconds(), 999);
498
499 let s = Subsecond::new().set_microseconds(999);
500 assert_eq!(s.microseconds(), 999);
501
502 let s = Subsecond::new().set_nanoseconds(999);
503 assert_eq!(s.nanoseconds(), 999);
504 }
505
506 #[test]
507 fn test_subsecond_display_edge_cases() {
508 assert_eq!(format!("{}", Subsecond::ZERO), "0.000");
510
511 let s = Subsecond::new().set_milliseconds(123);
513 assert_eq!(format!("{:.0}", s), "");
514
515 assert_eq!(format!("{:.25}", s), "0.123000000000000000");
517 }
518
519 #[test]
520 fn test_subsecond_parse_edge_cases() {
521 let s: Subsecond = "".parse().unwrap();
523 assert_eq!(s, Subsecond::ZERO);
524
525 let s: Subsecond = "1".parse().unwrap();
527 assert_eq!(s.milliseconds(), 100);
528
529 let s: Subsecond = "12".parse().unwrap();
531 assert_eq!(s.milliseconds(), 120);
532
533 let s: Subsecond = "999999999999999999".parse().unwrap();
535 assert_eq!(s.as_attoseconds(), 999999999999999999);
536 }
537
538 #[test]
539 fn test_subsecond_parse_too_long() {
540 let result = "1234567890123456789".parse::<Subsecond>();
542 assert!(result.is_err());
543 }
544
545 #[test]
546 fn test_subsecond_ordering() {
547 let a = Subsecond::from_attoseconds(100);
548 let b = Subsecond::from_attoseconds(200);
549 let c = Subsecond::from_attoseconds(200);
550
551 assert!(a < b);
552 assert!(b > a);
553 assert_eq!(b, c);
554 assert!(b <= c);
555 assert!(b >= c);
556 }
557
558 #[test]
559 fn test_subsecond_equality() {
560 let a = Subsecond::new().set_milliseconds(123);
561 let b = Subsecond::from_attoseconds(123000000000000000);
562
563 assert_eq!(a, b);
564 assert_eq!(a.as_attoseconds(), b.as_attoseconds());
565 }
566}