deep_time/time_parts/
from_str_ccsds.rs1use crate::{DtErr, DtErrKind, Scale, TimeParts, an_err};
2
3impl TimeParts {
4 pub fn from_str_ccsds(input: &str) -> Result<Self, DtErr> {
9 let bytes = input.as_bytes();
10 let len_ = bytes.len();
11
12 let mut start = 0usize;
13 while start < len_ {
14 let b = bytes[start];
15 if b.is_ascii_digit()
16 || (matches!(b, b'+' | b'-')
17 && start + 1 < len_
18 && bytes[start + 1].is_ascii_digit())
19 {
20 break;
21 }
22 start += 1;
23 }
24
25 if start == len_ {
26 return Err(an_err!(
27 DtErrKind::ExpectedValue,
28 "year start (digit or +/- and digit)"
29 ));
30 }
31
32 let input = &input[start..];
33 let bytes = input.as_bytes();
34 let len_ = bytes.len();
35 let mut pos: usize = 0;
36 let mut tp = TimeParts::new_utc();
37
38 let mut year: i64 = 0;
40 let negative_year = pos < len_ && bytes[pos] == b'-';
41
42 if pos < len_ && matches!(bytes[pos], b'+' | b'-') {
43 pos += 1;
44 }
45
46 let mut has_year_digit = false;
47 while pos < len_ && bytes[pos].is_ascii_digit() {
48 has_year_digit = true;
49 year = year * 10 + (bytes[pos] - b'0') as i64;
50 pos += 1;
51 }
52 if !has_year_digit {
53 return Err(an_err!(
54 DtErrKind::ExpectedValue,
55 "year (digits after optional sign)"
56 ));
57 }
58 if negative_year {
59 year = -year;
60 }
61 tp.yr = Some(year);
62
63 if pos < len_ && !bytes[pos].is_ascii_digit() {
65 pos += 1;
66 }
67
68 let is_doy = pos + 3 == len_ || (pos + 3 < len_ && !bytes[pos + 3].is_ascii_digit());
70
71 if is_doy {
72 if pos + 3 > len_ || !bytes[pos..pos + 3].iter().all(|&b| b.is_ascii_digit()) {
74 return Err(an_err!(DtErrKind::ExpectedValue, "3-digit day of year"));
75 }
76 let mut doy: u16 = 0;
77 for _ in 0..3 {
78 doy = doy * 10 + (bytes[pos] - b'0') as u16;
79 pos += 1;
80 }
81 tp.day_of_yr = Some(doy);
82 } else {
83 if pos + 2 > len_ || !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
85 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit month"));
86 }
87 let mut mo: u8 = 0;
88 for _ in 0..2 {
89 mo = mo * 10 + (bytes[pos] - b'0');
90 pos += 1;
91 }
92 tp.mo = Some(mo);
93
94 if pos < len_ && !bytes[pos].is_ascii_digit() {
96 pos += 1;
97 }
98
99 if pos + 2 > len_ || !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
101 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit day"));
102 }
103 let mut day: u8 = 0;
104 for _ in 0..2 {
105 day = day * 10 + (bytes[pos] - b'0');
106 pos += 1;
107 }
108 tp.day = Some(day);
109 }
110
111 if pos < len_ {
113 let c = bytes[pos];
114 if !c.is_ascii_digit() {
115 if pos + 1 < len_ && bytes[pos + 1].is_ascii_digit() {
116 pos += 1;
117 }
118 } else {
119 return Err(an_err!(
120 DtErrKind::InvalidSyntax,
121 "expected time separator e.g. T"
122 ));
123 }
124 }
125
126 if pos < len_ && bytes[pos].is_ascii_digit() {
128 if pos + 2 > len_ || !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
130 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit hour"));
131 }
132 let mut hr: u8 = 0;
133 for _ in 0..2 {
134 hr = hr * 10 + (bytes[pos] - b'0');
135 pos += 1;
136 }
137 tp.hr = hr;
138
139 if pos < len_ && !bytes[pos].is_ascii_digit() {
140 pos += 1;
141 }
142
143 if pos + 2 <= len_ {
145 if !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
146 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit minute"));
147 }
148 let mut min: u8 = 0;
149 for _ in 0..2 {
150 min = min * 10 + (bytes[pos] - b'0');
151 pos += 1;
152 }
153 tp.min = min;
154 }
155
156 if pos < len_ && !bytes[pos].is_ascii_digit() {
157 pos += 1;
158 }
159
160 if pos + 2 <= len_ {
162 if !bytes[pos..pos + 2].iter().all(|&b| b.is_ascii_digit()) {
163 return Err(an_err!(DtErrKind::ExpectedValue, "2-digit second"));
164 }
165 let mut sec: u8 = 0;
166 for _ in 0..2 {
167 sec = sec * 10 + (bytes[pos] - b'0');
168 pos += 1;
169 }
170 tp.sec = sec;
171 }
172
173 if pos < len_ {
175 let has_dot = bytes[pos] == b'.';
176 if has_dot {
177 pos += 1;
178 }
179
180 if pos < len_ && bytes[pos].is_ascii_digit() {
181 let mut attos: u64 = 0;
182 let mut digits_seen: usize = 0;
183
184 while pos < len_ && bytes[pos].is_ascii_digit() {
185 if digits_seen < 18 {
186 attos = attos * 10 + (bytes[pos] - b'0') as u64;
187 digits_seen += 1;
188 }
189 pos += 1;
191 }
192
193 if digits_seen > 0 {
194 tp.attos = attos * 10u64.pow(18u32.saturating_sub(digits_seen as u32));
195 }
196 }
197 }
198
199 if pos < len_ && matches!(bytes[pos], b'Z' | b'z') {
201 pos += 1;
202 }
203 }
204
205 if pos < len_ {
207 if pos < len_ && !bytes[pos].is_ascii_alphabetic() {
208 pos += 1;
209 }
210 if pos < len_ {
211 let end = {
212 let mut i = pos;
213 while i < len_ && bytes[i].is_ascii_alphabetic() {
214 i += 1;
215 if i - pos > 8 {
216 break;
217 }
218 }
219 i
220 };
221 if let Some(sc) = Scale::from_abbrev(&input[pos..end]) {
222 tp.scale = sc;
223 }
225 }
226 }
227
228 Ok(tp)
229 }
230}