doris_rs/
epoch.rs

1//! Epoch parsing helper
2
3use crate::prelude::{Epoch, ParsingError, TimeScale};
4
5/// Formats given [Epoch] according to standard specifications.
6pub(crate) fn format(epoch: Epoch) -> String {
7    let (y, m, d, hh, mm, ss, nanos) = epoch_decompose(epoch);
8
9    format!(
10        "{:04} {:02} {:02} {:02} {:02} {:>2}.{:07}",
11        y,
12        m,
13        d,
14        hh,
15        mm,
16        ss,
17        nanos / 100,
18    )
19}
20
21/// Parses [Epoch] from string, interprated in [TimeScale]
22pub(crate) fn parse_in_timescale(content: &str, ts: TimeScale) -> Result<Epoch, ParsingError> {
23    let mut y = 0_i32;
24    let mut m = 0_u8;
25    let mut d = 0_u8;
26    let mut hh = 0_u8;
27    let mut mm = 0_u8;
28    let mut ss = 0_u8;
29    let mut ns = 0_u64;
30
31    if content.split_ascii_whitespace().count() < 6 {
32        return Err(ParsingError::EpochFormat);
33    }
34
35    for (field_index, item) in content.split_ascii_whitespace().enumerate() {
36        match field_index {
37            0 => {
38                y = item.parse::<i32>().map_err(|_| ParsingError::EpochFormat)?;
39
40                /* old RINEX problem: YY sometimes encoded on two digits */
41                if y > 79 && y <= 99 {
42                    y += 1900;
43                } else if y < 79 {
44                    y += 2000;
45                }
46            },
47            1 => {
48                m = item.parse::<u8>().map_err(|_| ParsingError::EpochFormat)?;
49            },
50            2 => {
51                d = item.parse::<u8>().map_err(|_| ParsingError::EpochFormat)?;
52            },
53            3 => {
54                hh = item.parse::<u8>().map_err(|_| ParsingError::EpochFormat)?;
55            },
56            4 => {
57                mm = item.parse::<u8>().map_err(|_| ParsingError::EpochFormat)?;
58            },
59            5 => {
60                if let Some(dot) = item.find('.') {
61                    let is_nav = item.trim().len() < 7;
62
63                    ss = item[..dot]
64                        .trim()
65                        .parse::<u8>()
66                        .map_err(|_| ParsingError::EpochFormat)?;
67
68                    let nanos = item[dot + 1..].trim();
69
70                    ns = nanos
71                        .parse::<u64>()
72                        .map_err(|_| ParsingError::EpochFormat)?;
73
74                    if is_nav {
75                        // NAV RINEX : 100ms precision
76                        ns *= 100_000_000;
77                    } else if nanos.len() != 9 {
78                        // OBS RINEX : 100ns precision
79                        ns *= 100;
80                    }
81                } else {
82                    ss = item
83                        .trim()
84                        .parse::<u8>()
85                        .map_err(|_| ParsingError::EpochFormat)?;
86                }
87            },
88            _ => {},
89        }
90    }
91
92    //println!("content \"{}\"", content); // DEBUG
93    //println!("Y {} M {} D {} HH {} MM {} SS {} NS {}", y, m, d, hh, mm, ss, ns); // DEBUG
94    match ts {
95        TimeScale::UTC => {
96            // Catch possible Hifitime panic on bad string content
97            if y == 0 {
98                return Err(ParsingError::EpochFormat);
99            }
100
101            Ok(Epoch::from_gregorian_utc(y, m, d, hh, mm, ss, ns as u32))
102        },
103        TimeScale::TAI => {
104            // Catch possible Hifitime panic on bad string content
105            if y == 0 {
106                return Err(ParsingError::EpochFormat);
107            }
108            let epoch = Epoch::from_gregorian_tai(y, m, d, hh, mm, ss, ns as u32);
109            Ok(epoch)
110        },
111        ts => {
112            // Catch possible Hifitime panic on bad string content
113            if y == 0 {
114                return Err(ParsingError::EpochFormat);
115            }
116
117            let epoch = Epoch::from_gregorian_str(&format!(
118                "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06} {}",
119                y, m, d, hh, mm, ss, ns, ts
120            ))?;
121            Ok(epoch)
122        },
123    }
124}
125
126pub(crate) fn parse_utc(s: &str) -> Result<Epoch, ParsingError> {
127    parse_in_timescale(s, TimeScale::UTC)
128}
129
130pub(crate) fn epoch_decompose(epoch: Epoch) -> (i32, u8, u8, u8, u8, u8, u32) {
131    epoch.to_gregorian(epoch.time_scale)
132}
133
134#[cfg(test)]
135mod test {
136    use super::*;
137
138    use crate::prelude::{Epoch, TimeScale};
139
140    use std::str::FromStr;
141}