Skip to content

Commit

Permalink
Added missing timestamp macros (#106)
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Snaps <[email protected]>
  • Loading branch information
alexsnaps authored Nov 4, 2024
1 parent ea256eb commit 5b02b08
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 44 deletions.
19 changes: 15 additions & 4 deletions interpreter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,21 @@ impl<'a> Default for Context<'a> {

#[cfg(feature = "regex")]
ctx.add_function("matches", functions::matches);
#[cfg(feature = "chrono")]
ctx.add_function("duration", functions::duration);
#[cfg(feature = "chrono")]
ctx.add_function("timestamp", functions::timestamp);

if cfg!(feature = "chrono") {
ctx.add_function("duration", functions::time::duration);
ctx.add_function("timestamp", functions::time::timestamp);
ctx.add_function("getFullYear", functions::time::timestamp_year);
ctx.add_function("getMonth", functions::time::timestamp_month);
ctx.add_function("getDayOfYear", functions::time::timestamp_year_day);
ctx.add_function("getDayOfMonth", functions::time::timestamp_month_day);
ctx.add_function("getDate", functions::time::timestamp_date);
ctx.add_function("getDayOfWeek", functions::time::timestamp_weekday);
ctx.add_function("getHours", functions::time::timestamp_hours);
ctx.add_function("getMinutes", functions::time::timestamp_minutes);
ctx.add_function("getSeconds", functions::time::timestamp_seconds);
ctx.add_function("getMilliseconds", functions::time::timestamp_millis);
}

ctx
}
Expand Down
192 changes: 152 additions & 40 deletions interpreter/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,49 +503,119 @@ pub fn exists_one(
}
}

/// Duration parses the provided argument into a [`Value::Duration`] value.
///
/// The argument must be string, and must be in the format of a duration. See
/// the [`parse_duration`] documentation for more information on the supported
/// formats.
///
/// # Examples
/// - `1h` parses as 1 hour
/// - `1.5h` parses as 1 hour and 30 minutes
/// - `1h30m` parses as 1 hour and 30 minutes
/// - `1h30m1s` parses as 1 hour, 30 minutes, and 1 second
/// - `1ms` parses as 1 millisecond
/// - `1.5ms` parses as 1 millisecond and 500 microseconds
/// - `1ns` parses as 1 nanosecond
/// - `1.5ns` parses as 1 nanosecond (sub-nanosecond durations not supported)
#[cfg(feature = "chrono")]
pub fn duration(value: Arc<String>) -> Result<Value> {
Ok(Value::Duration(_duration(value.as_str())?))
}
pub mod time {
use super::Result;
use crate::magic::This;
use crate::{ExecutionError, Value};
use chrono::{Datelike, Days, Months, Timelike};
use std::sync::Arc;

/// Duration parses the provided argument into a [`Value::Duration`] value.
///
/// The argument must be string, and must be in the format of a duration. See
/// the [`parse_duration`] documentation for more information on the supported
/// formats.
///
/// # Examples
/// - `1h` parses as 1 hour
/// - `1.5h` parses as 1 hour and 30 minutes
/// - `1h30m` parses as 1 hour and 30 minutes
/// - `1h30m1s` parses as 1 hour, 30 minutes, and 1 second
/// - `1ms` parses as 1 millisecond
/// - `1.5ms` parses as 1 millisecond and 500 microseconds
/// - `1ns` parses as 1 nanosecond
/// - `1.5ns` parses as 1 nanosecond (sub-nanosecond durations not supported)
pub fn duration(value: Arc<String>) -> crate::functions::Result<Value> {
Ok(Value::Duration(_duration(value.as_str())?))
}

/// Timestamp parses the provided argument into a [`Value::Timestamp`] value.
/// The
pub fn timestamp(value: Arc<String>) -> Result<Value> {
Ok(Value::Timestamp(
chrono::DateTime::parse_from_rfc3339(value.as_str())
.map_err(|e| ExecutionError::function_error("timestamp", e.to_string().as_str()))?,
))
}

/// A wrapper around [`parse_duration`] that converts errors into [`ExecutionError`].
/// and only returns the duration, rather than returning the remaining input.
fn _duration(i: &str) -> Result<chrono::Duration> {
let (_, duration) = crate::duration::parse_duration(i)
.map_err(|e| ExecutionError::function_error("duration", e.to_string()))?;
Ok(duration)
}

fn _timestamp(i: &str) -> Result<chrono::DateTime<chrono::FixedOffset>> {
chrono::DateTime::parse_from_rfc3339(i)
.map_err(|e| ExecutionError::function_error("timestamp", e.to_string()))
}

pub fn timestamp_year(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok(this.year().into())
}

pub fn timestamp_month(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.month0() as i32).into())
}

pub fn timestamp_year_day(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
let year = this
.checked_sub_days(Days::new(this.day0() as u64))
.unwrap()
.checked_sub_months(Months::new(this.month0()))
.unwrap();
Ok(this.signed_duration_since(year).num_days().into())
}

/// Timestamp parses the provided argument into a [`Value::Timestamp`] value.
/// The
#[cfg(feature = "chrono")]
pub fn timestamp(value: Arc<String>) -> Result<Value> {
Ok(Value::Timestamp(
chrono::DateTime::parse_from_rfc3339(value.as_str())
.map_err(|e| ExecutionError::function_error("timestamp", e.to_string().as_str()))?,
))
}
pub fn timestamp_month_day(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.day0() as i32).into())
}

/// A wrapper around [`parse_duration`] that converts errors into [`ExecutionError`].
/// and only returns the duration, rather than returning the remaining input.
#[cfg(feature = "chrono")]
fn _duration(i: &str) -> Result<chrono::Duration> {
let (_, duration) = crate::duration::parse_duration(i)
.map_err(|e| ExecutionError::function_error("duration", e.to_string()))?;
Ok(duration)
}
pub fn timestamp_date(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.day() as i32).into())
}

#[cfg(feature = "chrono")]
fn _timestamp(i: &str) -> Result<chrono::DateTime<chrono::FixedOffset>> {
chrono::DateTime::parse_from_rfc3339(i)
.map_err(|e| ExecutionError::function_error("timestamp", e.to_string()))
pub fn timestamp_weekday(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.weekday().num_days_from_sunday() as i32).into())
}

pub fn timestamp_hours(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.hour() as i32).into())
}

pub fn timestamp_minutes(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.minute() as i32).into())
}

pub fn timestamp_seconds(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.second() as i32).into())
}

pub fn timestamp_millis(
This(this): This<chrono::DateTime<chrono::FixedOffset>>,
) -> Result<Value> {
Ok((this.timestamp_subsec_millis() as i32).into())
}
}

pub fn max(Arguments(args): Arguments) -> Result<Value> {
Expand Down Expand Up @@ -734,7 +804,49 @@ mod tests {
(
"timestamp string",
"timestamp('2023-05-28T00:00:00Z').string() == '2023-05-28T00:00:00+00:00'",
)]
),
(
"timestamp getFullYear",
"timestamp('2023-05-28T00:00:00Z').getFullYear() == 2023",
),
(
"timestamp getMonth",
"timestamp('2023-05-28T00:00:00Z').getMonth() == 4",
),
(
"timestamp getDayOfMonth",
"timestamp('2023-05-28T00:00:00Z').getDayOfMonth() == 27",
),
(
"timestamp getDayOfYear",
"timestamp('2023-05-28T00:00:00Z').getDayOfYear() == 147",
),
(
"timestamp getDate",
"timestamp('2023-05-28T00:00:00Z').getDate() == 28",
),
(
"timestamp getDayOfWeek",
"timestamp('2023-05-28T00:00:00Z').getDayOfWeek() == 0",
),
(
"timestamp getHours",
"timestamp('2023-05-28T02:00:00Z').getHours() == 2",
),
(
"timestamp getMinutes",
" timestamp('2023-05-28T00:05:00Z').getMinutes() == 5",
),
(
"timestamp getSeconds",
"timestamp('2023-05-28T00:00:06Z').getSeconds() == 6",
),
(
"timestamp getMilliseconds",
"timestamp('2023-05-28T00:00:42.123Z').getMilliseconds() == 123",
),

]
.iter()
.for_each(assert_script);
}
Expand Down

0 comments on commit 5b02b08

Please sign in to comment.