typst_library/foundations/
duration.rs

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