1use std::time::SystemTime;
2
3use anyhow::{anyhow, bail, Result};
4use chrono::{DateTime, Local, Utc};
5use tai64::Tai64N;
6
7use crate::{
8 fp::complement,
9 text::parseutil::{
10 char_is_white, drop_n, first_rest, parse_hex, take_while,
11 },
12 time::excel::exceldays_from_unixtime,
13};
14
15pub fn parse_timestamp(s: &str) -> Result<(Tai64N, &str)> {
16 let (c0, r) = first_rest(s)
17 .ok_or_else(|| anyhow!("empty line, missing timestamp"))?;
18 if c0 != '@' {
19 bail!("line does not start with @")
20 }
21 let (stamp, rest) = take_while(r, complement(char_is_white));
22 if stamp.len() < 24 {
23 bail!("timestamp string is too short")
24 }
25 let stamp8: [u8; 12] = parse_hex(stamp)?;
26 let t = Tai64N::from_slice(&stamp8)?;
27 Ok((t, drop_n(rest, 1, char_is_white)?))
28}
29
30pub trait Tai64Format {
31 fn to_rfc2822_local(&self) -> String;
32 fn to_rfc2822_utc(&self) -> String;
33 fn to_datetime_utc(&self) -> DateTime<Utc>;
34 fn to_exceldays(&self, offset_hours: f64) -> f64;
35}
36
37impl Tai64Format for Tai64N {
38 fn to_rfc2822_local(&self) -> String {
39 let t = self.to_system_time();
40 let dt: DateTime<Local> = DateTime::from(t);
41 dt.to_rfc2822()
42 }
43
44 fn to_rfc2822_utc(&self) -> String {
45 let t = self.to_system_time();
46 let dt: DateTime<Utc> = DateTime::from(t);
47 dt.to_rfc2822()
48 }
49
50 fn to_datetime_utc(&self) -> DateTime<Utc> {
51 let t = self.to_system_time();
52 DateTime::from(t)
53 }
54
55 fn to_exceldays(&self, offset_hours: f64) -> f64 {
60 let st = self.to_system_time();
61 let t = st
62 .duration_since(SystemTime::UNIX_EPOCH)
63 .expect("no overflows?")
64 .as_secs_f64();
65 exceldays_from_unixtime(t, offset_hours)
66 }
67}