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            log::warn!("swrite error {:?}", e);
208            //fall back
209            let s = format!($($y), +);
210            $out.push_str(&s);
211    }}
212}
213
214const SEC_PER_MONTH: u64 = 2629800;
215const SEC_PER_YEAR: u64 = 31_557_600;
216pub const USEC_PER_SEC: u64 = 1_000_000;
217const USEC_PER_YEAR: u64 = 31_557_600 * USEC_PER_SEC;
218const SEC_PER_DAY: u64 = 24 * SEC_PER_HOUR;
219const SEC_PER_WEEK: u64 = SEC_PER_DAY * 7;
220const SEC_PER_HOUR: u64 = 60 * 60;
221pub const SEC_PER_MINUTE: u64 = 60;
222pub const MSEC_PER_SEC: u64 = 1000;
223
224const USEC_PER_MONTH: u64 = SEC_PER_MONTH * USEC_PER_SEC;
225const USEC_PER_WEEK: u64 = SEC_PER_WEEK * USEC_PER_SEC;
226const USEC_PER_DAY: u64 = SEC_PER_DAY * USEC_PER_SEC;
227const USEC_PER_HOUR: u64 = SEC_PER_HOUR * USEC_PER_SEC;
228const USEC_PER_MINUTE: u64 = SEC_PER_MINUTE * USEC_PER_SEC;
229pub const USEC_PER_MSEC: u64 = 1000;
230pub const NSEC_PER_USEC: u64 = 1_000;
231
232fn format_timestamp_relative_full(timestamp_usec: u64) -> String {
233    let since_time = get_date_local(timestamp_usec);
234
235    let now = Local::now();
236
237    let delta = now.signed_duration_since(since_time);
238
239    format_timestamp_relative_full_delta(delta, true)
240}
241
242pub fn format_timestamp_relative_duration(
243    begin_timestamp_usec: u64,
244    finish_timestamp_usec: u64,
245) -> String {
246    let since_time = get_date_local(begin_timestamp_usec);
247
248    let to_time = get_date_local(finish_timestamp_usec);
249
250    let delta = to_time.signed_duration_since(since_time);
251
252    format_timestamp_relative_full_delta(delta, false)
253}
254
255///from systemd
256fn format_timestamp_relative_full_delta(delta: TimeDelta, show_suffix: bool) -> String {
257    let is_ago = delta.num_seconds() >= 0;
258    let suffix = if show_suffix {
259        if is_ago { "ago" } else { "left" }
260    } else {
261        ""
262    };
263
264    let delta = delta.abs();
265
266    let d = delta.num_seconds() as u64;
267
268    let mut out = String::with_capacity(256);
269
270    if d >= SEC_PER_YEAR {
271        let years = d / SEC_PER_YEAR;
272        let months = (d % SEC_PER_YEAR) / SEC_PER_MONTH;
273
274        swrite!(
275            out,
276            "{} {} {} {} {suffix}",
277            years,
278            plur_year!(years),
279            months,
280            plur_month!(months)
281        );
282    } else if d >= SEC_PER_MONTH {
283        let months = d / SEC_PER_MONTH;
284        let days = (d % SEC_PER_MONTH) / SEC_PER_DAY;
285
286        swrite!(
287            out,
288            "{} {} {} {} {suffix}",
289            months,
290            plur_month!(months),
291            days,
292            plur_day!(days)
293        );
294    } else if d >= SEC_PER_WEEK {
295        let weeks = d / SEC_PER_WEEK;
296        let days = (d % SEC_PER_WEEK) / SEC_PER_DAY;
297
298        swrite!(
299            out,
300            "{} {} {} {} {suffix}",
301            weeks,
302            plur_week!(weeks),
303            days,
304            plur_day!(days)
305        );
306    } else if d >= 2 * SEC_PER_DAY {
307        let days = d / SEC_PER_DAY;
308        swrite!(out, "{days} {} {suffix}", plur_day!(days));
309    } else if d >= 25 * SEC_PER_HOUR {
310        let hours = (d - SEC_PER_DAY) / SEC_PER_HOUR;
311        swrite!(out, "1 {} {hours}h {suffix}", plur_day!(1));
312    } else if d >= 6 * SEC_PER_HOUR {
313        let hours = d / SEC_PER_HOUR;
314        swrite!(out, "{hours}h {suffix}");
315    } else if d >= SEC_PER_HOUR {
316        let hours = d / SEC_PER_HOUR;
317        let mins = (d % SEC_PER_HOUR) / SEC_PER_MINUTE;
318        swrite!(out, "{hours}h {mins}min {suffix}");
319    } else if d >= 5 * SEC_PER_MINUTE {
320        let mins = d / SEC_PER_MINUTE;
321        swrite!(out, "{mins}min {suffix}");
322    } else if d >= SEC_PER_MINUTE {
323        let mins = d / SEC_PER_MINUTE;
324        let sec = d % SEC_PER_MINUTE;
325        swrite!(out, "{mins}min {sec}s {suffix}");
326    } else if d > 0 {
327        swrite!(out, "{d}s {suffix}");
328    } else if let Some(d_us) = delta.num_microseconds() {
329        let d_us = d_us as u64;
330        if d_us >= USEC_PER_MSEC {
331            let ms = d_us / USEC_PER_MSEC;
332            swrite!(out, "{ms}ms {suffix}");
333        } else if d_us > 0 {
334            swrite!(out, "{d_us}μs {suffix}");
335        } else {
336            out.push_str("now");
337        }
338    } else {
339        out.push_str("now");
340    }
341
342    out
343}
344
345pub fn now_monotonic() -> u64 {
346    now(libc::CLOCK_MONOTONIC)
347}
348
349pub fn now_realtime() -> u64 {
350    now(libc::CLOCK_REALTIME)
351}
352
353fn now(clock_id: i32) -> u64 {
354    let mut time = libc::timespec {
355        tv_sec: 0,
356        tv_nsec: 0,
357    };
358    let ret = unsafe { libc::clock_gettime(clock_id, &mut time) };
359    assert!(ret == 0);
360
361    time.tv_sec as u64 * USEC_PER_SEC + time.tv_nsec as u64 / NSEC_PER_USEC
362}
363pub const U64MAX: u64 = 18_446_744_073_709_551_615; //FIXME define only onec
364pub fn format_timespan(mut duration: u64, accuracy: u64) -> String {
365    let mut out = String::with_capacity(64);
366
367    if duration == U64MAX {
368        out.push_str("infinity");
369        return out;
370    }
371
372    if duration == 0 {
373        out.push('0');
374        return out;
375    }
376
377    const TABLE: [(&str, u64); 9] = [
378        ("y", USEC_PER_YEAR),
379        ("month", USEC_PER_MONTH),
380        ("w", USEC_PER_WEEK),
381        ("d", USEC_PER_DAY),
382        ("h", USEC_PER_HOUR),
383        ("min", USEC_PER_MINUTE),
384        ("s", USEC_PER_SEC),
385        ("ms", USEC_PER_MSEC),
386        ("μs", 1),
387    ];
388
389    let mut something = false;
390    let mut done = false;
391
392    for (suffix, unit_magnitute_in_usec) in TABLE {
393        if duration == 0 {
394            break;
395        }
396
397        if duration < accuracy && something {
398            break;
399        }
400
401        if duration < unit_magnitute_in_usec {
402            continue;
403        }
404
405        let a = duration / unit_magnitute_in_usec;
406        let mut b = duration % unit_magnitute_in_usec;
407
408        if duration < USEC_PER_MINUTE && b > 0 {
409            let mut zero_padding = 0;
410
411            let mut cc = unit_magnitute_in_usec;
412            while cc > 1 {
413                zero_padding += 1;
414                cc /= 10;
415            }
416
417            let mut cc = accuracy;
418            while cc > 1 {
419                b /= 10;
420                zero_padding -= 1;
421                cc /= 10;
422            }
423
424            if zero_padding > 0 {
425                let space_padding = if out.is_empty() { "" } else { " " };
426                swrite!(out, "{space_padding}{a}.{:0zero_padding$}{suffix}", b);
427
428                duration = 0;
429                done = true;
430            }
431        }
432
433        /* No? Then let's show it normally */
434        if !done {
435            let pad = if out.is_empty() { "" } else { " " };
436            swrite!(out, "{pad}{a}{suffix}");
437            duration = b;
438        }
439
440        something = true;
441    }
442
443    out
444}
445
446fn localtime_or_gmtime_usec(time_usec: i64, utc: bool) -> libc::tm {
447    let layout = std::alloc::Layout::new::<libc::tm>();
448
449    let time_usec_ptr: *const libc::time_t = &(time_usec as libc::time_t);
450
451    unsafe {
452        let returned_time_struct = std::alloc::alloc(layout) as *mut libc::tm;
453
454        if utc {
455            libc::gmtime_r(time_usec_ptr, returned_time_struct);
456        } else {
457            libc::localtime_r(time_usec_ptr, returned_time_struct);
458        }
459
460        *returned_time_struct
461    }
462}
463
464#[cfg(test)]
465mod tests {
466
467    use std::ffi::CStr;
468
469    use chrono::{Duration, TimeDelta};
470
471    use super::*;
472
473    #[test]
474    fn test_since() {
475        let since = get_since_and_passed_time(1727116768682604, TimestampStyle::Pretty);
476        println!("since {since:?}");
477        let since = get_since_and_passed_time(1727116768682442, TimestampStyle::Pretty);
478        println!("since {since:?}");
479        let since = get_since_and_passed_time(1727116768682435, TimestampStyle::Pretty);
480        println!("since {since:?}");
481        let since = get_since_and_passed_time(1727413184243915, TimestampStyle::Pretty);
482        println!("since {since:?}");
483    }
484
485    #[test]
486    fn test_duration() {
487        let now = Local::now();
488
489        let tomorrow_midnight = (now + Duration::days(1))
490            .date_naive()
491            .and_hms_opt(0, 0, 0)
492            .unwrap();
493
494        let tomorrow_midnight_local = tomorrow_midnight
495            .and_local_timezone(Local)
496            .earliest()
497            .unwrap();
498
499        let duration = tomorrow_midnight_local
500            .signed_duration_since(now)
501            .to_std()
502            .unwrap();
503
504        println!("Duration between {now:?} and {tomorrow_midnight:?}: {duration:?}");
505    }
506
507    #[test]
508    fn test_duration2() {
509        let prev = get_date_local(1727116768682604);
510
511        let now = Local::now();
512
513        let duration = now.signed_duration_since(prev);
514
515        println!(
516            "Duration between {:?} and {:?}: {:?}",
517            prev,
518            now,
519            duration.to_std().unwrap()
520        );
521
522        println!(
523            "{} ago",
524            format_timestamp_relative_full_delta(duration, true)
525        )
526    }
527
528    #[test]
529    fn most_significant_duration_test() {
530        let a = TimeDelta::minutes(1) + TimeDelta::seconds(30);
531        println!("{a:?}");
532        println!("{:?}", format_timestamp_relative_full_delta(a, true));
533
534        let b = TimeDelta::minutes(2);
535        println!("{b:?}");
536        println!("{:?}", format_timestamp_relative_full_delta(b, true));
537
538        let a = TimeDelta::minutes(10) + TimeDelta::seconds(30);
539        println!("{a:?}");
540        println!("{:?}", format_timestamp_relative_full_delta(a, true));
541
542        let a = TimeDelta::minutes(9) + TimeDelta::seconds(30);
543        println!("{a:?}");
544        println!("{:?}", format_timestamp_relative_full_delta(a, true));
545    }
546
547    #[test]
548    fn test_duration_() {
549        /*         ActiveEnterTimestamp	1727500436962647
550        ActiveEnterTimestampMonotonic	383605378536
551        ActiveExitTimestamp	1727501504134907
552        ActiveExitTimestampMonotonic	384672550797 */
553
554        let enter = get_date_local(1727500436962647);
555        let exit = get_date_local(1727501504134907);
556
557        let d = exit.signed_duration_since(enter);
558        println!(
559            "{:?} {:?}",
560            format_timestamp_relative_full_delta(d, true),
561            d
562        );
563    }
564
565    #[test]
566    fn test_format_timestamp_relative_full() {
567        //println!("{} - {}", now_realtime(), (USEC_PER_YEAR + USEC_PER_MONTH));
568        const SEC_PER_YEAR_I: i64 = SEC_PER_YEAR as i64;
569        const SEC_PER_MONTH_I: i64 = SEC_PER_MONTH as i64;
570        const SEC_PER_DAY_I: i64 = SEC_PER_DAY as i64;
571        const SEC_PER_WEEK_I: i64 = SEC_PER_WEEK as i64;
572
573        const USEC_PER_SEC: i64 = 1_000_000;
574        const USEC_PER_YEAR_I: i64 = SEC_PER_YEAR_I * USEC_PER_SEC;
575        const USEC_PER_MONTH: i64 = SEC_PER_MONTH_I * USEC_PER_SEC;
576        const USEC_PER_DAY: i64 = SEC_PER_DAY_I * USEC_PER_SEC;
577        const USEC_PER_WEEK: i64 = SEC_PER_WEEK_I * USEC_PER_SEC;
578
579        let tests: Vec<(i64, &str)> = vec![
580            ((USEC_PER_YEAR_I + USEC_PER_MONTH), "1 year 1 month ago"),
581            (
582                -(USEC_PER_YEAR_I + (1.5 * USEC_PER_MONTH as f64) as i64),
583                "1 year 1 month left",
584            ),
585            (
586                (USEC_PER_YEAR_I + (2 * USEC_PER_MONTH)),
587                "1 year 2 months ago",
588            ),
589            (
590                (2 * USEC_PER_YEAR_I + USEC_PER_MONTH),
591                "2 years 1 month ago",
592            ),
593            (
594                (2 * USEC_PER_YEAR_I + 2 * USEC_PER_MONTH),
595                "2 years 2 months ago",
596            ),
597            ((USEC_PER_MONTH + USEC_PER_DAY), "1 month 1 day ago"),
598            ((USEC_PER_MONTH + 2 * USEC_PER_DAY), "1 month 2 days ago"),
599            ((2 * USEC_PER_MONTH + USEC_PER_DAY), "2 months 1 day ago"),
600            (
601                (2 * USEC_PER_MONTH + 2 * USEC_PER_DAY),
602                "2 months 2 days ago",
603            ),
604            /* Weeks and days */
605            ((USEC_PER_WEEK + USEC_PER_DAY), "1 week 1 day ago"),
606            ((USEC_PER_WEEK + 2 * USEC_PER_DAY), "1 week 2 days ago"),
607            ((2 * USEC_PER_WEEK + USEC_PER_DAY), "2 weeks 1 day ago"),
608            ((2 * USEC_PER_WEEK + 2 * USEC_PER_DAY), "2 weeks 2 days ago"),
609            (3 * 1000, "3ms ago"),
610            (2, "2μs ago"),
611            (0, "now"),
612        ];
613
614        for (time_us, time_output) in tests {
615            let delta = TimeDelta::new(
616                time_us / USEC_PER_SEC,
617                (time_us % USEC_PER_SEC) as u32 * 1000,
618            )
619            .expect("Time delta not supposed to have bondary issues");
620
621            let value = format_timestamp_relative_full_delta(delta, true);
622            assert_eq!(value, time_output);
623        }
624    }
625
626    #[test]
627    fn test_format_timespan() {
628        println!("{:?}", format_timespan(4 * USEC_PER_YEAR, MSEC_PER_SEC));
629        println!("{:?}", format_timespan(4 * USEC_PER_MONTH, MSEC_PER_SEC));
630        println!("{:?}", format_timespan(4 * USEC_PER_WEEK, MSEC_PER_SEC));
631        println!("{:?}", format_timespan(4 * USEC_PER_DAY, MSEC_PER_SEC));
632        println!("{:?}", format_timespan(4 * USEC_PER_HOUR, MSEC_PER_SEC));
633        println!("{:?}", format_timespan(4 * USEC_PER_MINUTE, MSEC_PER_SEC));
634        println!("{:?}", format_timespan(4 * USEC_PER_SEC, MSEC_PER_SEC));
635        println!("{:?}", format_timespan(4 * USEC_PER_MSEC, MSEC_PER_SEC));
636
637        println!(
638            "{:?}",
639            format_timespan(
640                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC,
641                MSEC_PER_SEC
642            )
643        );
644        println!(
645            "{:?}",
646            format_timespan(
647                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 4 * USEC_PER_MSEC,
648                MSEC_PER_SEC
649            )
650        );
651        println!(
652            "{:?}",
653            format_timespan(
654                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 4 * USEC_PER_MSEC + 4,
655                MSEC_PER_SEC
656            )
657        );
658
659        println!(
660            "{:?}",
661            format_timespan(
662                4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 50 * USEC_PER_MSEC,
663                MSEC_PER_SEC
664            )
665        );
666    }
667
668    #[test]
669    fn test_localtime_or_gmtime_usec() {
670        let asdf = localtime_or_gmtime_usec(0, false);
671        println!("time {asdf:?}");
672
673        let c_str = unsafe { CStr::from_ptr(asdf.tm_zone) };
674        println!("time zone {c_str:?}");
675
676        let asdf = localtime_or_gmtime_usec(0, true);
677        println!("time {asdf:?}");
678
679        let c_str = unsafe { CStr::from_ptr(asdf.tm_zone) };
680        println!("time zone {c_str:?}");
681    }
682
683    #[test]
684    fn test_date_format() {
685        let date = Local::now();
686
687        //%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.
688        println!("{}", date.format("%a, %d %b %Y %H:%M:%S %Z"));
689        println!("{}", date.to_rfc2822());
690        println!("{}", date.to_rfc3339());
691
692        let date = Utc::now();
693
694        //%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.
695        println!("{}", date.format("%a, %d %b %Y %H:%M:%S %Z"));
696        println!("{}", date.to_rfc2822());
697        println!("{}", date.to_rfc3339());
698    }
699
700    #[test]
701    fn test_casting() {
702        let a: i64 = 0x0000_FFFF_FFFF_FFFF;
703
704        let b: i32 = a as i32;
705
706        println!("a {a:#x} b {b:#x}");
707    }
708}