typst_library/foundations/
duration.rs

1use std::fmt::{self, Debug, Formatter};
2use std::ops::{Add, Div, Mul, Neg, Sub};
3
4use ecow::{eco_format, EcoString};
5use time::ext::NumericalDuration;
6
7use crate::foundations::{func, repr, scope, ty, Repr};
8
9/// Represents a positive or negative span of time.
10#[ty(scope, cast)]
11#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
12pub struct Duration(time::Duration);
13
14impl Duration {
15    /// Whether the duration is empty / zero.
16    pub fn is_zero(&self) -> bool {
17        self.0.is_zero()
18    }
19}
20
21#[scope]
22impl Duration {
23    /// Creates a new duration.
24    ///
25    /// You can specify the [duration] using weeks, days, hours, minutes and
26    /// seconds. You can also get a duration by subtracting two
27    /// [datetimes]($datetime).
28    ///
29    /// ```example
30    /// #duration(
31    ///   days: 3,
32    ///   hours: 12,
33    /// ).hours()
34    /// ```
35    #[func(constructor)]
36    pub fn construct(
37        /// The number of seconds.
38        #[named]
39        #[default(0)]
40        seconds: i64,
41        /// The number of minutes.
42        #[named]
43        #[default(0)]
44        minutes: i64,
45        /// The number of hours.
46        #[named]
47        #[default(0)]
48        hours: i64,
49        /// The number of days.
50        #[named]
51        #[default(0)]
52        days: i64,
53        /// The number of weeks.
54        #[named]
55        #[default(0)]
56        weeks: i64,
57    ) -> Duration {
58        Duration::from(
59            time::Duration::seconds(seconds)
60                + time::Duration::minutes(minutes)
61                + time::Duration::hours(hours)
62                + time::Duration::days(days)
63                + time::Duration::weeks(weeks),
64        )
65    }
66
67    /// The duration expressed in seconds.
68    ///
69    /// This function returns the total duration represented in seconds as a
70    /// floating-point number rather than the second component of the duration.
71    #[func]
72    pub fn seconds(&self) -> f64 {
73        self.0.as_seconds_f64()
74    }
75
76    /// The duration expressed in minutes.
77    ///
78    /// This function returns the total duration represented in minutes as a
79    /// floating-point number rather than the second component of the duration.
80    #[func]
81    pub fn minutes(&self) -> f64 {
82        self.seconds() / 60.0
83    }
84
85    /// The duration expressed in hours.
86    ///
87    /// This function returns the total duration represented in hours as a
88    /// floating-point number rather than the second component of the duration.
89    #[func]
90    pub fn hours(&self) -> f64 {
91        self.seconds() / 3_600.0
92    }
93
94    /// The duration expressed in days.
95    ///
96    /// This function returns the total duration represented in days as a
97    /// floating-point number rather than the second component of the duration.
98    #[func]
99    pub fn days(&self) -> f64 {
100        self.seconds() / 86_400.0
101    }
102
103    /// The duration expressed in weeks.
104    ///
105    /// This function returns the total duration represented in weeks as a
106    /// floating-point number rather than the second component of the duration.
107    #[func]
108    pub fn weeks(&self) -> f64 {
109        self.seconds() / 604_800.0
110    }
111}
112
113impl Debug for Duration {
114    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
115        self.0.fmt(f)
116    }
117}
118
119impl Repr for Duration {
120    fn repr(&self) -> EcoString {
121        let mut tmp = self.0;
122        let mut vec = Vec::with_capacity(5);
123
124        let weeks = tmp.whole_seconds() / 604_800.0 as i64;
125        if weeks != 0 {
126            vec.push(eco_format!("weeks: {}", weeks.repr()));
127        }
128        tmp -= weeks.weeks();
129
130        let days = tmp.whole_days();
131        if days != 0 {
132            vec.push(eco_format!("days: {}", days.repr()));
133        }
134        tmp -= days.days();
135
136        let hours = tmp.whole_hours();
137        if hours != 0 {
138            vec.push(eco_format!("hours: {}", hours.repr()));
139        }
140        tmp -= hours.hours();
141
142        let minutes = tmp.whole_minutes();
143        if minutes != 0 {
144            vec.push(eco_format!("minutes: {}", minutes.repr()));
145        }
146        tmp -= minutes.minutes();
147
148        let seconds = tmp.whole_seconds();
149        if seconds != 0 {
150            vec.push(eco_format!("seconds: {}", seconds.repr()));
151        }
152
153        eco_format!("duration{}", &repr::pretty_array_like(&vec, false))
154    }
155}
156
157impl From<time::Duration> for Duration {
158    fn from(value: time::Duration) -> Self {
159        Self(value)
160    }
161}
162
163impl From<Duration> for time::Duration {
164    fn from(value: Duration) -> Self {
165        value.0
166    }
167}
168
169impl Add for Duration {
170    type Output = Duration;
171
172    fn add(self, rhs: Self) -> Self::Output {
173        Duration(self.0 + rhs.0)
174    }
175}
176
177impl Sub for Duration {
178    type Output = Duration;
179
180    fn sub(self, rhs: Self) -> Self::Output {
181        Duration(self.0 - rhs.0)
182    }
183}
184
185impl Neg for Duration {
186    type Output = Duration;
187
188    fn neg(self) -> Self::Output {
189        Duration(-self.0)
190    }
191}
192
193impl Mul<f64> for Duration {
194    type Output = Duration;
195
196    fn mul(self, rhs: f64) -> Self::Output {
197        Duration(self.0 * rhs)
198    }
199}
200
201impl Div<f64> for Duration {
202    type Output = Duration;
203
204    fn div(self, rhs: f64) -> Self::Output {
205        Duration(self.0 / rhs)
206    }
207}
208
209impl Div for Duration {
210    type Output = f64;
211
212    fn div(self, rhs: Self) -> Self::Output {
213        self.0 / rhs.0
214    }
215}