tytanic_core/world_builder/
datetime.rs

1use std::sync::Mutex;
2
3use chrono::DateTime;
4use chrono::Datelike;
5use chrono::FixedOffset;
6use chrono::Local;
7use chrono::Utc;
8use typst::foundations::Datetime;
9
10use super::ProvideDatetime;
11
12/// Provides access to the system date, but not time.
13#[derive(Debug)]
14pub struct SystemDateProvider {
15    today: Mutex<Option<DateTime<Utc>>>,
16}
17
18impl SystemDateProvider {
19    /// Reset the compilation state in preparation of a new compilation.
20    pub fn reset(&self) {
21        *self.today.lock().unwrap() = None;
22    }
23}
24
25impl SystemDateProvider {
26    /// The current system date.
27    pub fn today(&self) -> DateTime<Utc> {
28        *self.today.lock().unwrap().get_or_insert_with(Utc::now)
29    }
30}
31
32impl SystemDateProvider {
33    /// The current system date.
34    pub fn today_with_offset(&self, offset: Option<i64>) -> Option<Datetime> {
35        with_offset(self.today(), offset)
36    }
37}
38
39impl ProvideDatetime for SystemDateProvider {
40    fn provide_today(&self, offset: Option<i64>) -> Option<Datetime> {
41        self.today_with_offset(offset)
42    }
43
44    fn reset_today(&self) {
45        self.reset();
46    }
47}
48
49/// Provides access to a fixed date, but not time.
50#[derive(Debug)]
51pub struct FixedDateProvider {
52    date: DateTime<Utc>,
53}
54
55impl FixedDateProvider {
56    /// Create a new fixed date provider with the given date.
57    pub fn new(date: DateTime<Utc>) -> Self {
58        Self { date }
59    }
60}
61
62impl FixedDateProvider {
63    /// The fixed date.
64    pub fn date(&self) -> DateTime<Utc> {
65        self.date
66    }
67}
68
69impl FixedDateProvider {
70    /// The fixed date.
71    pub fn date_with_offset(&self, offset: Option<i64>) -> Option<Datetime> {
72        with_offset(self.date, offset)
73    }
74}
75
76fn with_offset(today: DateTime<Utc>, offset: Option<i64>) -> Option<Datetime> {
77    // The time with the specified UTC offset, or within the local time zone.
78    let with_offset = match offset {
79        Some(hours) => {
80            let seconds = i32::try_from(hours).ok()?.checked_mul(3600)?;
81            today.with_timezone(&FixedOffset::east_opt(seconds)?)
82        }
83        None => today.with_timezone(&Local).fixed_offset(),
84    };
85
86    Datetime::from_ymd(
87        with_offset.year(),
88        with_offset.month().try_into().ok()?,
89        with_offset.day().try_into().ok()?,
90    )
91}
92
93impl ProvideDatetime for FixedDateProvider {
94    fn provide_today(&self, offset: Option<i64>) -> Option<Datetime> {
95        self.date_with_offset(offset)
96    }
97
98    fn reset_today(&self) {}
99}