1use jiff::Zoned;
11use jiff::fmt::StdIoWrite;
12use jiff::fmt::strtime::{BrokenDownTime, Config};
13use std::io::Write;
14use std::time::{SystemTime, UNIX_EPOCH};
15
16use crate::error::{UResult, USimpleError};
17use crate::show_error;
18
19fn format_zoned<W: Write>(out: &mut W, zoned: Zoned, fmt: &str) -> UResult<()> {
21 let tm = BrokenDownTime::from(&zoned);
22 let mut out = StdIoWrite(out);
23 let config = Config::new().lenient(true);
24 tm.format_with_config(&config, fmt, &mut out)
25 .map_err(|x| USimpleError::new(1, x.to_string()))
26}
27
28pub fn system_time_to_sec(time: SystemTime) -> (i64, u32) {
30 if time > UNIX_EPOCH {
31 let d = time.duration_since(UNIX_EPOCH).unwrap();
32 (d.as_secs() as i64, d.subsec_nanos())
33 } else {
34 let d = UNIX_EPOCH.duration_since(time).unwrap();
35 (-(d.as_secs() as i64), d.subsec_nanos())
36 }
37}
38
39pub mod format {
40 pub static FULL_ISO: &str = "%Y-%m-%d %H:%M:%S.%N %z";
41 pub static LONG_ISO: &str = "%Y-%m-%d %H:%M";
42 pub static ISO: &str = "%Y-%m-%d";
43}
44
45pub enum FormatSystemTimeFallback {
47 Integer, IntegerError, Float, }
51
52pub fn format_system_time<W: Write>(
54 out: &mut W,
55 time: SystemTime,
56 fmt: &str,
57 mode: FormatSystemTimeFallback,
58) -> UResult<()> {
59 let zoned: Result<Zoned, _> = time.try_into();
60 if let Ok(zoned) = zoned {
61 format_zoned(out, zoned, fmt)
62 } else {
63 let (mut secs, mut nsecs) = system_time_to_sec(time);
70 match mode {
71 FormatSystemTimeFallback::Integer => out.write_all(secs.to_string().as_bytes())?,
72 FormatSystemTimeFallback::IntegerError => {
73 let str = secs.to_string();
74 show_error!("time '{str}' is out of range");
75 out.write_all(str.as_bytes())?;
76 }
77 FormatSystemTimeFallback::Float => {
78 if secs < 0 && nsecs != 0 {
79 secs -= 1;
80 nsecs = 1_000_000_000 - nsecs;
81 }
82 out.write_fmt(format_args!("{secs}.{nsecs:09}"))?;
83 }
84 }
85 Ok(())
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use crate::time::{FormatSystemTimeFallback, format_system_time};
92 use std::time::{Duration, UNIX_EPOCH};
93
94 #[test]
96 fn test_simple_system_time() {
97 unsafe { std::env::set_var("TZ", "UTC0") };
98
99 let time = UNIX_EPOCH;
100 let mut out = Vec::new();
101 format_system_time(
102 &mut out,
103 time,
104 "%Y-%m-%d %H:%M",
105 FormatSystemTimeFallback::Integer,
106 )
107 .expect("Formatting error.");
108 assert_eq!(String::from_utf8(out).unwrap(), "1970-01-01 00:00");
109
110 let mut out = Vec::new();
111 format_system_time(
112 &mut out,
113 time,
114 "%Y-%m-%d %H:%M:%S.%N %z",
115 FormatSystemTimeFallback::Integer,
116 )
117 .expect("Formatting error.");
118 assert_eq!(
119 String::from_utf8(out).unwrap(),
120 "1970-01-01 00:00:00.000000000 +0000"
121 );
122 }
123
124 #[test]
126 fn test_large_system_time() {
127 let time = UNIX_EPOCH + Duration::from_secs(67_768_036_191_763_200);
128 let mut out = Vec::new();
129 format_system_time(
130 &mut out,
131 time,
132 "%Y-%m-%d %H:%M",
133 FormatSystemTimeFallback::Integer,
134 )
135 .expect("Formatting error.");
136 assert_eq!(String::from_utf8(out).unwrap(), "67768036191763200");
137
138 let time = UNIX_EPOCH - Duration::from_secs(67_768_040_922_076_800);
139 let mut out = Vec::new();
140 format_system_time(
141 &mut out,
142 time,
143 "%Y-%m-%d %H:%M",
144 FormatSystemTimeFallback::Integer,
145 )
146 .expect("Formatting error.");
147 assert_eq!(String::from_utf8(out).unwrap(), "-67768040922076800");
148 }
149
150 #[test]
152 fn test_large_system_time_float() {
153 let time =
154 UNIX_EPOCH + Duration::from_secs(67_768_036_191_763_000) + Duration::from_nanos(123);
155 let mut out = Vec::new();
156 format_system_time(
157 &mut out,
158 time,
159 "%Y-%m-%d %H:%M",
160 FormatSystemTimeFallback::Float,
161 )
162 .expect("Formatting error.");
163 assert_eq!(
164 String::from_utf8(out).unwrap(),
165 "67768036191763000.000000123"
166 );
167
168 let time =
169 UNIX_EPOCH - Duration::from_secs(67_768_040_922_076_000) + Duration::from_nanos(123);
170 let mut out = Vec::new();
171 format_system_time(
172 &mut out,
173 time,
174 "%Y-%m-%d %H:%M",
175 FormatSystemTimeFallback::Float,
176 )
177 .expect("Formatting error.");
178 assert_eq!(
179 String::from_utf8(out).unwrap(),
180 "-67768040922076000.000000123"
181 );
182 }
183}