diff options
| author | Laurenz Stampfl <47084093+LaurenzV@users.noreply.github.com> | 2023-05-23 10:41:20 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-23 10:41:20 +0200 |
| commit | 752817ae74607ca23ec0aad51824ddca66faa7a8 (patch) | |
| tree | d9f51f3d029c1ca8754be9ae9ccc9dc91ae6b000 /library/src/compute | |
| parent | f4fd6855e7fb833a2125d2e81aa3b9f548b18170 (diff) | |
Add support for date & time handling (#435)
Diffstat (limited to 'library/src/compute')
| -rw-r--r-- | library/src/compute/construct.rs | 173 | ||||
| -rw-r--r-- | library/src/compute/mod.rs | 1 |
2 files changed, 173 insertions, 1 deletions
diff --git a/library/src/compute/construct.rs b/library/src/compute/construct.rs index 74e96b5d..4ff9040b 100644 --- a/library/src/compute/construct.rs +++ b/library/src/compute/construct.rs @@ -1,7 +1,9 @@ use std::num::NonZeroI64; use std::str::FromStr; -use typst::eval::Regex; +use time::{Month, PrimitiveDateTime}; + +use typst::eval::{Datetime, Dynamic, Regex}; use crate::prelude::*; @@ -179,6 +181,175 @@ cast_from_value! { }, } +/// Create a new datetime. +/// +/// You can specify the [datetime]($type/datetime) using a year, month, day, +/// hour, minute, and second. +/// +/// ## Example +/// ```example +/// #datetime( +/// year: 2012, +/// month: 8, +/// day: 3, +/// ).display() +/// ``` +/// +/// ## Format +/// _Note_: Depending on which components of the datetime you specify, Typst +/// will store it in one of the following three ways: +/// * If you specify year, month and day, Typst will store just a date. +/// * If you specify hour, minute and second, Typst will store just a time. +/// * If you specify all of year, month, day, hour, minute and second, Typst +/// will store a full datetime. +/// +/// Depending on how it is stored, the [`display`]($type/datetime.display) +/// method will choose a different formatting by default. +/// +/// Display: Datetime +/// Category: construct +/// Returns: datetime +#[func] +#[scope( + scope.define("today", datetime_today); + scope +)] +pub fn datetime( + /// The year of the datetime. + #[named] + year: Option<YearComponent>, + /// The month of the datetime. + #[named] + month: Option<MonthComponent>, + /// The day of the datetime. + #[named] + day: Option<DayComponent>, + /// The hour of the datetime. + #[named] + hour: Option<HourComponent>, + /// The minute of the datetime. + #[named] + minute: Option<MinuteComponent>, + /// The second of the datetime. + #[named] + second: Option<SecondComponent>, +) -> Value { + let time = match (hour, minute, second) { + (Some(hour), Some(minute), Some(second)) => { + match time::Time::from_hms(hour.0, minute.0, second.0) { + Ok(time) => Some(time), + Err(_) => bail!(args.span, "time is invalid"), + } + } + (None, None, None) => None, + _ => bail!(args.span, "time is incomplete"), + }; + + let date = match (year, month, day) { + (Some(year), Some(month), Some(day)) => { + match time::Date::from_calendar_date(year.0, month.0, day.0) { + Ok(date) => Some(date), + Err(_) => bail!(args.span, "date is invalid"), + } + } + (None, None, None) => None, + _ => bail!(args.span, "date is incomplete"), + }; + + match (date, time) { + (Some(date), Some(time)) => Value::Dyn(Dynamic::new(Datetime::Datetime( + PrimitiveDateTime::new(date, time), + ))), + (Some(date), None) => Value::Dyn(Dynamic::new(Datetime::Date(date))), + (None, Some(time)) => Value::Dyn(Dynamic::new(Datetime::Time(time))), + (None, None) => { + bail!(args.span, "at least one of date or time must be fully specified") + } + } +} + +struct YearComponent(i32); +struct MonthComponent(Month); +struct DayComponent(u8); +struct HourComponent(u8); +struct MinuteComponent(u8); +struct SecondComponent(u8); + +cast_from_value!( + YearComponent, + v: i64 => match i32::try_from(v) { + Ok(n) => Self(n), + _ => Err("year is invalid")? + } +); + +cast_from_value!( + MonthComponent, + v: i64 => match u8::try_from(v).ok().and_then(|n1| Month::try_from(n1).ok()).map(Self) { + Some(m) => m, + _ => Err("month is invalid")? + } +); + +cast_from_value!( + DayComponent, + v: i64 => match u8::try_from(v) { + Ok(n) => Self(n), + _ => Err("day is invalid")? + } +); + +cast_from_value!( + HourComponent, + v: i64 => match u8::try_from(v) { + Ok(n) => Self(n), + _ => Err("hour is invalid")? + } +); + +cast_from_value!( + MinuteComponent, + v: i64 => match u8::try_from(v) { + Ok(n) => Self(n), + _ => Err("minute is invalid")? + } +); + +cast_from_value!( + SecondComponent, + v: i64 => match u8::try_from(v) { + Ok(n) => Self(n), + _ => Err("second is invalid")? + } +); + +/// Returns the current date. +/// +/// ## Example +/// ```example +/// Today's date is +/// #datetime.today().display(). +/// ``` +/// +/// Display: Today +/// Category: construct +/// Returns: datetime +#[func] +pub fn datetime_today( + /// An offset to apply to the current UTC date. If set to `{auto}`, the + /// offset will be the local offset. + #[named] + #[default] + offset: Smart<i64>, +) -> Value { + let current_date = match vm.vt.world.today(offset.as_custom()) { + Some(d) => d, + None => bail!(args.span, "unable to get the current date"), + }; + + Value::Dyn(Dynamic::new(current_date)) +} + /// Create a CMYK color. /// /// This is useful if you want to target a specific printer. The conversion diff --git a/library/src/compute/mod.rs b/library/src/compute/mod.rs index 8ebae48e..90730e02 100644 --- a/library/src/compute/mod.rs +++ b/library/src/compute/mod.rs @@ -23,6 +23,7 @@ pub(super) fn define(global: &mut Scope) { global.define("luma", luma); global.define("rgb", rgb); global.define("cmyk", cmyk); + global.define("datetime", datetime); global.define("symbol", symbol); global.define("str", str); global.define("label", label); |
