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 TimestampStyle::Pretty => pgettext("pref time style", "Pretty"),
37 TimestampStyle::PrettyUsec => pgettext("pref time style", "Pretty usec"),
39 TimestampStyle::Utc => pgettext("pref time style", "UTC"),
41 TimestampStyle::UtcUsec => pgettext("pref time style", "UTC usec"),
43 TimestampStyle::Unix => pgettext("pref time style", "Unix"),
45 TimestampStyle::UnixUsec => pgettext("pref time style", "Unix usec"),
47 }
48 }
49
50 pub fn details(&self) -> String {
51 match self {
52 TimestampStyle::Pretty => pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS TZ"),
54
55 TimestampStyle::PrettyUsec => {
57 pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS.000000 TZ")
58 }
59
60 TimestampStyle::Utc => pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS UTC"),
62
63 TimestampStyle::UtcUsec => {
65 pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS.000000 UTC")
66 }
67
68 TimestampStyle::Unix => pgettext("pref time style", "Seconds since the epoch"),
70
71 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 if let Err(e) = write!($out, $($y), +) {
207 tracing::warn!("swrite error {:?}", e);
208 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
262fn 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 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
471pub 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 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 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 ((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 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 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}