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}