thetime/lib.rs
1/// re-exported for easier access (no `use thetime::ntp::System;`, just `use thetime::System;`)
2pub mod ntp;
3
4/// re-exported for easier access (no `use thetime::system::System;`, just `use thetime::System;`)
5pub mod system;
6
7/// Timezones - a list of common timezones
8/// Note: some names clash, examples Arabia Standard Time (AST) and Atlantic Standard Time (ATST), so we lengthen as shown above
9///
10/// # Examples
11/// ```
12/// use thetime::Tz;
13/// println!("{}", Tz::UtcWet);
14/// println!("{}", Tz::BstCet);
15/// ```
16pub mod timezones;
17
18pub mod epoch {
19 pub const UNIX: &str = "1970-01-01 00:00:00";
20 pub const WINDOWS_NT: &str = "1601-01-01 00:00:00";
21 pub const WEBKIT: &str = "1601-01-01 00:00:00";
22 pub const MAC_OS: &str = "1904-01-01 00:00:00";
23 pub const MAC_OS_CFA: &str = "2001-01-01 00:00:00";
24 pub const SAS_4GL: &str = "1960-01-01 00:00:00";
25}
26
27use chrono::Local;
28/// export the ntp file for easier access
29pub use ntp::*;
30
31/// export the system file for easier access
32pub use system::*;
33
34// export the timezones file for easier access
35pub use timezones::*;
36
37/// Reference time
38pub const REF_TIME_1970: u64 = 2208988800;
39
40/// Offset between 1601 and 1970
41pub const OFFSET_1601: u64 = 11644473600;
42
43/// Magic number for SAS 4GL (offset betwenn 1960 and 1970)
44pub const MAGIC_SAS_4GL: i64 = 315619200;
45
46/// Magic number for Macos epoch (offset between 1904 and 1970)
47pub const MAGIC_MAC_OS: i64 = 2082844800;
48
49/// Magic number for Macos Absolute epoch (offset between 2001 and 1970)
50pub const MAGIC_MAC_OS_CFA: i64 = 978307200;
51/// Returns the current time in seconds since Unix epoch
52///
53/// # Examples
54/// ```rust
55/// use thetime::now;
56/// println!("{} seconds since Unix epoch", now());
57/// ```
58pub fn now() -> i64 {
59 System::now().unix()
60}
61
62/// An enum to represent whether a time is in the past, present or future
63#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
64pub enum RelativeTime {
65 Past,
66 Present,
67 Future
68}
69
70impl core::fmt::Display for RelativeTime {
71 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
72 match self {
73 RelativeTime::Past => write!(f, "past"),
74 RelativeTime::Present => write!(f, "present"),
75 RelativeTime::Future => write!(f, "future"),
76 }
77 }
78}
79
80/// Implements the core functionality of the library
81///
82/// The conversion methods from struct to various timestamps do support negatives where needed (everything but `windows_ns` as it uses the same epoch as we do)
83///
84/// Note that while all the examples use System time, as Ntp is not guaranteed to be included, Ntp can be used in exactly the same way in every one of these examples, as it too implements the Time trait.
85pub trait Time {
86 /// Get current time, returning the relevant struct
87 ///
88 /// # Examples
89 /// ```rust
90 /// use thetime::{System, Time};
91 /// println!("{} says the system, but {} says the server", System::now(), System::now());
92 /// ```
93 fn now() -> Self;
94
95 /// Parse a string into a time struct
96 ///
97 /// # Examples
98 /// ```rust
99 /// use thetime::{System, Time};
100 /// println!("The time was {}", System::strptime("2015-01-18 23:16:09", "%Y-%m-%d %H:%M:%S"));
101 /// ```
102 fn strptime<T: ToString, G: ToString>(s: T, format: G) -> Self;
103 /// Get the time in seconds since Unix epoch
104 ///
105 /// # Examples
106 /// ```rust
107 /// use thetime::{System, Time};
108 /// println!("{} seconds since Unix epoch", System::now().unix());
109 /// println!("{} seconds since Unix epoch from pool.ntp.org", System::now().unix());
110 /// ```
111 fn unix(&self) -> i64;
112
113 /// Get the time in milliseconds since Unix epoch
114 ///
115 /// # Examples
116 /// ```rust
117 /// use thetime::{System, Time};
118 /// println!("{} milliseconds since Unix epoch", System::now().unix_ms());
119 /// println!("{} milliseconds since Unix epoch from pool.ntp.org", System::now().unix_ms());
120 /// ```
121 fn unix_ms(&self) -> i64;
122
123 /// Gets the time in nanoseconds (approximate) since Windows epoch (`1601-01-01 00:00:00`)
124 ///
125 /// # Examples
126 /// ```rust
127 /// use thetime::{System, Time};
128 /// println!("{} nanoseconds since Windows epoch", System::now().windows_ns());
129 /// println!("{} nanoseconds since Windows epoch from pool.ntp.org", System::now().windows_ns());
130 /// ```
131 fn windows_ns(&self) -> i64 {
132 ((self.epoch() as f64) * 1e4) as i64
133 }
134
135 /// Gets the time in microseconds (approximate) since Webkit epoch (`1601-01-01 00:00:00`)
136 ///
137 /// # Examples
138 /// ```rust
139 /// use thetime::{System, Time};
140 /// println!("{} microseconds since Webkit epoch", System::now().webkit());
141 /// println!("{} microseconds since Webkit epoch from pool.ntp.org", System::now().webkit());
142 /// ```
143 fn webkit(&self) -> i64 {
144 ((self.epoch() as f64) * 1e3) as i64
145 }
146
147 /// Get the time in seconds since the Mac OS epoch (1904-01-01 00:00:00)
148 ///
149 /// # Examples
150 /// ```rust
151 /// use thetime::{System, Time};
152 /// println!("{} seconds since Mac OS epoch", System::now().mac_os());
153 /// println!("{} seconds since Mac OS epoch from pool.ntp.org", System::now().mac_os());
154 /// ```
155 fn mac_os(&self) -> i64 {
156 self.unix() + MAGIC_MAC_OS
157 }
158
159 /// Get the time in seconds since the Mac OS Absolute epoch (2001-01-01 00:00:00)
160 ///
161 /// # Examples
162 /// ```rust
163 /// use thetime::{System, Time};
164 /// println!("{} seconds since Mac OS Absolute epoch", System::now().mac_os_cfa());
165 /// println!("{} seconds since Mac OS Absolute epoch from pool.ntp.org", System::now().mac_os_cfa());
166 /// ```
167 fn mac_os_cfa(&self) -> i64 {
168 self.unix() - MAGIC_MAC_OS_CFA
169 }
170
171 /// Get the time in seconds since the SAS 4GL epoch (1960-01-01 00:00:00)
172 ///
173 /// # Examples
174 /// ```rust
175 /// use thetime::{System, Time};
176 /// println!("{} seconds since SAS 4GL epoch", System::now().sas_4gl());
177 /// println!("{} seconds since SAS 4GL epoch from pool.ntp.org", System::now().sas_4gl());
178 /// ```
179 fn sas_4gl(&self) -> i64 {
180 self.unix() + MAGIC_SAS_4GL
181 }
182 /// Format the time according to the given format string
183 ///
184 /// # Examples
185 /// ```rust
186 /// use thetime::{System, Time};
187 /// println!("{}", System::now().strftime("%Y-%m-%d %H:%M:%S"));
188 /// println!("{}", System::now().strftime("%Y-%B-%d %H:%M:%S"));
189 /// ```
190 fn strftime(&self, format: &str) -> String;
191
192 /// Get the time since the epoch we use (`1601-01-01 00:00:00`). we use this for full compataibility with Windows
193 ///
194 /// # Examples
195 /// ```rust
196 /// use thetime::{System, Time};
197 /// println!("{} milliseconds since the epoch we use", System::now().epoch());
198 /// println!("{} milliseconds since the epoch we use from pool.ntp.org", System::now().epoch());
199 /// ```
200 fn epoch(&self) -> i64 {
201 self.unix_ms() + (OFFSET_1601 as i64 * 1000i64)
202 }
203
204 /// pretty print the time object
205 ///
206 /// # Examples
207 /// ```rust
208 /// use thetime::{System, Time, StrTime};
209 /// let date2017 = "2017-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S");
210 /// println!("2017 - {}", date2017.pretty());
211 /// assert_eq!(date2017.pretty(), "2017-01-01 00:00:00");
212 /// ```
213 fn pretty(&self) -> String {
214 self.strftime("%Y-%m-%d %H:%M:%S")
215 }
216
217 /// Don't use this method, it's for internal use only (for instantiating structs from timestamps using the `1601-01-01 00:00:00` epoch)
218 #[doc(hidden)]
219 fn from_epoch(timestamp: u64) -> Self;
220
221 /// Don't use this method, it's for internal use only (returns raw struct ms)
222 #[doc(hidden)]
223 fn raw(&self) -> u64;
224
225 /// Returns the date formatted in ISO8601 format
226 ///
227 /// # Examples
228 /// ```rust
229 /// use thetime::{System, Time};
230 /// println!("{}", System::now().iso8601());
231 /// println!("{}", System::now().iso8601());
232 /// ```
233 fn iso8601(&self) -> String {
234 self.strftime("%Y-%m-%d %H:%M:%S.") + &(self.raw() % 1000).to_string()
235 }
236
237 /// Returns the date formatted in RFC3339 format
238 ///
239 /// # Examples
240 /// ```rust
241 /// use thetime::{System, Time};
242 /// println!("{}", System::now().rfc3339());
243 /// println!("{}", System::now().rfc3339());
244 /// ```
245 fn rfc3339(&self) -> String {
246 self.strftime("%Y-%m-%dT%H:%M:%S.") + &(self.raw() % 1000).to_string() + "Z"
247 }
248
249 /// internal only
250 #[doc(hidden)]
251 fn utc_offset(&self) -> i32;
252
253 /// Gets the timezone offset in the format HH:MM
254 ///
255 /// # Examples
256 /// ```rust
257 /// use thetime::{System, Time};
258 /// println!("{}", System::now().tz_offset());
259 /// println!("{}", System::now().tz_offset());
260 /// ```
261 fn tz_offset(&self) -> String {
262 let offset = self.utc_offset();
263 let sign = if offset < 0 { "-" } else { "+" };
264 let offset = offset.abs();
265 let hours = offset / 3600;
266 let minutes = (offset % 3600) / 60;
267 format!("{}{:02}:{:02}", sign, hours, minutes)
268 }
269
270 /// Represents the timezone as an enum
271 ///
272 /// # Examples
273 /// ```rust
274 /// use thetime::{System, Time};
275 /// println!("{:?}", System::now().tz_enum());
276 /// println!("{:?}", System::now().tz_enum());
277 /// ```
278 fn tz_enum(&self) -> Option<Tz> {
279 Tz::from_offset(-self.utc_offset())
280 }
281
282 /// Changes the timezone offset of the time object, where `offset` is in the form "+|-[0-5][0-9]:[0-5][0-9]"
283 /// Note that this change is relative to UTC, not the current timezone
284 ///
285 /// # Examples
286 /// ```rust
287 /// use thetime::{System, Time};
288 /// println!("{}", System::now().change_tz("+01:00"));
289 /// println!("{}", System::now().change_tz("-01:00"));
290 /// ```
291 fn change_tz<T: ToString>(&self, offset: T) -> Self
292 where Self: Sized {
293 let offset = offset.to_string();
294 let offset_seconds = offset[1..3].parse::<i32>().unwrap() * 3600
295 + offset[4..6].parse::<i32>().unwrap() * 60;
296
297 let offset_seconds = if offset.starts_with('+') {
298 offset_seconds
299 } else {
300 -offset_seconds
301 };
302
303 let duped_secs = offset_seconds;
304
305
306 let utc_self = Self::from_epoch_offset((self.raw() as i64 + (self.utc_offset() as i64 * 1000i64)) as u64, 0);
307
308
309 Self::from_epoch_offset((utc_self.raw() as i64 + (offset_seconds as i64 * 1000i64)) as u64, -duped_secs)
310 }
311
312 /// Changes the timezone offset of the time object to the local timezone
313 ///
314 /// # Examples
315 /// ```rust
316 /// use thetime::{System, Time};
317 /// println!("{}", System::now().local());
318 /// println!("{}", System::now().local());
319 /// ```
320 fn local(&self) -> Self
321 where Self: Sized {
322 self.change_tz(Local::now().format("%:z").to_string())
323 }
324
325 /// add an amount in seconds to a time object
326 ///
327 /// # Examples
328 /// ```rust
329 /// use thetime::{System, Time};
330 /// println!("{}", System::now().add_seconds(3600));
331 /// println!("{}", System::now().add_seconds(3600));
332 /// ```
333 fn add_seconds(&self, duration: i64) -> Self
334 where Self: Sized {
335 Self::from_epoch((self.raw() as i64 + (duration * 1000)) as u64)
336 }
337
338 /// add an amount in minutes to a time object
339 ///
340 /// # Examples
341 /// ```rust
342 /// use thetime::{System, Time};
343 /// println!("{}", System::now().add_minutes(60));
344 /// println!("{}", System::now().add_minutes(-60));
345 /// ```
346 fn add_minutes(&self, minutes: i64) -> Self
347 where Self: Sized {
348 self.add_seconds(minutes * 60)
349 }
350
351 /// add an amount in hours to a time object
352 ///
353 /// # Examples
354 /// ```rust
355 /// use thetime::{System, Time};
356 /// println!("{}", System::now().add_minutes(60));
357 /// println!("{}", System::now().add_minutes(24));
358 /// ```
359 fn add_hours(&self, hours: i64) -> Self
360 where Self: Sized {
361 self.add_seconds(hours * 3600)
362 }
363
364 /// add an amount in days to a time object
365 ///
366 /// # Examples
367 /// ```rust
368 /// use thetime::{System, Time};
369 /// println!("{}", System::now().add_days(7));
370 /// println!("{}", System::now().add_days(24));
371 /// ```
372 fn add_days(&self, days: i64) -> Self
373 where Self: Sized {
374 self.add_seconds(days * 86400)
375 }
376
377 /// add an amount in weeks to a time object
378 /// we stop at weeks to avoid potential leap year confusion
379 ///
380 /// # Examples
381 /// ```rust
382 /// use thetime::{System, Time};
383 /// println!("{}", System::now().add_weeks(7));
384 /// println!("{}", System::now().add_weeks(52));
385 /// ```
386 fn add_weeks(&self, weeks: i64) -> Self
387 where Self: Sized {
388 self.add_seconds(weeks * 604800)
389 }
390
391
392 /// determine whether a time object is in the past, present or future
393 ///
394 /// # Examples
395 /// ```rust
396 /// use thetime::{System, Time};
397 /// let x = System::now();
398 /// let y = System::now();
399 /// println!("{} is in the {}", x, x.past_future(&y));
400 /// println!("{} is in the {}", y, y.past_future(&x));
401 /// ```
402 fn past_future<T: Time>(&self, other: &T) -> RelativeTime {
403 #[allow(clippy::comparison_chain)] // this is a false positive: we don't want to use a match statement here
404 if self.raw() < other.raw() {
405 RelativeTime::Past
406 } else if self.raw() > other.raw() {
407 RelativeTime::Future
408 } else {
409 RelativeTime::Present
410 }
411 }
412
413 /// add a duration to a time object
414 ///
415 /// # Examples
416 /// ```rust
417 /// use thetime::{System, Time, ImplsDuration};
418 /// let x = System::now();
419 /// println!("{}", x.add_duration(chrono::Duration::seconds(3600)));
420 /// ```
421 fn add_duration<T: ImplsDuration>(&self, duration: T) -> Self
422 where Self: Sized {
423 self.add_seconds(duration.num_seconds())
424 }
425
426 /// cast a time object to another time object
427 ///
428 /// # Examples
429 /// ```rust
430 /// use thetime::{System, Ntp, Time};
431 /// let x = System::now();
432 /// println!("{}", x.cast::<Ntp>());
433 /// ```
434 fn cast<T: Time>(&self) -> T
435 where Self: Sized {
436 T::from_epoch(self.raw())
437 }
438
439 /// internal only
440 #[doc(hidden)]
441 fn from_epoch_offset(timestamp: u64, offset: i32) -> Self;
442}
443
444/// A trait so that we can use chrono::Duration and core::time::Duration interchangeably in the `Time::add_duration` function
445pub trait ImplsDuration {
446 fn num_seconds(&self) -> i64;
447}
448impl ImplsDuration for chrono::Duration {
449 fn num_seconds(&self) -> i64 {
450 self.num_seconds()
451 }
452}
453
454impl ImplsDuration for core::time::Duration {
455 fn num_seconds(&self) -> i64 {
456 self.as_secs() as i64
457 }
458}
459
460/// Implements the diff functions (optional)
461pub trait TimeDiff {
462 /// Get the difference between two times in seconds
463 ///
464 /// # Examples
465 /// ```rust
466 /// use thetime::{System, Time, TimeDiff, StrTime, IntTime};
467 /// let x = "2018-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S");
468 /// let y = "2017-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S");
469 /// println!("{} seconds difference", x.diff(&y));
470 /// assert_eq!(x.diff(&y), 31536000u64);
471 /// ```
472 fn diff<T: Time>(&self, other: &T) -> u64
473 where
474 Self: Time,
475 {
476 self.raw().abs_diff(other.raw()) / 1000
477 }
478
479 /// Get the difference between two times in milliseconds
480 ///
481 /// # Examples
482 /// ```rust
483 /// use thetime::{System, Time, TimeDiff};
484 /// let x = System::now();
485 /// let y = System::now();
486 /// println!("{} milliseconds difference", x.diff_ms(&y));
487 /// ```
488 fn diff_ms<T: Time>(&self, other: &T) -> u64
489 where
490 Self: Time,
491 {
492 self.raw().abs_diff(other.raw())
493 }
494}
495
496/// Provides wrappers on string std types to parse into time structs
497pub trait StrTime {
498 /// Parse a string into a time struct of choice
499 ///
500 /// # Examples
501 /// ```rust
502 /// use thetime::{System, Time, StrTime};
503 /// println!("2017 - {}", "2017-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S"));
504 /// println!("{}", "2017-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S").unix());
505 /// assert_eq!("2017-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S").unix(), 1483228800);
506 /// ```
507 fn parse_time<T: Time>(&self, format: &str) -> T
508 where
509 Self: core::fmt::Display,
510 {
511 T::strptime(self, format)
512 }
513
514 /// Parse a string into a time struct of choice, using the ISO8601 format
515 ///
516 /// # Examples
517 /// ```rust
518 /// use thetime::{System, Time, StrTime};
519 /// println!("2017 - {}", "2017-01-01T00:00:00.000".strp_iso8601::<System>());
520 /// println!("{}", "2017-01-01T00:00:00.000".strp_iso8601::<System>().unix());
521 /// assert_eq!("2017-01-01T00:00:00.000".strp_iso8601::<System>().unix(), 1483228800);
522 /// ```
523 fn strp_iso8601<T: Time>(&self) -> T
524 where
525 Self: core::fmt::Display,
526 {
527 T::strptime(self, "%Y-%m-%dT%H:%M:%S.%f")
528 }
529
530 /// Parse a string into a time struct of choice, using the RFC3339 format
531 ///
532 /// # Examples
533 /// ```rust
534 /// use thetime::{System, Time, StrTime};
535 /// println!("2017 - {}", "2017-01-01T00:00:00.000Z".strp_rf3339::<System>());
536 /// println!("{}", "2017-01-01T00:00:00.000Z".strp_rf3339::<System>().unix());
537 /// assert_eq!("2017-01-01T00:00:00.000Z".strp_rf3339::<System>().unix(), 1483228800);
538 /// ```
539 fn strp_rf3339<T: Time>(&self) -> T
540 where
541 Self: core::fmt::Display,
542 {
543 T::strptime(self, "%Y-%m-%dT%H:%M:%S.%fZ")
544 }
545}
546
547/// Provides wrappers on integer std types to parse into time structs, and also to pretty print timestamp integers
548///
549/// Note: If there is an error, the function will return the Unix epoch time for the struct of choice
550///
551/// You can only convert from positive integers, as negative integers are not supported, as they cannot be represented in the time structs. While it would be possible to fix this, I don't think it is a needed feature at the moment.
552pub trait IntTime: core::fmt::Display + Into<u64> {
553 /// Convert an integer into a time struct of choice
554 ///
555 /// # Examples
556 /// ```rust
557 /// use thetime::{System, Time, IntTime};
558 /// assert_eq!(1483228800u32.unix::<System>().pretty(), "2017-01-01 00:00:00");
559 /// ```
560 fn unix<T: Time>(self) -> T {
561 let unix: u64 = self.into();
562 T::from_epoch((unix + OFFSET_1601) * 1000)
563 }
564
565 /// Convert an integer into a time struct of choice, from a Windows timestamp (100ns since `1601-01-01 00:00:00`)
566 ///
567 /// # Examples
568 /// ```rust
569 /// use thetime::{System, Time, IntTime};
570 /// assert_eq!(131277024000000000u64.windows_ns::<System>().pretty(),"2017-01-01 00:00:00");
571 /// ```
572 fn windows_ns<T: Time>(self) -> T {
573 T::from_epoch(self.into() / (1e4 as u64))
574 }
575
576 /// Convert an integer into a time struct of choice, from a Webkit timestamp (microseconds since `1601-01-01 00:00:00`)
577 ///
578 /// # Examples
579 /// ```rust
580 /// use thetime::{System, Time, IntTime};
581 /// println!("2017 - {:#?}", 13127702400000000u64.webkit::<System>());
582 /// assert_eq!(13127702400000000u64.webkit::<System>().strftime("%Y-%m-%d %H:%M:%S"), "2017-01-01 00:00:00");
583 /// ```
584 fn webkit<T: Time>(self) -> T {
585 T::from_epoch(self.into() / (1e3 as u64))
586 }
587
588 /// Convert an integer into a time struct of choice, from a Mac OS timestamp (seconds since 1904-01-01 00:00:00)
589 ///
590 /// # Examples
591 /// ```rust
592 /// use thetime::{System, Time, IntTime};
593 /// println!("2024 - {:#?}", 3787310789u64.mac_os::<System>());
594 /// assert_eq!(3787310789u64.mac_os::<System>().strftime("%Y-%m-%d %H:%M:%S"), "2024-01-05 14:46:29");
595 /// ```
596 fn mac_os<T: Time>(self) -> T {
597 let selfu64: u64 = self.into();
598 let selfi64: i64 = selfu64 as i64;
599 let unix: i64 = selfi64 - MAGIC_MAC_OS;
600 T::from_epoch((unix + (OFFSET_1601 as i64)) as u64 * 1000)
601 }
602
603 /// Convert an integer into a time struct of choice, from a Mac OS Absolute timestamp (seconds since 2001-01-01 00:00:00)
604 ///
605 /// # Examples
606 /// ```rust
607 /// use thetime::{System, Time, IntTime};
608 /// println!("2024 - {:#?}", 726158877u64.mac_os_cfa::<System>());
609 /// assert_eq!(726158877u64.mac_os_cfa::<System>().strftime("%Y-%m-%d %H:%M:%S"), "2024-01-05 14:47:57");
610 /// ```
611 fn mac_os_cfa<T: Time>(self) -> T {
612 let selfu64: u64 = self.into();
613 let unix: u64 = selfu64 + MAGIC_MAC_OS_CFA as u64;
614 T::from_epoch((unix + OFFSET_1601) * 1000)
615 }
616
617 /// Convert an integer into a time struct of choice, from a SAS 4GL timestamp (seconds since 1960-01-01 00:00:00)
618 ///
619 /// # Examples
620 /// ```rust
621 /// use thetime::{System, Time, IntTime};
622 /// println!("2024 - {:#?}", 2020003754u64.sas_4gl::<System>());
623 /// assert_eq!(2020003754u64.sas_4gl::<System>().strftime("%Y-%m-%d %H:%M:%S"), "2024-01-04 16:09:14");
624 /// ```
625 fn sas_4gl<T: Time>(self) -> T {
626 let selfu64: u64 = self.into();
627 let selfi64: i64 = selfu64 as i64;
628 let unix: i64 = selfi64 - MAGIC_SAS_4GL;
629 T::from_epoch((unix + (OFFSET_1601 as i64)) as u64 * 1000)
630 }
631
632 /// Prints the time duration in a formatted string. Note that this only goes up to weeks, as years are rather subjective
633 ///
634 /// # Examples
635 /// ```rust
636 /// use thetime::IntTime;
637 /// let duration = 3600u64;
638 /// let formatted = duration.ts_print();
639 /// assert_eq!(formatted, "0w 0d 1h 0m 0s");
640 /// ```
641 fn ts_print(self) -> String {
642 let duration = chrono::Duration::seconds(self.into() as i64);
643 format!(
644 "{}w {}d {}h {}m {}s",
645 duration.num_weeks(),
646 duration.num_days() % 7,
647 duration.num_hours() % 24,
648 duration.num_minutes() % 60,
649 duration.num_seconds() % 60
650 )
651 }
652}
653
654/// implement the StrTime trait for `String` types
655impl StrTime for str {}
656
657/// implement the StrTime trait for `&str` types
658impl StrTime for String {}
659
660/// implement the IntTime trait for all integer types that implement conversion to u64
661impl<T: core::fmt::Display + Into<u64>> IntTime for T {}
662
663#[cfg(test)]
664mod test {
665 use super::*;
666
667 #[test]
668 fn test_system() {
669 let x = System::now();
670 println!("{}", x);
671 println!("{}", x.unix_ms());
672 println!("{}", x.strftime("%Y-%m-%d %H:%M:%S"));
673 }
674
675 #[test]
676 fn test_ntp() {
677 let x = Ntp::now();
678 println!("{:#?}", x);
679 println!("{}", x.unix_ms());
680 println!("{}", x);
681 }
682
683 #[test]
684 fn strptime() {
685 let x = System::strptime("2015-02-18 23:16:09.234", "%Y-%m-%d %H:%M:%S%.3f");
686 println!("2015 - {}", x);
687 let x = Ntp::strptime("2021-01-01 00:00:00 +0000", "%Y-%m-%d %H:%M:%S %z");
688 println!("2021 - {}", x);
689 assert_eq!(x.unix(), 1609459200);
690 println!("1950 - {}", Ntp::strptime("1950-01-01 00:00:00", "%Y-%m-%d %H:%M:%S"))
691 }
692
693 #[test]
694 fn str_time() {
695 let date2017 = "2017-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S");
696 println!("2017 - {}", date2017);
697 assert_eq!(date2017.unix(), 1483228800);
698 }
699
700 #[test]
701 fn time_diff() {
702 let x = "2015-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S");
703 let y = "2017-01-01 00:00:00".parse_time::<System>("%Y-%m-%d %H:%M:%S");
704 println!("{} difference", y.diff(&x).ts_print());
705 println!("{} milliseconds difference", x.diff_ms(&y));
706 assert_eq!(x.diff(&y), 63158400u64);
707 }
708
709 #[test]
710 fn int_ntp_time() {
711 assert_eq!(1483228800u32.unix::<Ntp>().pretty(), "2017-01-01 00:00:00");
712 assert_eq!(
713 131277024000000000u64.windows_ns::<Ntp>().pretty(),
714 "2017-01-01 00:00:00"
715 );
716 assert_eq!(
717 3787310789u64.mac_os::<Ntp>().pretty(),
718 "2024-01-05 14:46:29"
719 );
720 assert_eq!(
721 726158877u32.mac_os_cfa::<Ntp>().pretty(),
722 "2024-01-05 14:47:57"
723 );
724 assert_eq!(0u32.sas_4gl::<Ntp>().pretty(), "1960-01-01 00:00:00");
725 }
726
727 #[test]
728 fn int_system_time() {
729 assert_eq!(1483228800u32.unix::<System>().pretty(), "2017-01-01 00:00:00");
730 assert_eq!(
731 131277024000000000u64.windows_ns::<System>().pretty(),
732 "2017-01-01 00:00:00"
733 );
734 assert_eq!(
735 3787310789u64.mac_os::<System>().pretty(),
736 "2024-01-05 14:46:29"
737 );
738 assert_eq!(
739 726158877u32.mac_os_cfa::<System>().pretty(),
740 "2024-01-05 14:47:57"
741 );
742 assert_eq!(0u32.sas_4gl::<System>().pretty(), "1960-01-01 00:00:00");
743 }
744
745 #[test]
746 fn windows_tests() {
747 let x = System::now();
748 println!("{} lots of 100ns since Windows epoch", x.windows_ns());
749 }
750
751 #[test]
752 fn mac_os() {
753 let x = System::now();
754 println!("{} seconds since Mac OS epoch", x.mac_os());
755 println!("{} seconds since Mac OS Absolute epoch", x.mac_os_cfa());
756 }
757
758 #[test]
759 fn sas_4gl() {
760 let x = System::now();
761 println!("{} seconds since SAS 4GL epoch", x.sas_4gl());
762 }
763
764 #[test]
765 fn rfc3339_iso8601() {
766 let x = System::now();
767 println!("{}", x.iso8601());
768 println!("{}", x.rfc3339());
769 }
770
771 #[test]
772 fn strptime_rfc_and_iso() {
773 let x = "2017-01-01T00:00:00.000".strp_iso8601::<System>();
774 let y = "2017-01-01T00:00:00.000Z".strp_rf3339::<System>();
775 assert_eq!(x.unix(), 1483228800);
776 assert_eq!(y.unix(), 1483228800);
777 }
778
779 #[test]
780 fn tz_tests() {
781 let x = Ntp::now();
782 // println!("{}", x.tz_offset());
783 // println!("{}", x);
784 println!("{:#?}", x);
785 println!("{}", x.change_tz("+01:00"));
786 println!("{}", x.change_tz("-01:00"));
787 }
788
789 #[test]
790 fn test_add_seconds() {
791 let x = System::now();
792 println!("{}", x.add_seconds(3600));
793 }
794
795 #[test]
796 fn test_add_minshoursdaysweeks() {
797 let x = System::now();
798 println!("{}", x.add_minutes(60));
799 println!("{}", x.add_hours(24));
800 println!("{}", x.add_days(7));
801 println!("{}", x.add_weeks(52));
802 }
803
804 #[test]
805 fn long_ago_dates() {
806 let x = System::from_epoch(0);
807 println!("{}", x);
808 println!("{}", x.unix());
809 println!("{}", x.strftime("%Y-%m-%d %H:%M:%S"));
810 }
811
812 #[test]
813 fn test_past_future() {
814 let x = "2029-01-01T00:00:00.000".strp_iso8601::<System>();
815 let y = "2017-01-01T00:00:00.000Z".strp_rf3339::<System>();
816 println!("{} is in the {}", x, x.past_future(&y));
817 println!("{} is in the {}", y, y.past_future(&x));
818 }
819
820 #[test]
821 fn test_add_duration() {
822 let x = System::now();
823
824 println!("{}", x.add_duration(std::time::Duration::from_secs(3600)));
825
826 println!("{}", x.add_duration(chrono::Duration::seconds(3600)));
827 }
828 #[test]
829 fn test_local() {
830 let x = System::now();
831 println!("{}", x.local());
832 }
833
834 #[test]
835 fn test_tz_enum() {
836 let x = System::now().change_tz("+08:00");
837 println!("{}", x.tz_enum().unwrap_or_default());
838 println!("{}", Tz::from_offset(3600).unwrap_or_default());
839 println!("{}", Tz::from_offset(0).unwrap_or_default()); // Some(UtcWet)
840 println!("{}", Tz::from_offset(3600).unwrap_or_default()); // Some(BstCet)
841 println!("{}", Tz::from_offset(7200).unwrap_or_default()); // Some(CestEet)
842 println!("{}", Tz::from_offset(123456).unwrap_or_default()); // None
843 println!("{}", Tz::Acst.offset_struct(System::now()));
844 }
845
846 #[test]
847 fn huge_number() {
848 let x = System::strptime("+262143-01-01 00:00:00", "%Y-%m-%d %H:%M:%S");
849 println!("{}", x);
850 }
851
852 #[test]
853 fn test_cast() {
854 let x = System::now();
855 println!("{:#?}", x.cast::<Ntp>());
856 }
857}