1use 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#[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 Add,
32 Sub,
34 Set,
36 Format,
38 StartOf,
40 EndOf,
42 Diff,
44 Tz,
46
47 IsSame,
50 IsBefore,
52 IsAfter,
54 IsSameOrBefore,
56 IsSameOrAfter,
58
59 Second,
62 Minute,
64 Hour,
66 Day,
68 DayOfYear,
70 Week,
72 Weekday,
74 Month,
76 Quarter,
78 Year,
80 Timestamp,
82 OffsetName,
84
85 IsValid,
88 IsYesterday,
90 IsToday,
92 IsTomorrow,
94 IsLeapYear,
96}
97
98enum CompareOperation {
102 IsSame,
103 IsBefore,
104 IsAfter,
105 IsSameOrBefore,
106 IsSameOrAfter,
107}
108
109enum 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 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 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 DM::Add => Rc::new(CompositeFunction {
158 implementation: Rc::new(imp::add),
159 signatures: op_signature.clone(),
160 }),
161 DM::Sub => Rc::new(CompositeFunction {
163 implementation: Rc::new(imp::sub),
164 signatures: op_signature.clone(),
165 }),
166 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 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 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 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 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 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 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 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 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 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 GetterOperation::OffsetName => {
524 V::String(Rc::from(dt.timezone().name()))
525 },
526 })
527 },
528 ),
529 })
530 }
531}