Skip to main content

sysd_manager_comcontroler/
time_handling.rs

1use chrono::{DateTime, Local, TimeDelta, TimeZone, Utc};
2use gettextrs::pgettext;
3use glib;
4use strum::EnumIter;
5
6use std::{
7    ffi::CStr,
8    fmt::{Display, Write},
9};
10
11#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumIter)]
12pub enum TimestampStyle {
13    Pretty,
14    PrettyUsec,
15    Utc,
16    UtcUsec,
17    Unix,
18    UnixUsec,
19}
20
21impl TimestampStyle {
22    pub fn code(&self) -> &str {
23        match self {
24            TimestampStyle::Pretty => "Pretty",
25            TimestampStyle::PrettyUsec => "Pretty usec",
26            TimestampStyle::Utc => "UTC",
27            TimestampStyle::UtcUsec => "UTC usec",
28            TimestampStyle::Unix => "Unix",
29            TimestampStyle::UnixUsec => "Unix usec",
30        }
31    }
32
33    pub fn label(&self) -> String {
34        match self {
35            //time style option
36            TimestampStyle::Pretty => pgettext("pref time style", "Pretty"),
37            //time style option
38            TimestampStyle::PrettyUsec => pgettext("pref time style", "Pretty usec"),
39            //time style option
40            TimestampStyle::Utc => pgettext("pref time style", "UTC"),
41            //time style option
42            TimestampStyle::UtcUsec => pgettext("pref time style", "UTC usec"),
43            //time style option
44            TimestampStyle::Unix => pgettext("pref time style", "Unix"),
45            //time style option
46            TimestampStyle::UnixUsec => pgettext("pref time style", "Unix usec"),
47        }
48    }
49
50    pub fn details(&self) -> String {
51        match self {
52            //time style option tooltip
53            TimestampStyle::Pretty => pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS TZ"),
54
55            //time style option tooltip
56            TimestampStyle::PrettyUsec => {
57                pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS.000000 TZ")
58            }
59
60            //time style option tooltip
61            TimestampStyle::Utc => pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS UTC"),
62
63            //time style option tooltip
64            TimestampStyle::UtcUsec => {
65                pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS.000000 UTC")
66            }
67
68            //time style option tooltip
69            TimestampStyle::Unix => pgettext("pref time style", "Seconds since the epoch"),
70
71            //time style option tooltip
72            TimestampStyle::UnixUsec => {
73                pgettext("pref time style", "Micro seconds since the epoch")
74            }
75        }
76    }
77
78    pub fn usec_formated(&self, timestamp_usec: u64) -> String {
79        match self {
80            TimestampStyle::Pretty => pretty(timestamp_usec, "%a, %d %b %Y %H:%M:%S"),
81            TimestampStyle::PrettyUsec => pretty(timestamp_usec, "%a, %d %b %Y %H:%M:%S%.6f"),
82            TimestampStyle::Utc => {
83                let since_local = get_date_utc(timestamp_usec);
84                since_local.format("%a, %d %b %Y %H:%M:%S %Z").to_string()
85            }
86            TimestampStyle::UtcUsec => {
87                let since_local = get_date_utc(timestamp_usec);
88                since_local
89                    .format("%a, %d %b %Y %H:%M:%S%.6f %Z")
90                    .to_string()
91            }
92            TimestampStyle::Unix => {
93                let timestamp_sec = timestamp_usec / USEC_PER_SEC;
94                format!("@{timestamp_sec}")
95            }
96            TimestampStyle::UnixUsec => {
97                format!("@{timestamp_usec}")
98            }
99        }
100    }
101}
102
103impl Display for TimestampStyle {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        f.write_str(&self.label())
106    }
107}
108
109impl From<glib::GString> for TimestampStyle {
110    fn from(level: glib::GString) -> Self {
111        level.as_str().into()
112    }
113}
114
115impl From<&str> for TimestampStyle {
116    fn from(style: &str) -> Self {
117        match style {
118            "UTC" => TimestampStyle::Utc,
119            "UTC usec" => TimestampStyle::UtcUsec,
120            "Unix" => TimestampStyle::Unix,
121            "Unix usec" => TimestampStyle::UnixUsec,
122            "Pretty usec" => TimestampStyle::PrettyUsec,
123            _ => TimestampStyle::Pretty,
124        }
125    }
126}
127
128impl From<i32> for TimestampStyle {
129    fn from(style: i32) -> Self {
130        match style {
131            1 => TimestampStyle::PrettyUsec,
132            2 => TimestampStyle::Utc,
133            3 => TimestampStyle::UtcUsec,
134            4 => TimestampStyle::Unix,
135            5 => TimestampStyle::UnixUsec,
136            _ => TimestampStyle::Pretty,
137        }
138    }
139}
140
141pub fn get_since_and_passed_time(
142    timestamp_usec: u64,
143    timestamp_style: TimestampStyle,
144) -> (String, String) {
145    let since = get_since_time(timestamp_usec, timestamp_style);
146
147    (since, format_timestamp_relative_full(timestamp_usec))
148}
149
150pub fn get_since_time(timestamp_usec: u64, timestamp_style: TimestampStyle) -> String {
151    timestamp_style.usec_formated(timestamp_usec)
152}
153
154fn pretty(timestamp_usec: u64, format: &str) -> String {
155    let since_local = get_date_local(timestamp_usec);
156
157    let time: libc::tm = localtime_or_gmtime_usec(timestamp_usec as i64, false);
158    let time_zone = unsafe { CStr::from_ptr(time.tm_zone) };
159    let time_zone = match time_zone.to_str() {
160        Ok(s) => s,
161        Err(_e) => &since_local.format("%Z").to_string(),
162    };
163
164    let formated_time = since_local.format(format).to_string();
165    format!("{formated_time} {time_zone}")
166}
167
168fn get_date_local(timestamp_usec: u64) -> DateTime<Local> {
169    let timestamp = get_date_utc(timestamp_usec);
170    DateTime::from(timestamp)
171}
172
173fn get_date_utc(timestamp_usec: u64) -> DateTime<Utc> {
174    match Utc.timestamp_micros(timestamp_usec as i64) {
175        chrono::offset::LocalResult::Single(a) => a,
176        chrono::offset::LocalResult::Ambiguous(a, _b) => a,
177        chrono::offset::LocalResult::None => panic!("timestamp_opt None"),
178    }
179}
180
181macro_rules! plur {
182    ($num:expr, $single:expr, $plur:expr) => {{ if $num > 1 { $plur } else { $single } }};
183}
184
185macro_rules! plur_year {
186    ($num:expr) => {{ plur!($num, "year", "years") }};
187}
188
189macro_rules! plur_month {
190    ($num:expr) => {{ plur!($num, "month", "months") }};
191}
192
193macro_rules! plur_week {
194    ($num:expr) => {{ plur!($num, "week", "weeks") }};
195}
196
197macro_rules! plur_day {
198    ($num:expr) => {{ plur!($num, "day", "days") }};
199}
200
201#[macro_export]
202macro_rules! swrite {
203
204    ($out:expr, $($y:expr),+) => {
205        // Call `find_min!` on the tail `$y`
206        if let Err(e) = write!($out, $($y), +) {
207            tracing::warn!("swrite error {:?}", e);
208            //fall back
209            let s = format!($($y), +);
210            $out.push_str(&s);
211    }}
212}
213
214#[macro_export]
215macro_rules! timestamp_is_set {
216    ($t:expr) => {
217        $t > 0 && $t != u64::MAX
218    };
219}
220
221const SEC_PER_MONTH: u64 = 2629800;
222const SEC_PER_YEAR: u64 = 31_557_600;
223pub const USEC_PER_SEC: u64 = 1_000_000;
224const USEC_PER_YEAR: u64 = 31_557_600 * USEC_PER_SEC;
225const SEC_PER_DAY: u64 = 24 * SEC_PER_HOUR;
226const SEC_PER_WEEK: u64 = SEC_PER_DAY * 7;
227const SEC_PER_HOUR: u64 = 60 * 60;
228pub const SEC_PER_MINUTE: u64 = 60;
229pub const MSEC_PER_SEC: u64 = 1000;
230
231const USEC_PER_MONTH: u64 = SEC_PER_MONTH * USEC_PER_SEC;
232const USEC_PER_WEEK: u64 = SEC_PER_WEEK * USEC_PER_SEC;
233const USEC_PER_DAY: u64 = SEC_PER_DAY * USEC_PER_SEC;
234const USEC_PER_HOUR: u64 = SEC_PER_HOUR * USEC_PER_SEC;
235const USEC_PER_MINUTE: u64 = SEC_PER_MINUTE * USEC_PER_SEC;
236pub const USEC_PER_MSEC: u64 = 1000;
237pub const NSEC_PER_USEC: u64 = 1_000;
238
239pub fn format_timestamp_relative_full(timestamp_usec: u64) -> String {
240    let since_time = get_date_local(timestamp_usec);
241
242    let now = Local::now();
243
244    let delta = now.signed_duration_since(since_time);
245
246    format_timestamp_relative_full_delta(delta, true)
247}
248
249pub fn format_timestamp_relative_duration(
250    begin_timestamp_usec: u64,
251    finish_timestamp_usec: u64,
252) -> String {
253    let since_time = get_date_local(begin_timestamp_usec);
254
255    let to_time = get_date_local(finish_timestamp_usec);
256
257    let delta = to_time.signed_duration_since(since_time);
258
259    format_timestamp_relative_full_delta(delta, false)
260}
261
262///from systemd
263fn format_timestamp_relative_full_delta(delta: TimeDelta, show_suffix: bool) -> String {
264    let is_ago = delta.num_seconds() >= 0;
265    let suffix = if show_suffix {
266        if is_ago { "ago" } else { "left" }
267    } else {
268        ""
269    };
270
271    let delta = delta.abs();
272
273    let d = delta.num_seconds() as u64;
274
275    let mut out = String::with_capacity(256);
276
277    if d >= SEC_PER_YEAR {
278        let years = d / SEC_PER_YEAR;
279        let months = (d % SEC_PER_YEAR) / SEC_PER_MONTH;
280
281        swrite!(
282            out,
283            "{} {} {} {} {suffix}",
284            years,
285            plur_year!(years),
286            months,
287            plur_month!(months)
288        );
289    } else if d >= SEC_PER_MONTH {
290        let months = d / SEC_PER_MONTH;
291        let days = (d % SEC_PER_MONTH) / SEC_PER_DAY;
292
293        swrite!(
294            out,
295            "{} {} {} {} {suffix}",
296            months,
297            plur_month!(months),
298            days,
299            plur_day!(days)
300        );
301    } else if d >= SEC_PER_WEEK {
302        let weeks = d / SEC_PER_WEEK;
303        let days = (d % SEC_PER_WEEK) / SEC_PER_DAY;
304
305        swrite!(
306            out,
307            "{} {} {} {} {suffix}",
308            weeks,
309            plur_week!(weeks),
310            days,
311            plur_day!(days)
312        );
313    } else if d >= 2 * SEC_PER_DAY {
314        let days = d / SEC_PER_DAY;
315        swrite!(out, "{days} {} {suffix}", plur_day!(days));
316    } else if d >= 25 * SEC_PER_HOUR {
317        let hours = (d - SEC_PER_DAY) / SEC_PER_HOUR;
318        swrite!(out, "1 {} {hours}h {suffix}", plur_day!(1));
319    } else if d >= 6 * SEC_PER_HOUR {
320        let hours = d / SEC_PER_HOUR;
321        swrite!(out, "{hours}h {suffix}");
322    } else if d >= SEC_PER_HOUR {
323        let hours = d / SEC_PER_HOUR;
324        let mins = (d % SEC_PER_HOUR) / SEC_PER_MINUTE;
325        swrite!(out, "{hours}h {mins}min {suffix}");
326    } else if d >= 5 * SEC_PER_MINUTE {
327        let mins = d / SEC_PER_MINUTE;
328        swrite!(out, "{mins}min {suffix}");
329    } else if d >= SEC_PER_MINUTE {
330        let mins = d / SEC_PER_MINUTE;
331        let sec = d % SEC_PER_MINUTE;
332        swrite!(out, "{mins}min {sec}s {suffix}");
333    } else if d > 0 {
334        swrite!(out, "{d}s {suffix}");
335    } else if let Some(d_us) = delta.num_microseconds() {
336        let d_us = d_us as u64;
337        if d_us >= USEC_PER_MSEC {
338            let ms = d_us / USEC_PER_MSEC;
339            swrite!(out, "{ms}ms {suffix}");
340        } else if d_us > 0 {
341            swrite!(out, "{d_us}μs {suffix}");
342        } else {
343            out.push_str("now");
344        }
345    } else {
346        out.push_str("now");
347    }
348
349    out
350}
351
352pub fn now_monotonic() -> u64 {
353    now(libc::CLOCK_MONOTONIC)
354}
355
356pub fn now_realtime() -> u64 {
357    now(libc::CLOCK_REALTIME)
358}
359
360fn now(clock_id: i32) -> u64 {
361    let mut time = libc::timespec {
362        tv_sec: 0,
363        tv_nsec: 0,
364    };
365    let ret = unsafe { libc::clock_gettime(clock_id, &mut time) };
366    assert!(ret == 0);
367
368    time.tv_sec as u64 * USEC_PER_SEC + time.tv_nsec as u64 / NSEC_PER_USEC
369}
370
371pub fn format_timespan(mut duration: u64, accuracy: u64) -> String {
372    let mut out = String::with_capacity(64);
373
374    if duration == u64::MAX {
375        out.push_str("infinity");
376        return out;
377    }
378
379    if duration == 0 {
380        out.push('0');
381        return out;
382    }
383
384    const TABLE: [(&str, u64); 9] = [
385        ("y", USEC_PER_YEAR),
386        ("month", USEC_PER_MONTH),
387        ("w", USEC_PER_WEEK),
388        ("d", USEC_PER_DAY),
389        ("h", USEC_PER_HOUR),
390        ("min", USEC_PER_MINUTE),
391        ("s", USEC_PER_SEC),
392        ("ms", USEC_PER_MSEC),
393        ("μs", 1),
394    ];
395
396    let mut something = false;
397    let mut done = false;
398
399    for (suffix, unit_magnitute_in_usec) in TABLE {
400        if duration == 0 {
401            break;
402        }
403
404        if duration < accuracy && something {
405            break;
406        }
407
408        if duration < unit_magnitute_in_usec {
409            continue;
410        }
411
412        let a = duration / unit_magnitute_in_usec;
413        let mut b = duration % unit_magnitute_in_usec;
414
415        if duration < USEC_PER_MINUTE && b > 0 {
416            let mut zero_padding = 0;
417
418            let mut cc = unit_magnitute_in_usec;
419            while cc > 1 {
420                zero_padding += 1;
421                cc /= 10;
422            }
423
424            let mut cc = accuracy;
425            while cc > 1 {
426                b /= 10;
427                zero_padding -= 1;
428                cc /= 10;
429            }
430
431            if zero_padding > 0 {
432                let space_padding = if out.is_empty() { "" } else { " " };
433                swrite!(out, "{space_padding}{a}.{:0zero_padding$}{suffix}", b);
434
435                duration = 0;
436                done = true;
437            }
438        }
439
440        /* No? Then let's show it normally */
441        if !done {
442            let pad = if out.is_empty() { "" } else { " " };
443            swrite!(out, "{pad}{a}{suffix}");
444            duration = b;
445        }
446
447        something = true;
448    }
449
450    out
451}
452
453fn localtime_or_gmtime_usec(time_usec: i64, utc: bool) -> libc::tm {
454    let layout = std::alloc::Layout::new::<libc::tm>();
455
456    let time_usec_ptr: *const libc::time_t = &(time_usec as libc::time_t);
457
458    unsafe {
459        let returned_time_struct = std::alloc::alloc(layout) as *mut libc::tm;
460
461        if utc {
462            libc::gmtime_r(time_usec_ptr, returned_time_struct);
463        } else {
464            libc::localtime_r(time_usec_ptr, returned_time_struct);
465        }
466
467        *returned_time_struct
468    }
469}
470
471///from systemd
472pub fn calc_next_elapse(next_elapse_realtime: u64, next_elapse_monotonic: u64) -> u64 {
473    let now_realtime = now_realtime();
474    let now_monotonic = now_monotonic();
475
476    if timestamp_is_set!(next_elapse_monotonic) {
477        let converted = if next_elapse_monotonic > now_monotonic {
478            now_realtime + (next_elapse_monotonic - now_monotonic)
479        } else {
480            now_realtime - (now_monotonic - next_elapse_monotonic)
481        };
482
483        if timestamp_is_set!(next_elapse_realtime) {
484            converted.min(next_elapse_realtime)
485        } else {
486            converted
487        }
488    } else {
489        next_elapse_realtime
490    }
491}
492
493#[cfg(test)]
494mod tests {
495
496    use std::ffi::CStr;
497
498    use chrono::{Duration, TimeDelta};
499
500    use super::*;
501
502    #[test]
503    fn test_since() {
504        let since = get_since_and_passed_time(1727116768682604, TimestampStyle::Pretty);
505        println!("since {since:?}");
506        let since = get_since_and_passed_time(1727116768682442, TimestampStyle::Pretty);
507        println!("since {since:?}");
508        let since = get_since_and_passed_time(1727116768682435, TimestampStyle::Pretty);
509        println!("since {since:?}");
510        let since = get_since_and_passed_time(1727413184243915, TimestampStyle::Pretty);
511        println!("since {since:?}");
512    }
513
514    #[test]
515    fn test_duration() {
516        let now = Local::now();
517
518        let tomorrow_midnight = (now + Duration::days(1))
519            .date_naive()
520            .and_hms_opt(0, 0, 0)
521            .unwrap();
522
523        let tomorrow_midnight_local = tomorrow_midnight
524            .and_local_timezone(Local)
525            .earliest()
526            .unwrap();
527
528        let duration = tomorrow_midnight_local
529            .signed_duration_since(now)
530            .to_std()
531            .unwrap();
532
533        println!("Duration between {now:?} and {tomorrow_midnight:?}: {duration:?}");
534    }
535
536    #[test]
537    fn test_duration2() {
538        let prev = get_date_local(1727116768682604);
539
540        let now = Local::now();
541
542        let duration = now.signed_duration_since(prev);
543
544        println!(
545            "Duration between {:?} and {:?}: {:?}",
546            prev,
547            now,
548            duration.to_std().unwrap()
549        );
550
551        println!(
552            "{} ago",
553            format_timestamp_relative_full_delta(duration, true)
554        )
555    }
556
557    #[test]
558    fn most_significant_duration_test() {
559        let a = TimeDelta::minutes(1) + TimeDelta::seconds(30);
560        println!("{a:?}");
561        println!("{:?}", format_timestamp_relative_full_delta(a, true));
562
563        let b = TimeDelta::minutes(2);
564        println!("{b:?}");
565        println!("{:?}", format_timestamp_relative_full_delta(b, true));
566
567        let a = TimeDelta::minutes(10) + TimeDelta::seconds(30);
568        println!("{a:?}");
569        println!("{:?}", format_timestamp_relative_full_delta(a, true));
570
571        let a = TimeDelta::minutes(9) + TimeDelta::seconds(30);
572        println!("{a:?}");
573        println!("{:?}", format_timestamp_relative_full_delta(a, true));
574    }
575
576    #[test]
577    fn test_duration_() {
578        /*         ActiveEnterTimestamp	1727500436962647
579        ActiveEnterTimestampMonotonic	383605378536
580        ActiveExitTimestamp	1727501504134907
581        ActiveExitTimestampMonotonic	384672550797 */
582
583        let enter = get_date_local(1727500436962647);
584        let exit = get_date_local(1727501504134907);
585
586        let d = exit.signed_duration_since(enter);
587        println!(
588            "{:?} {:?}",
589            format_timestamp_relative_full_delta(d, true),
590            d
591        );
592    }
593
594    #[test]
595    fn test_format_timestamp_relative_full() {
596        //println!("{} - {}", now_realtime(), (USEC_PER_YEAR + USEC_PER_MONTH));
597        const SEC_PER_YEAR_I: i64 = SEC_PER_YEAR as i64;
598        const SEC_PER_MONTH_I: i64 = SEC_PER_MONTH as i64;
599        const SEC_PER_DAY_I: i64 = SEC_PER_DAY as i64;
600        const SEC_PER_WEEK_I: i64 = SEC_PER_WEEK as i64;
601
602        const USEC_PER_SEC: i64 = 1_000_000;
603        const USEC_PER_YEAR_I: i64 = SEC_PER_YEAR_I * USEC_PER_SEC;
604        const USEC_PER_MONTH: i64 = SEC_PER_MONTH_I * USEC_PER_SEC;
605        const USEC_PER_DAY: i64 = SEC_PER_DAY_I * USEC_PER_SEC;
606        const USEC_PER_WEEK: i64 = SEC_PER_WEEK_I * USEC_PER_SEC;
607
608        let tests: Vec<(i64, &str)> = vec![
609            ((USEC_PER_YEAR_I + USEC_PER_MONTH), "1 year 1 month ago"),
610            (
611                -(USEC_PER_YEAR_I + (1.5 * USEC_PER_MONTH as f64) as i64),
612                "1 year 1 month left",
613            ),
614            (
615                (USEC_PER_YEAR_I + (2 * USEC_PER_MONTH)),
616                "1 year 2 months ago",
617            ),
618            (
619                (2 * USEC_PER_YEAR_I + USEC_PER_MONTH),
620                "2 years 1 month ago",
621            ),
622            (
623                (2 * USEC_PER_YEAR_I + 2 * USEC_PER_MONTH),
624                "2 years 2 months ago",
625            ),
626            ((USEC_PER_MONTH + USEC_PER_DAY), "1 month 1 day ago"),
627            ((USEC_PER_MONTH + 2 * USEC_PER_DAY), "1 month 2 days ago"),
628            ((2 * USEC_PER_MONTH + USEC_PER_DAY), "2 months 1 day ago"),
629            (
630                (2 * USEC_PER_MONTH + 2 * USEC_PER_DAY),
631                "2 months 2 days ago",
632            ),
633            /* Weeks and days */
634            ((USEC_PER_WEEK + USEC_PER_DAY), "1 week 1 day ago"),
635            ((USEC_PER_WEEK + 2 * USEC_PER_DAY), "1 week 2 days ago"),
636            ((2 * USEC_PER_WEEK + USEC_PER_DAY), "2 weeks 1 day ago"),
637            ((2 * USEC_PER_WEEK + 2 * USEC_PER_DAY), "2 weeks 2 days ago"),
638            (3 * 1000, "3ms ago"),
639            (2, "2μs ago"),
640            (0, "now"),
641        ];
642
643        for (time_us, time_output) in tests {
644            let delta = TimeDelta::new(
645                time_us / USEC_PER_SEC,
646                (time_us % USEC_PER_SEC) as u32 * 1000,
647            )
648            .expect("Time delta not supposed to have bondary issues");
649
650            let value = format_timestamp_relative_full_delta(delta, true);
651            assert_eq!(value, time_output);
652        }
653    }
654
655    #[test]
656    fn test_format_timespan() {
657        println!("{:?}", format_timespan(4 * USEC_PER_YEAR, MSEC_PER_SEC));
658        println!("{:?}", format_timespan(4 * USEC_PER_MONTH, MSEC_PER_SEC));
659        println!("{:?}", format_timespan(4 * USEC_PER_WEEK, MSEC_PER_SEC));
660        println!("{:?}", format_timespan(4 * USEC_PER_DAY, MSEC_PER_SEC));
661        println!("{:?}", format_timespan(4 * USEC_PER_HOUR, MSEC_PER_SEC));
662        println!("{:?}", format_timespan(4 * USEC_PER_MINUTE, MSEC_PER_SEC));
663        println!("{:?}", format_timespan(4 * USEC_PER_SEC, MSEC_PER_SEC));
664        println!("{:?}", format_timespan(4 * USEC_PER_MSEC, MSEC_PER_SEC));
665
666        println!(
667            "{:?}",
668            format_timespan(
669                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC,
670                MSEC_PER_SEC
671            )
672        );
673        println!(
674            "{:?}",
675            format_timespan(
676                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 4 * USEC_PER_MSEC,
677                MSEC_PER_SEC
678            )
679        );
680        println!(
681            "{:?}",
682            format_timespan(
683                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 4 * USEC_PER_MSEC + 4,
684                MSEC_PER_SEC
685            )
686        );
687
688        println!(
689            "{:?}",
690            format_timespan(
691                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 50 * USEC_PER_MSEC,
692                MSEC_PER_SEC
693            )
694        );
695    }
696
697    #[test]
698    fn test_localtime_or_gmtime_usec() {
699        let asdf = localtime_or_gmtime_usec(0, false);
700        println!("time {asdf:?}");
701
702        let c_str = unsafe { CStr::from_ptr(asdf.tm_zone) };
703        println!("time zone {c_str:?}");
704
705        let asdf = localtime_or_gmtime_usec(0, true);
706        println!("time {asdf:?}");
707
708        let c_str = unsafe { CStr::from_ptr(asdf.tm_zone) };
709        println!("time zone {c_str:?}");
710    }
711
712    #[test]
713    fn test_date_format() {
714        let date = Local::now();
715
716        //%Z: Since chrono is not aware of timezones beyond their offsets, this specifier only prints the offset when used for formatting. The timezone abbreviation will NOT be printed. See this issue for more information.
717        println!("{}", date.format("%a, %d %b %Y %H:%M:%S %Z"));
718        println!("{}", date.to_rfc2822());
719        println!("{}", date.to_rfc3339());
720
721        let date = Utc::now();
722
723        //%Z: Since chrono is not aware of timezones beyond their offsets, this specifier only prints the offset when used for formatting. The timezone abbreviation will NOT be printed. See this issue for more information.
724        println!("{}", date.format("%a, %d %b %Y %H:%M:%S %Z"));
725        println!("{}", date.to_rfc2822());
726        println!("{}", date.to_rfc3339());
727    }
728
729    #[test]
730    fn test_casting() {
731        let a: i64 = 0x0000_FFFF_FFFF_FFFF;
732
733        let b: i32 = a as i32;
734
735        println!("a {a:#x} b {b:#x}");
736    }
737}