1
//! Module to generate the ASCII timecodes specified in
2
//! [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) section 3.5 .
3
//! See [chrono::DateTime::format] for a usage example of the generated
4
//! [chrono::format::DelayedFormat] structs.
5
#[cfg(all(feature = "alloc", feature = "chrono"))]
6
pub use alloc_mod_chrono::*;
7

            
8
/// Tuple of format string and formatted size for time code A.
9
///
10
/// Format: YYYY-MM-DDThh:mm:ss.ddd
11
///
12
/// Three digits are used for the decimal fraction
13
pub const FMT_STR_CODE_A_WITH_SIZE: (&str, usize) = ("%FT%T%.3f", 23);
14
/// Tuple of format string and formatted size for time code A.
15
///
16
///  Format: YYYY-MM-DDThh:mm:ss.dddZ
17
///
18
/// Three digits are used for the decimal fraction and a terminator is added at the end.
19
pub const FMT_STR_CODE_A_TERMINATED_WITH_SIZE: (&str, usize) = ("%FT%T%.3fZ", 24);
20

            
21
/// Tuple of format string and formatted size for time code A.
22
///
23
/// Format: YYYY-DDDThh:mm:ss.ddd
24
///
25
/// Three digits are used for the decimal fraction
26
pub const FMT_STR_CODE_B_WITH_SIZE: (&str, usize) = ("%Y-%jT%T%.3f", 21);
27
/// Tuple of format string and formatted size for time code A.
28
///
29
/// Format: YYYY-DDDThh:mm:ss.dddZ
30
///
31
/// Three digits are used for the decimal fraction and a terminator is added at the end.
32
pub const FMT_STR_CODE_B_TERMINATED_WITH_SIZE: (&str, usize) = ("%Y-%jT%T%.3fZ", 22);
33

            
34
#[cfg(all(feature = "alloc", feature = "chrono"))]
35
pub mod alloc_mod_chrono {
36
    use super::*;
37
    use chrono::{
38
        format::{DelayedFormat, StrftimeItems},
39
        DateTime, Utc,
40
    };
41

            
42
    /// Generates a time code formatter using the [FMT_STR_CODE_A_WITH_SIZE] format.
43
4
    pub fn generate_time_code_a(date: &DateTime<Utc>) -> DelayedFormat<StrftimeItems<'static>> {
44
4
        date.format(FMT_STR_CODE_A_WITH_SIZE.0)
45
4
    }
46

            
47
    /// Generates a time code formatter using the [FMT_STR_CODE_A_TERMINATED_WITH_SIZE] format.
48
4
    pub fn generate_time_code_a_terminated(
49
4
        date: &DateTime<Utc>,
50
4
    ) -> DelayedFormat<StrftimeItems<'static>> {
51
4
        date.format(FMT_STR_CODE_A_TERMINATED_WITH_SIZE.0)
52
4
    }
53

            
54
    /// Generates a time code formatter using the [FMT_STR_CODE_B_WITH_SIZE] format.
55
4
    pub fn generate_time_code_b(date: &DateTime<Utc>) -> DelayedFormat<StrftimeItems<'static>> {
56
4
        date.format(FMT_STR_CODE_B_WITH_SIZE.0)
57
4
    }
58

            
59
    /// Generates a time code formatter using the [FMT_STR_CODE_B_TERMINATED_WITH_SIZE] format.
60
4
    pub fn generate_time_code_b_terminated(
61
4
        date: &DateTime<Utc>,
62
4
    ) -> DelayedFormat<StrftimeItems<'static>> {
63
4
        date.format(FMT_STR_CODE_B_TERMINATED_WITH_SIZE.0)
64
4
    }
65
}
66

            
67
#[cfg(test)]
68
mod tests {
69
    use super::*;
70
    use chrono::Utc;
71
    use std::format;
72

            
73
    #[test]
74
2
    fn test_ascii_timestamp_a_unterminated_epoch() {
75
2
        let date = chrono::DateTime::UNIX_EPOCH;
76
2
        let stamp_formatter = generate_time_code_a(&date);
77
2
        let stamp = format!("{}", stamp_formatter);
78
2
        let t_sep = stamp.find('T');
79
2
        assert!(t_sep.is_some());
80
2
        assert_eq!(t_sep.unwrap(), 10);
81
2
        assert_eq!(stamp.len(), FMT_STR_CODE_A_WITH_SIZE.1);
82
2
    }
83

            
84
    #[test]
85
    #[cfg_attr(miri, ignore)]
86
2
    fn test_ascii_timestamp_a_unterminated_now() {
87
2
        let date = Utc::now();
88
2
        let stamp_formatter = generate_time_code_a(&date);
89
2
        let stamp = format!("{}", stamp_formatter);
90
2
        let t_sep = stamp.find('T');
91
2
        assert!(t_sep.is_some());
92
2
        assert_eq!(t_sep.unwrap(), 10);
93
2
        assert_eq!(stamp.len(), FMT_STR_CODE_A_WITH_SIZE.1);
94
2
    }
95

            
96
    #[test]
97
2
    fn test_ascii_timestamp_a_terminated_epoch() {
98
2
        let date = chrono::DateTime::UNIX_EPOCH;
99
2
        let stamp_formatter = generate_time_code_a_terminated(&date);
100
2
        let stamp = format!("{}", stamp_formatter);
101
2
        let t_sep = stamp.find('T');
102
2
        assert!(t_sep.is_some());
103
2
        assert_eq!(t_sep.unwrap(), 10);
104
2
        let z_terminator = stamp.find('Z');
105
2
        assert!(z_terminator.is_some());
106
2
        assert_eq!(
107
2
            z_terminator.unwrap(),
108
2
            FMT_STR_CODE_A_TERMINATED_WITH_SIZE.1 - 1
109
2
        );
110
2
        assert_eq!(stamp.len(), FMT_STR_CODE_A_TERMINATED_WITH_SIZE.1);
111
2
    }
112
    #[test]
113
    #[cfg_attr(miri, ignore)]
114
2
    fn test_ascii_timestamp_a_terminated_now() {
115
2
        let date = Utc::now();
116
2
        let stamp_formatter = generate_time_code_a_terminated(&date);
117
2
        let stamp = format!("{}", stamp_formatter);
118
2
        let t_sep = stamp.find('T');
119
2
        assert!(t_sep.is_some());
120
2
        assert_eq!(t_sep.unwrap(), 10);
121
2
        let z_terminator = stamp.find('Z');
122
2
        assert!(z_terminator.is_some());
123
2
        assert_eq!(
124
2
            z_terminator.unwrap(),
125
2
            FMT_STR_CODE_A_TERMINATED_WITH_SIZE.1 - 1
126
2
        );
127
2
        assert_eq!(stamp.len(), FMT_STR_CODE_A_TERMINATED_WITH_SIZE.1);
128
2
    }
129

            
130
    #[test]
131
2
    fn test_ascii_timestamp_b_unterminated_epoch() {
132
2
        let date = chrono::DateTime::UNIX_EPOCH;
133
2
        let stamp_formatter = generate_time_code_b(&date);
134
2
        let stamp = format!("{}", stamp_formatter);
135
2
        let t_sep = stamp.find('T');
136
2
        assert!(t_sep.is_some());
137
2
        assert_eq!(t_sep.unwrap(), 8);
138
2
        assert_eq!(stamp.len(), FMT_STR_CODE_B_WITH_SIZE.1);
139
2
    }
140

            
141
    #[test]
142
    #[cfg_attr(miri, ignore)]
143
2
    fn test_ascii_timestamp_b_unterminated_now() {
144
2
        let date = Utc::now();
145
2
        let stamp_formatter = generate_time_code_b(&date);
146
2
        let stamp = format!("{}", stamp_formatter);
147
2
        let t_sep = stamp.find('T');
148
2
        assert!(t_sep.is_some());
149
2
        assert_eq!(t_sep.unwrap(), 8);
150
2
        assert_eq!(stamp.len(), FMT_STR_CODE_B_WITH_SIZE.1);
151
2
    }
152

            
153
    #[test]
154
2
    fn test_ascii_timestamp_b_terminated_epoch() {
155
2
        let date = chrono::DateTime::UNIX_EPOCH;
156
2
        let stamp_formatter = generate_time_code_b_terminated(&date);
157
2
        let stamp = format!("{}", stamp_formatter);
158
2
        let t_sep = stamp.find('T');
159
2
        assert!(t_sep.is_some());
160
2
        assert_eq!(t_sep.unwrap(), 8);
161
2
        let z_terminator = stamp.find('Z');
162
2
        assert!(z_terminator.is_some());
163
2
        assert_eq!(
164
2
            z_terminator.unwrap(),
165
2
            FMT_STR_CODE_B_TERMINATED_WITH_SIZE.1 - 1
166
2
        );
167
2
        assert_eq!(stamp.len(), FMT_STR_CODE_B_TERMINATED_WITH_SIZE.1);
168
2
    }
169

            
170
    #[test]
171
    #[cfg_attr(miri, ignore)]
172
2
    fn test_ascii_timestamp_b_terminated_now() {
173
2
        let date = Utc::now();
174
2
        let stamp_formatter = generate_time_code_b_terminated(&date);
175
2
        let stamp = format!("{}", stamp_formatter);
176
2
        let t_sep = stamp.find('T');
177
2
        assert!(t_sep.is_some());
178
2
        assert_eq!(t_sep.unwrap(), 8);
179
2
        let z_terminator = stamp.find('Z');
180
2
        assert!(z_terminator.is_some());
181
2
        assert_eq!(
182
2
            z_terminator.unwrap(),
183
2
            FMT_STR_CODE_B_TERMINATED_WITH_SIZE.1 - 1
184
2
        );
185
2
        assert_eq!(stamp.len(), FMT_STR_CODE_B_TERMINATED_WITH_SIZE.1);
186
2
    }
187
}