deep_time/time_parts/
from_ccsds_str.rs1use crate::{DtErrKind, DtErr, TimeParts, an_err};
2
3impl TimeParts {
4 pub fn from_ccsds_str(input: &str) -> Result<Self, DtErr> {
8 let cleaned = input.trim_end_matches(|c: char| c.to_ascii_uppercase() == 'Z');
9 let bytes = cleaned.as_bytes();
10 let len_ = bytes.len();
11
12 let mut fmt_buf: [u8; 64] = [0; 64];
13 let mut fmt_len: usize = 0;
14 let mut pos: usize = 0;
15
16 if pos + 4 > len_ || !bytes[pos..pos + 4].iter().all(|&b| b.is_ascii_digit()) {
18 return Err(an_err!(DtErrKind::ExpectedValue, "4-digit year"));
19 }
20 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%Y");
21 fmt_len += 2;
22 pos += 4;
23
24 if pos < len_ && !bytes[pos].is_ascii_digit() {
26 fmt_buf[fmt_len] = bytes[pos];
27 fmt_len += 1;
28 pos += 1;
29 }
30
31 let is_doy =
33 pos + 3 == len_ || (pos + 3 < len_ && matches!(bytes[pos + 3], b' ' | b'T' | b't'));
34
35 if is_doy {
36 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%j");
37 fmt_len += 2;
38 pos += 3;
39 } else {
40 if pos + 2 > len_ || !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
42 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit month"));
43 }
44 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%m");
45 fmt_len += 2;
46 pos += 2;
47
48 if pos < len_ && !bytes[pos].is_ascii_digit() {
49 fmt_buf[fmt_len] = bytes[pos];
50 fmt_len += 1;
51 pos += 1;
52 }
53
54 if pos + 2 > len_ || !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
56 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit day"));
57 }
58 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%d");
59 fmt_len += 2;
60 pos += 2;
61 }
62
63 if pos < len_ {
65 let c = bytes[pos];
66 if matches!(c, b'T' | b't' | b' ') {
67 fmt_buf[fmt_len] = c;
68 fmt_len += 1;
69 pos += 1;
70 } else {
71 return Err(an_err!(
72 DtErrKind::InvalidSyntax,
73 "expected T/t/space separator"
74 ));
75 }
76 }
77
78 if pos + 2 <= len_ {
81 if !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
82 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit hour"));
83 }
84 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%H");
85 fmt_len += 2;
86 pos += 2;
87 }
88
89 if pos < len_ && !bytes[pos].is_ascii_digit() {
90 fmt_buf[fmt_len] = bytes[pos];
91 fmt_len += 1;
92 pos += 1;
93 }
94
95 if pos + 2 <= len_ {
96 if !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
97 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit minute"));
98 }
99 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%M");
100 fmt_len += 2;
101 pos += 2;
102 }
103
104 if pos < len_ && !bytes[pos].is_ascii_digit() {
105 fmt_buf[fmt_len] = bytes[pos];
106 fmt_len += 1;
107 pos += 1;
108 }
109
110 if pos + 2 <= len_ {
111 if !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
112 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit second"));
113 }
114 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%S");
115 fmt_len += 2;
116 pos += 2;
117 }
118
119 if pos < len_ {
121 if bytes[pos] == b'.' {
122 fmt_buf[fmt_len..fmt_len + 3].copy_from_slice(b"%.f");
123 fmt_len += 3;
124 pos += 1;
125 } else {
126 fmt_buf[fmt_len..fmt_len + 2].copy_from_slice(b"%f");
127 fmt_len += 2;
128 }
129 while pos < len_ && bytes[pos].is_ascii_digit() {
130 pos += 1;
131 }
132 }
133
134 let format = match core::str::from_utf8(&fmt_buf[0..fmt_len]) {
135 Ok(f) => f,
136 Err(_) => {
137 return Err(an_err!(DtErrKind::InvalidBytes, "from utf8"));
138 }
139 };
140
141 TimeParts::from_str(format, cleaned, false, false, false)
142 }
143}