mf_expression/functions/
date_method.rs

1//! 日期方法模块
2//!
3//! 提供各种日期和时间的操作方法,包括算术运算、比较、格式化和属性获取
4
5use crate::functions::defs::{
6    CompositeFunction, FunctionDefinition, FunctionSignature, StaticFunction,
7};
8use crate::vm::date::DurationUnit;
9use std::rc::Rc;
10use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
11
12/// 日期方法枚举
13///
14/// 定义了所有可用的日期操作方法
15#[derive(
16    Debug,
17    PartialEq,
18    Eq,
19    Hash,
20    Display,
21    EnumString,
22    EnumIter,
23    IntoStaticStr,
24    Clone,
25    Copy,
26)]
27#[strum(serialize_all = "camelCase")]
28pub enum DateMethod {
29    // 算术运算
30    /// 日期加法:向日期添加时间间隔
31    Add,
32    /// 日期减法:从日期减去时间间隔
33    Sub,
34    /// 设置日期组件:设置年、月、日等特定组件
35    Set,
36    /// 格式化:将日期格式化为字符串
37    Format,
38    /// 开始时间:获取某个时间单位的开始时间(如月初、年初)
39    StartOf,
40    /// 结束时间:获取某个时间单位的结束时间(如月末、年末)
41    EndOf,
42    /// 时间差:计算两个日期之间的差值
43    Diff,
44    /// 时区转换:将日期转换到指定时区
45    Tz,
46
47    // 比较方法
48    /// 相同判断:检查两个日期是否相同
49    IsSame,
50    /// 早于判断:检查第一个日期是否早于第二个日期
51    IsBefore,
52    /// 晚于判断:检查第一个日期是否晚于第二个日期
53    IsAfter,
54    /// 相同或早于:检查第一个日期是否相同或早于第二个日期
55    IsSameOrBefore,
56    /// 相同或晚于:检查第一个日期是否相同或晚于第二个日期
57    IsSameOrAfter,
58
59    // 属性获取方法
60    /// 秒:获取日期的秒数
61    Second,
62    /// 分钟:获取日期的分钟数
63    Minute,
64    /// 小时:获取日期的小时数
65    Hour,
66    /// 日:获取日期的天数
67    Day,
68    /// 年中的天:获取日期在一年中的第几天
69    DayOfYear,
70    /// 周:获取日期在一年中的第几周
71    Week,
72    /// 星期几:获取日期是星期几
73    Weekday,
74    /// 月:获取日期的月份
75    Month,
76    /// 季度:获取日期所在的季度
77    Quarter,
78    /// 年:获取日期的年份
79    Year,
80    /// 时间戳:获取日期的Unix时间戳
81    Timestamp,
82    /// 时区名称:获取日期的时区名称
83    OffsetName,
84
85    // 状态检查方法
86    /// 有效性检查:检查日期是否有效
87    IsValid,
88    /// 昨天判断:检查日期是否为昨天
89    IsYesterday,
90    /// 今天判断:检查日期是否为今天
91    IsToday,
92    /// 明天判断:检查日期是否为明天
93    IsTomorrow,
94    /// 闰年判断:检查日期所在年份是否为闰年
95    IsLeapYear,
96}
97
98/// 比较操作类型
99///
100/// 用于统一处理各种日期比较操作
101enum CompareOperation {
102    IsSame,
103    IsBefore,
104    IsAfter,
105    IsSameOrBefore,
106    IsSameOrAfter,
107}
108
109/// 属性获取操作类型
110///
111/// 用于统一处理各种日期属性获取操作
112enum GetterOperation {
113    Second,
114    Minute,
115    Hour,
116    Day,
117    Weekday,
118    DayOfYear,
119    Week,
120    Month,
121    Quarter,
122    Year,
123    Timestamp,
124    OffsetName,
125
126    IsValid,
127    IsYesterday,
128    IsToday,
129    IsTomorrow,
130    IsLeapYear,
131}
132
133impl From<&DateMethod> for Rc<dyn FunctionDefinition> {
134    /// 将日期方法枚举转换为函数定义
135    ///
136    /// 为每个日期方法创建相应的函数定义,包括函数签名和实现
137    fn from(value: &DateMethod) -> Self {
138        use crate::variable::VariableType as VT;
139        use DateMethod as DM;
140
141        let unit_vt = DurationUnit::variable_type();
142
143        // 操作签名:支持字符串和数字+单位两种形式
144        let op_signature = vec![
145            FunctionSignature {
146                parameters: vec![VT::Date, VT::String],
147                return_type: VT::Date,
148            },
149            FunctionSignature {
150                parameters: vec![VT::Date, VT::Number, unit_vt.clone()],
151                return_type: VT::Date,
152            },
153        ];
154
155        match value {
156            // 日期加法:支持字符串和数字+单位
157            DM::Add => Rc::new(CompositeFunction {
158                implementation: Rc::new(imp::add),
159                signatures: op_signature.clone(),
160            }),
161            // 日期减法:支持字符串和数字+单位
162            DM::Sub => Rc::new(CompositeFunction {
163                implementation: Rc::new(imp::sub),
164                signatures: op_signature.clone(),
165            }),
166            // 设置日期组件:单位 + 数值
167            DM::Set => Rc::new(StaticFunction {
168                implementation: Rc::new(imp::set),
169                signature: FunctionSignature {
170                    parameters: vec![VT::Date, unit_vt.clone(), VT::Number],
171                    return_type: VT::Date,
172                },
173            }),
174            // 时区转换
175            DM::Tz => Rc::new(StaticFunction {
176                implementation: Rc::new(imp::tz),
177                signature: FunctionSignature {
178                    parameters: vec![VT::Date, VT::String],
179                    return_type: VT::Date,
180                },
181            }),
182            // 格式化:支持默认格式和自定义格式
183            DM::Format => Rc::new(CompositeFunction {
184                implementation: Rc::new(imp::format),
185                signatures: vec![
186                    FunctionSignature {
187                        parameters: vec![VT::Date],
188                        return_type: VT::String,
189                    },
190                    FunctionSignature {
191                        parameters: vec![VT::Date, VT::String],
192                        return_type: VT::String,
193                    },
194                ],
195            }),
196            // 获取时间单位的开始时间
197            DM::StartOf => Rc::new(StaticFunction {
198                implementation: Rc::new(imp::start_of),
199                signature: FunctionSignature {
200                    parameters: vec![VT::Date, unit_vt.clone()],
201                    return_type: VT::Date,
202                },
203            }),
204            // 获取时间单位的结束时间
205            DM::EndOf => Rc::new(StaticFunction {
206                implementation: Rc::new(imp::end_of),
207                signature: FunctionSignature {
208                    parameters: vec![VT::Date, unit_vt.clone()],
209                    return_type: VT::Date,
210                },
211            }),
212            // 计算时间差:支持默认单位和指定单位
213            DM::Diff => Rc::new(CompositeFunction {
214                implementation: Rc::new(imp::diff),
215                signatures: vec![
216                    FunctionSignature {
217                        parameters: vec![VT::Date, VT::Date],
218                        return_type: VT::Number,
219                    },
220                    FunctionSignature {
221                        parameters: vec![VT::Date, VT::Date, unit_vt.clone()],
222                        return_type: VT::Number,
223                    },
224                ],
225            }),
226            // 日期比较方法
227            DateMethod::IsSame => imp::compare_using(CompareOperation::IsSame),
228            DateMethod::IsBefore => {
229                imp::compare_using(CompareOperation::IsBefore)
230            },
231            DateMethod::IsAfter => {
232                imp::compare_using(CompareOperation::IsAfter)
233            },
234            DateMethod::IsSameOrBefore => {
235                imp::compare_using(CompareOperation::IsSameOrBefore)
236            },
237            DateMethod::IsSameOrAfter => {
238                imp::compare_using(CompareOperation::IsSameOrAfter)
239            },
240
241            // 日期属性获取方法
242            DateMethod::Second => imp::getter(GetterOperation::Second),
243            DateMethod::Minute => imp::getter(GetterOperation::Minute),
244            DateMethod::Hour => imp::getter(GetterOperation::Hour),
245            DateMethod::Day => imp::getter(GetterOperation::Day),
246            DateMethod::Weekday => imp::getter(GetterOperation::Weekday),
247            DateMethod::DayOfYear => imp::getter(GetterOperation::DayOfYear),
248            DateMethod::Week => imp::getter(GetterOperation::Week),
249            DateMethod::Month => imp::getter(GetterOperation::Month),
250            DateMethod::Quarter => imp::getter(GetterOperation::Quarter),
251            DateMethod::Year => imp::getter(GetterOperation::Year),
252            DateMethod::Timestamp => imp::getter(GetterOperation::Timestamp),
253            DateMethod::OffsetName => imp::getter(GetterOperation::OffsetName),
254
255            // 日期状态检查方法
256            DateMethod::IsValid => imp::getter(GetterOperation::IsValid),
257            DateMethod::IsYesterday => {
258                imp::getter(GetterOperation::IsYesterday)
259            },
260            DateMethod::IsToday => imp::getter(GetterOperation::IsToday),
261            DateMethod::IsTomorrow => imp::getter(GetterOperation::IsTomorrow),
262            DateMethod::IsLeapYear => imp::getter(GetterOperation::IsLeapYear),
263        }
264    }
265}
266
267mod imp {
268    use crate::functions::arguments::Arguments;
269    use crate::functions::date_method::{CompareOperation, GetterOperation};
270    use crate::functions::defs::{
271        CompositeFunction, FunctionDefinition, FunctionSignature,
272        StaticFunction,
273    };
274    use crate::variable::VariableType as VT;
275    use crate::vm::date::{Duration, DurationUnit};
276    use crate::vm::VmDate;
277    use crate::Variable as V;
278    use anyhow::{anyhow, Context};
279    use chrono::{Datelike, Timelike};
280    use chrono_tz::Tz;
281    use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
282    use rust_decimal::Decimal;
283    use std::rc::Rc;
284    use std::str::FromStr;
285
286    fn __internal_extract_duration(
287        args: &Arguments,
288        from: usize,
289    ) -> anyhow::Result<Duration> {
290        match args.var(from)? {
291            V::String(s) => Ok(Duration::parse(s.as_ref())?),
292            V::Number(n) => {
293                let unit = __internal_extract_duration_unit(args, from + 1)?;
294                Ok(Duration::from_unit(*n, unit)
295                    .context("Invalid duration unit")?)
296            },
297            _ => Err(anyhow!("无效的时间参数")),
298        }
299    }
300
301    fn __internal_extract_duration_unit(
302        args: &Arguments,
303        pos: usize,
304    ) -> anyhow::Result<DurationUnit> {
305        let unit_str = args.str(pos)?;
306        DurationUnit::parse(unit_str).context("无效的持续时间单位")
307    }
308
309    fn __internal_extract_duration_unit_opt(
310        args: &Arguments,
311        pos: usize,
312    ) -> anyhow::Result<Option<DurationUnit>> {
313        let unit_ostr = args.ostr(pos)?;
314        let Some(unit_str) = unit_ostr else {
315            return Ok(None);
316        };
317
318        Ok(Some(DurationUnit::parse(unit_str).context("无效的持续时间单位")?))
319    }
320
321    pub fn add(args: Arguments) -> anyhow::Result<V> {
322        let this = args.dynamic::<VmDate>(0)?;
323        let duration = __internal_extract_duration(&args, 1)?;
324
325        let date_time = this.add(duration);
326        Ok(V::Dynamic(Rc::new(date_time)))
327    }
328
329    pub fn sub(args: Arguments) -> anyhow::Result<V> {
330        let this = args.dynamic::<VmDate>(0)?;
331        let duration = __internal_extract_duration(&args, 1)?;
332
333        let date_time = this.sub(duration);
334        Ok(V::Dynamic(Rc::new(date_time)))
335    }
336
337    pub fn set(args: Arguments) -> anyhow::Result<V> {
338        let this = args.dynamic::<VmDate>(0)?;
339        let unit = __internal_extract_duration_unit(&args, 1)?;
340        let value = args.number(2)?;
341
342        let value_u32 = value.to_u32().context("无效的持续时间值")?;
343
344        let date_time = this.set(value_u32, unit);
345        Ok(V::Dynamic(Rc::new(date_time)))
346    }
347
348    pub fn format(args: Arguments) -> anyhow::Result<V> {
349        let this = args.dynamic::<VmDate>(0)?;
350        let format = args.ostr(1)?;
351
352        let formatted = this.format(format);
353        Ok(V::String(Rc::from(formatted)))
354    }
355
356    pub fn start_of(args: Arguments) -> anyhow::Result<V> {
357        let this = args.dynamic::<VmDate>(0)?;
358        let unit = __internal_extract_duration_unit(&args, 1)?;
359
360        let date_time = this.start_of(unit);
361        Ok(V::Dynamic(Rc::new(date_time)))
362    }
363
364    pub fn end_of(args: Arguments) -> anyhow::Result<V> {
365        let this = args.dynamic::<VmDate>(0)?;
366        let unit = __internal_extract_duration_unit(&args, 1)?;
367
368        let date_time = this.end_of(unit);
369        Ok(V::Dynamic(Rc::new(date_time)))
370    }
371
372    pub fn diff(args: Arguments) -> anyhow::Result<V> {
373        let this = args.dynamic::<VmDate>(0)?;
374        let date_time = VmDate::new(args.var(1)?.clone(), None);
375        let maybe_unit = __internal_extract_duration_unit_opt(&args, 2)?;
376
377        let var =
378            match this.diff(&date_time, maybe_unit).and_then(Decimal::from_i64)
379            {
380                Some(n) => V::Number(n),
381                None => V::Null,
382            };
383
384        Ok(var)
385    }
386
387    pub fn tz(args: Arguments) -> anyhow::Result<V> {
388        let this = args.dynamic::<VmDate>(0)?;
389        let tz_str = args.str(1)?;
390
391        let timezone = Tz::from_str(tz_str).context("无效的时区")?;
392        Ok(V::Dynamic(Rc::new(this.tz(timezone))))
393    }
394
395    pub fn compare_using(op: CompareOperation) -> Rc<dyn FunctionDefinition> {
396        Rc::new(CompositeFunction {
397            signatures: vec![
398                FunctionSignature {
399                    parameters: vec![VT::Date, VT::Date],
400                    return_type: VT::Date,
401                },
402                FunctionSignature {
403                    parameters: vec![
404                        VT::Date,
405                        VT::Date,
406                        DurationUnit::variable_type(),
407                    ],
408                    return_type: VT::Date,
409                },
410            ],
411            implementation: Rc::new(
412                move |args: Arguments| -> anyhow::Result<V> {
413                    let this = args.dynamic::<VmDate>(0)?;
414                    let date_time = VmDate::new(args.var(1)?.clone(), None);
415                    let maybe_unit =
416                        __internal_extract_duration_unit_opt(&args, 2)?;
417
418                    let check = match op {
419                        CompareOperation::IsSame => {
420                            this.is_same(&date_time, maybe_unit)
421                        },
422                        CompareOperation::IsBefore => {
423                            this.is_before(&date_time, maybe_unit)
424                        },
425                        CompareOperation::IsAfter => {
426                            this.is_after(&date_time, maybe_unit)
427                        },
428                        CompareOperation::IsSameOrBefore => {
429                            this.is_same_or_before(&date_time, maybe_unit)
430                        },
431                        CompareOperation::IsSameOrAfter => {
432                            this.is_same_or_after(&date_time, maybe_unit)
433                        },
434                    };
435
436                    Ok(V::Bool(check))
437                },
438            ),
439        })
440    }
441
442    pub fn getter(op: GetterOperation) -> Rc<dyn FunctionDefinition> {
443        Rc::new(StaticFunction {
444            signature: FunctionSignature {
445                parameters: vec![VT::Date],
446                return_type: match op {
447                    GetterOperation::Second
448                    | GetterOperation::Minute
449                    | GetterOperation::Hour
450                    | GetterOperation::Day
451                    | GetterOperation::Weekday
452                    | GetterOperation::DayOfYear
453                    | GetterOperation::Week
454                    | GetterOperation::Month
455                    | GetterOperation::Quarter
456                    | GetterOperation::Year
457                    | GetterOperation::Timestamp => VT::Number,
458                    GetterOperation::IsValid
459                    | GetterOperation::IsYesterday
460                    | GetterOperation::IsToday
461                    | GetterOperation::IsTomorrow
462                    | GetterOperation::IsLeapYear => VT::Bool,
463                    GetterOperation::OffsetName => VT::String,
464                },
465            },
466            implementation: Rc::new(
467                move |args: Arguments| -> anyhow::Result<V> {
468                    let this = args.dynamic::<VmDate>(0)?;
469                    if let GetterOperation::IsValid = op {
470                        return Ok(V::Bool(this.is_valid()));
471                    }
472
473                    let Some(dt) = this.0 else {
474                        return Ok(V::Null);
475                    };
476
477                    Ok(match op {
478                        GetterOperation::Second => {
479                            V::Number(dt.second().into())
480                        },
481                        GetterOperation::Minute => {
482                            V::Number(dt.minute().into())
483                        },
484                        GetterOperation::Hour => V::Number(dt.hour().into()),
485                        GetterOperation::Day => V::Number(dt.day().into()),
486                        GetterOperation::Weekday => {
487                            V::Number(dt.weekday().number_from_monday().into())
488                        },
489                        GetterOperation::DayOfYear => {
490                            V::Number(dt.ordinal().into())
491                        },
492                        GetterOperation::Week => {
493                            V::Number(dt.iso_week().week().into())
494                        },
495                        GetterOperation::Month => V::Number(dt.month().into()),
496                        GetterOperation::Quarter => {
497                            V::Number(dt.quarter().into())
498                        },
499                        GetterOperation::Year => V::Number(dt.year().into()),
500                        GetterOperation::Timestamp => {
501                            V::Number(dt.timestamp_millis().into())
502                        },
503                        // Boolean
504                        GetterOperation::IsValid => V::Bool(true),
505                        GetterOperation::IsYesterday => V::Bool(this.is_same(
506                            &VmDate::yesterday(),
507                            Some(DurationUnit::Day),
508                        )),
509                        GetterOperation::IsToday => {
510                            V::Bool(this.is_same(
511                                &VmDate::now(),
512                                Some(DurationUnit::Day),
513                            ))
514                        },
515                        GetterOperation::IsTomorrow => V::Bool(this.is_same(
516                            &VmDate::tomorrow(),
517                            Some(DurationUnit::Day),
518                        )),
519                        GetterOperation::IsLeapYear => {
520                            V::Bool(dt.date_naive().leap_year())
521                        },
522                        // String
523                        GetterOperation::OffsetName => {
524                            V::String(Rc::from(dt.timezone().name()))
525                        },
526                    })
527                },
528            ),
529        })
530    }
531}