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