Skip to main content

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[duration] using weeks, days, hours,
41    /// minutes and seconds. You can also get a duration by subtracting two
42    /// @datetime[datetimes].
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 seconds component of the
86    /// duration.
87    #[func]
88    pub fn seconds(&self) -> f64 {
89        self.0.as_seconds_f64()
90    }
91
92    /// The duration expressed in minutes.
93    ///
94    /// This function returns the total duration represented in minutes as a
95    /// floating-point number, rather than the minutes component of the
96    /// duration.
97    #[func]
98    pub fn minutes(&self) -> f64 {
99        self.seconds() / 60.0
100    }
101
102    /// The duration expressed in hours.
103    ///
104    /// This function returns the total duration represented in hours as a
105    /// floating-point number, rather than the hours component of the duration.
106    #[func]
107    pub fn hours(&self) -> f64 {
108        self.seconds() / 3_600.0
109    }
110
111    /// The duration expressed in days.
112    ///
113    /// This function returns the total duration represented in days as a
114    /// floating-point number, rather than the days component of the duration.
115    #[func]
116    pub fn days(&self) -> f64 {
117        self.seconds() / 86_400.0
118    }
119
120    /// The duration expressed in weeks.
121    ///
122    /// This function returns the total duration represented in weeks as a
123    /// floating-point number, rather than the weeks component of the duration.
124    #[func]
125    pub fn weeks(&self) -> f64 {
126        self.seconds() / 604_800.0
127    }
128}
129
130impl Debug for Duration {
131    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
132        self.0.fmt(f)
133    }
134}
135
136impl Repr for Duration {
137    fn repr(&self) -> EcoString {
138        let [weeks, days, hours, minutes, seconds] = self.decompose();
139        let mut vec = Vec::with_capacity(5);
140
141        if weeks != 0 {
142            vec.push(eco_format!("weeks: {}", weeks.repr()));
143        }
144
145        if days != 0 {
146            vec.push(eco_format!("days: {}", days.repr()));
147        }
148
149        if hours != 0 {
150            vec.push(eco_format!("hours: {}", hours.repr()));
151        }
152
153        if minutes != 0 {
154            vec.push(eco_format!("minutes: {}", minutes.repr()));
155        }
156
157        if seconds != 0 {
158            vec.push(eco_format!("seconds: {}", seconds.repr()));
159        }
160
161        eco_format!("duration{}", &repr::pretty_array_like(&vec, false))
162    }
163}
164
165impl From<time::Duration> for Duration {
166    fn from(value: time::Duration) -> Self {
167        Self(value)
168    }
169}
170
171impl From<Duration> for time::Duration {
172    fn from(value: Duration) -> Self {
173        value.0
174    }
175}
176
177impl Add for Duration {
178    type Output = Duration;
179
180    fn add(self, rhs: Self) -> Self::Output {
181        Duration(self.0 + rhs.0)
182    }
183}
184
185impl Sub for Duration {
186    type Output = Duration;
187
188    fn sub(self, rhs: Self) -> Self::Output {
189        Duration(self.0 - rhs.0)
190    }
191}
192
193impl Neg for Duration {
194    type Output = Duration;
195
196    fn neg(self) -> Self::Output {
197        Duration(-self.0)
198    }
199}
200
201impl Mul<f64> for Duration {
202    type Output = Duration;
203
204    fn mul(self, rhs: f64) -> Self::Output {
205        Duration(self.0 * rhs)
206    }
207}
208
209impl Div<f64> for Duration {
210    type Output = Duration;
211
212    fn div(self, rhs: f64) -> Self::Output {
213        Duration(self.0 / rhs)
214    }
215}
216
217impl Div for Duration {
218    type Output = f64;
219
220    fn div(self, rhs: Self) -> Self::Output {
221        self.0 / rhs.0
222    }
223}