use crate::functions::defs::{
CompositeFunction, FunctionDefinition, FunctionSignature, StaticFunction,
};
use crate::vm::date::DurationUnit;
use std::rc::Rc;
use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
#[derive(
Debug,
PartialEq,
Eq,
Hash,
Display,
EnumString,
EnumIter,
IntoStaticStr,
Clone,
Copy,
)]
#[strum(serialize_all = "camelCase")]
pub enum DateMethod {
Add,
Sub,
Set,
Format,
StartOf,
EndOf,
Diff,
Tz,
IsSame,
IsBefore,
IsAfter,
IsSameOrBefore,
IsSameOrAfter,
Second,
Minute,
Hour,
Day,
DayOfYear,
Week,
Weekday,
Month,
Quarter,
Year,
Timestamp,
OffsetName,
IsValid,
IsYesterday,
IsToday,
IsTomorrow,
IsLeapYear,
}
enum CompareOperation {
IsSame,
IsBefore,
IsAfter,
IsSameOrBefore,
IsSameOrAfter,
}
enum GetterOperation {
Second,
Minute,
Hour,
Day,
Weekday,
DayOfYear,
Week,
Month,
Quarter,
Year,
Timestamp,
OffsetName,
IsValid,
IsYesterday,
IsToday,
IsTomorrow,
IsLeapYear,
}
impl From<&DateMethod> for Rc<dyn FunctionDefinition> {
fn from(value: &DateMethod) -> Self {
use crate::variable::VariableType as VT;
use DateMethod as DM;
let unit_vt = DurationUnit::variable_type();
let op_signature = vec![
FunctionSignature {
parameters: vec![VT::Date, VT::String],
return_type: VT::Date,
},
FunctionSignature {
parameters: vec![VT::Date, VT::Number, unit_vt.clone()],
return_type: VT::Date,
},
];
match value {
DM::Add => Rc::new(CompositeFunction {
implementation: Rc::new(imp::add),
signatures: op_signature.clone(),
}),
DM::Sub => Rc::new(CompositeFunction {
implementation: Rc::new(imp::sub),
signatures: op_signature.clone(),
}),
DM::Set => Rc::new(StaticFunction {
implementation: Rc::new(imp::set),
signature: FunctionSignature {
parameters: vec![VT::Date, unit_vt.clone(), VT::Number],
return_type: VT::Date,
},
}),
DM::Tz => Rc::new(StaticFunction {
implementation: Rc::new(imp::tz),
signature: FunctionSignature {
parameters: vec![VT::Date, VT::String],
return_type: VT::Date,
},
}),
DM::Format => Rc::new(CompositeFunction {
implementation: Rc::new(imp::format),
signatures: vec![
FunctionSignature {
parameters: vec![VT::Date],
return_type: VT::String,
},
FunctionSignature {
parameters: vec![VT::Date, VT::String],
return_type: VT::String,
},
],
}),
DM::StartOf => Rc::new(StaticFunction {
implementation: Rc::new(imp::start_of),
signature: FunctionSignature {
parameters: vec![VT::Date, unit_vt.clone()],
return_type: VT::Date,
},
}),
DM::EndOf => Rc::new(StaticFunction {
implementation: Rc::new(imp::end_of),
signature: FunctionSignature {
parameters: vec![VT::Date, unit_vt.clone()],
return_type: VT::Date,
},
}),
DM::Diff => Rc::new(CompositeFunction {
implementation: Rc::new(imp::diff),
signatures: vec![
FunctionSignature {
parameters: vec![VT::Date, VT::Date],
return_type: VT::Number,
},
FunctionSignature {
parameters: vec![VT::Date, VT::Date, unit_vt.clone()],
return_type: VT::Number,
},
],
}),
DateMethod::IsSame => imp::compare_using(CompareOperation::IsSame),
DateMethod::IsBefore => {
imp::compare_using(CompareOperation::IsBefore)
},
DateMethod::IsAfter => {
imp::compare_using(CompareOperation::IsAfter)
},
DateMethod::IsSameOrBefore => {
imp::compare_using(CompareOperation::IsSameOrBefore)
},
DateMethod::IsSameOrAfter => {
imp::compare_using(CompareOperation::IsSameOrAfter)
},
DateMethod::Second => imp::getter(GetterOperation::Second),
DateMethod::Minute => imp::getter(GetterOperation::Minute),
DateMethod::Hour => imp::getter(GetterOperation::Hour),
DateMethod::Day => imp::getter(GetterOperation::Day),
DateMethod::Weekday => imp::getter(GetterOperation::Weekday),
DateMethod::DayOfYear => imp::getter(GetterOperation::DayOfYear),
DateMethod::Week => imp::getter(GetterOperation::Week),
DateMethod::Month => imp::getter(GetterOperation::Month),
DateMethod::Quarter => imp::getter(GetterOperation::Quarter),
DateMethod::Year => imp::getter(GetterOperation::Year),
DateMethod::Timestamp => imp::getter(GetterOperation::Timestamp),
DateMethod::OffsetName => imp::getter(GetterOperation::OffsetName),
DateMethod::IsValid => imp::getter(GetterOperation::IsValid),
DateMethod::IsYesterday => {
imp::getter(GetterOperation::IsYesterday)
},
DateMethod::IsToday => imp::getter(GetterOperation::IsToday),
DateMethod::IsTomorrow => imp::getter(GetterOperation::IsTomorrow),
DateMethod::IsLeapYear => imp::getter(GetterOperation::IsLeapYear),
}
}
}
mod imp {
use crate::functions::arguments::Arguments;
use crate::functions::date_method::{CompareOperation, GetterOperation};
use crate::functions::defs::{
CompositeFunction, FunctionDefinition, FunctionSignature,
StaticFunction,
};
use crate::variable::VariableType as VT;
use crate::vm::date::{Duration, DurationUnit};
use crate::vm::VmDate;
use crate::Variable as V;
use anyhow::{anyhow, Context};
use chrono::{Datelike, Timelike};
use chrono_tz::Tz;
use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
use rust_decimal::Decimal;
use std::rc::Rc;
use std::str::FromStr;
fn __internal_extract_duration(
args: &Arguments,
from: usize,
) -> anyhow::Result<Duration> {
match args.var(from)? {
V::String(s) => Ok(Duration::parse(s.as_ref())?),
V::Number(n) => {
let unit = __internal_extract_duration_unit(args, from + 1)?;
Ok(Duration::from_unit(*n, unit)
.context("Invalid duration unit")?)
},
_ => Err(anyhow!("无效的时间参数")),
}
}
fn __internal_extract_duration_unit(
args: &Arguments,
pos: usize,
) -> anyhow::Result<DurationUnit> {
let unit_str = args.str(pos)?;
DurationUnit::parse(unit_str).context("无效的持续时间单位")
}
fn __internal_extract_duration_unit_opt(
args: &Arguments,
pos: usize,
) -> anyhow::Result<Option<DurationUnit>> {
let unit_ostr = args.ostr(pos)?;
let Some(unit_str) = unit_ostr else {
return Ok(None);
};
Ok(Some(DurationUnit::parse(unit_str).context("无效的持续时间单位")?))
}
pub fn add(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let duration = __internal_extract_duration(&args, 1)?;
let date_time = this.add(duration);
Ok(V::Dynamic(Rc::new(date_time)))
}
pub fn sub(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let duration = __internal_extract_duration(&args, 1)?;
let date_time = this.sub(duration);
Ok(V::Dynamic(Rc::new(date_time)))
}
pub fn set(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let unit = __internal_extract_duration_unit(&args, 1)?;
let value = args.number(2)?;
let value_u32 = value.to_u32().context("无效的持续时间值")?;
let date_time = this.set(value_u32, unit);
Ok(V::Dynamic(Rc::new(date_time)))
}
pub fn format(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let format = args.ostr(1)?;
let formatted = this.format(format);
Ok(V::String(Rc::from(formatted)))
}
pub fn start_of(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let unit = __internal_extract_duration_unit(&args, 1)?;
let date_time = this.start_of(unit);
Ok(V::Dynamic(Rc::new(date_time)))
}
pub fn end_of(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let unit = __internal_extract_duration_unit(&args, 1)?;
let date_time = this.end_of(unit);
Ok(V::Dynamic(Rc::new(date_time)))
}
pub fn diff(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let date_time = VmDate::new(args.var(1)?.clone(), None);
let maybe_unit = __internal_extract_duration_unit_opt(&args, 2)?;
let var =
match this.diff(&date_time, maybe_unit).and_then(Decimal::from_i64)
{
Some(n) => V::Number(n),
None => V::Null,
};
Ok(var)
}
pub fn tz(args: Arguments) -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let tz_str = args.str(1)?;
let timezone = Tz::from_str(tz_str).context("无效的时区")?;
Ok(V::Dynamic(Rc::new(this.tz(timezone))))
}
pub fn compare_using(op: CompareOperation) -> Rc<dyn FunctionDefinition> {
Rc::new(CompositeFunction {
signatures: vec![
FunctionSignature {
parameters: vec![VT::Date, VT::Date],
return_type: VT::Date,
},
FunctionSignature {
parameters: vec![
VT::Date,
VT::Date,
DurationUnit::variable_type(),
],
return_type: VT::Date,
},
],
implementation: Rc::new(
move |args: Arguments| -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
let date_time = VmDate::new(args.var(1)?.clone(), None);
let maybe_unit =
__internal_extract_duration_unit_opt(&args, 2)?;
let check = match op {
CompareOperation::IsSame => {
this.is_same(&date_time, maybe_unit)
},
CompareOperation::IsBefore => {
this.is_before(&date_time, maybe_unit)
},
CompareOperation::IsAfter => {
this.is_after(&date_time, maybe_unit)
},
CompareOperation::IsSameOrBefore => {
this.is_same_or_before(&date_time, maybe_unit)
},
CompareOperation::IsSameOrAfter => {
this.is_same_or_after(&date_time, maybe_unit)
},
};
Ok(V::Bool(check))
},
),
})
}
pub fn getter(op: GetterOperation) -> Rc<dyn FunctionDefinition> {
Rc::new(StaticFunction {
signature: FunctionSignature {
parameters: vec![VT::Date],
return_type: match op {
GetterOperation::Second
| GetterOperation::Minute
| GetterOperation::Hour
| GetterOperation::Day
| GetterOperation::Weekday
| GetterOperation::DayOfYear
| GetterOperation::Week
| GetterOperation::Month
| GetterOperation::Quarter
| GetterOperation::Year
| GetterOperation::Timestamp => VT::Number,
GetterOperation::IsValid
| GetterOperation::IsYesterday
| GetterOperation::IsToday
| GetterOperation::IsTomorrow
| GetterOperation::IsLeapYear => VT::Bool,
GetterOperation::OffsetName => VT::String,
},
},
implementation: Rc::new(
move |args: Arguments| -> anyhow::Result<V> {
let this = args.dynamic::<VmDate>(0)?;
if let GetterOperation::IsValid = op {
return Ok(V::Bool(this.is_valid()));
}
let Some(dt) = this.0 else {
return Ok(V::Null);
};
Ok(match op {
GetterOperation::Second => {
V::Number(dt.second().into())
},
GetterOperation::Minute => {
V::Number(dt.minute().into())
},
GetterOperation::Hour => V::Number(dt.hour().into()),
GetterOperation::Day => V::Number(dt.day().into()),
GetterOperation::Weekday => {
V::Number(dt.weekday().number_from_monday().into())
},
GetterOperation::DayOfYear => {
V::Number(dt.ordinal().into())
},
GetterOperation::Week => {
V::Number(dt.iso_week().week().into())
},
GetterOperation::Month => V::Number(dt.month().into()),
GetterOperation::Quarter => {
V::Number(dt.quarter().into())
},
GetterOperation::Year => V::Number(dt.year().into()),
GetterOperation::Timestamp => {
V::Number(dt.timestamp_millis().into())
},
GetterOperation::IsValid => V::Bool(true),
GetterOperation::IsYesterday => V::Bool(this.is_same(
&VmDate::yesterday(),
Some(DurationUnit::Day),
)),
GetterOperation::IsToday => {
V::Bool(this.is_same(
&VmDate::now(),
Some(DurationUnit::Day),
))
},
GetterOperation::IsTomorrow => V::Bool(this.is_same(
&VmDate::tomorrow(),
Some(DurationUnit::Day),
)),
GetterOperation::IsLeapYear => {
V::Bool(dt.date_naive().leap_year())
},
GetterOperation::OffsetName => {
V::String(Rc::from(dt.timezone().name()))
},
})
},
),
})
}
}