1use crate::functions::defs::{
6 CompositeFunction, FunctionDefinition, FunctionSignature, StaticFunction,
7};
8use std::rc::Rc;
9use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
10
11#[derive(
15 Debug,
16 PartialEq,
17 Eq,
18 Hash,
19 Display,
20 EnumString,
21 EnumIter,
22 IntoStaticStr,
23 Clone,
24 Copy,
25)]
26#[strum(serialize_all = "camelCase")]
27pub enum InternalFunction {
28 Len,
31 Contains,
33 Flatten,
35
36 Upper,
39 Lower,
41 Trim,
43 StartsWith,
45 EndsWith,
47 Matches,
49 Extract,
51 FuzzyMatch,
53 Split,
55
56 Abs,
59 Sum,
61 Avg,
63 Min,
65 Max,
67 Rand,
69 Median,
71 Mode,
73 Floor,
75 Ceil,
77 Round,
79 Trunc,
81
82 IsNumeric,
85 String,
87 Number,
89 Bool,
91 Type,
93
94 Keys,
97 Values,
99
100 #[strum(serialize = "d")]
102 Date,
103}
104
105impl From<&InternalFunction> for Rc<dyn FunctionDefinition> {
106 fn from(value: &InternalFunction) -> Self {
110 use crate::variable::VariableType as VT;
111 use InternalFunction as IF;
112
113 let s: Rc<dyn FunctionDefinition> = match value {
114 IF::Len => Rc::new(CompositeFunction {
116 implementation: Rc::new(imp::len),
117 signatures: vec![
118 FunctionSignature::single(VT::String, VT::Number),
119 FunctionSignature::single(VT::Any.array(), VT::Number),
120 ],
121 }),
122
123 IF::Contains => Rc::new(CompositeFunction {
125 implementation: Rc::new(imp::contains),
126 signatures: vec![
127 FunctionSignature {
128 parameters: vec![VT::String, VT::String],
129 return_type: VT::Bool,
130 },
131 FunctionSignature {
132 parameters: vec![VT::Any.array(), VT::Any],
133 return_type: VT::Bool,
134 },
135 ],
136 }),
137
138 IF::Flatten => Rc::new(StaticFunction {
140 implementation: Rc::new(imp::flatten),
141 signature: FunctionSignature::single(
142 VT::Any.array(),
143 VT::Any.array(),
144 ),
145 }),
146
147 IF::Upper => Rc::new(StaticFunction {
149 implementation: Rc::new(imp::upper),
150 signature: FunctionSignature::single(VT::String, VT::String),
151 }),
152
153 IF::Lower => Rc::new(StaticFunction {
155 implementation: Rc::new(imp::lower),
156 signature: FunctionSignature::single(VT::String, VT::String),
157 }),
158
159 IF::Trim => Rc::new(StaticFunction {
161 implementation: Rc::new(imp::trim),
162 signature: FunctionSignature::single(VT::String, VT::String),
163 }),
164
165 IF::StartsWith => Rc::new(StaticFunction {
167 implementation: Rc::new(imp::starts_with),
168 signature: FunctionSignature {
169 parameters: vec![VT::String, VT::String],
170 return_type: VT::Bool,
171 },
172 }),
173
174 IF::EndsWith => Rc::new(StaticFunction {
176 implementation: Rc::new(imp::ends_with),
177 signature: FunctionSignature {
178 parameters: vec![VT::String, VT::String],
179 return_type: VT::Bool,
180 },
181 }),
182
183 IF::Matches => Rc::new(StaticFunction {
185 implementation: Rc::new(imp::matches),
186 signature: FunctionSignature {
187 parameters: vec![VT::String, VT::String],
188 return_type: VT::Bool,
189 },
190 }),
191
192 IF::Extract => Rc::new(StaticFunction {
194 implementation: Rc::new(imp::extract),
195 signature: FunctionSignature {
196 parameters: vec![VT::String, VT::String],
197 return_type: VT::String.array(),
198 },
199 }),
200
201 IF::Split => Rc::new(StaticFunction {
203 implementation: Rc::new(imp::split),
204 signature: FunctionSignature {
205 parameters: vec![VT::String, VT::String],
206 return_type: VT::String.array(),
207 },
208 }),
209
210 IF::FuzzyMatch => Rc::new(CompositeFunction {
212 implementation: Rc::new(imp::fuzzy_match),
213 signatures: vec![
214 FunctionSignature {
215 parameters: vec![VT::String, VT::String],
216 return_type: VT::Number,
217 },
218 FunctionSignature {
219 parameters: vec![VT::String.array(), VT::String],
220 return_type: VT::Number.array(),
221 },
222 ],
223 }),
224
225 IF::Abs => Rc::new(StaticFunction {
227 implementation: Rc::new(imp::abs),
228 signature: FunctionSignature::single(VT::Number, VT::Number),
229 }),
230
231 IF::Rand => Rc::new(StaticFunction {
233 implementation: Rc::new(imp::rand),
234 signature: FunctionSignature::single(VT::Number, VT::Number),
235 }),
236
237 IF::Floor => Rc::new(StaticFunction {
239 implementation: Rc::new(imp::floor),
240 signature: FunctionSignature::single(VT::Number, VT::Number),
241 }),
242
243 IF::Ceil => Rc::new(StaticFunction {
245 implementation: Rc::new(imp::ceil),
246 signature: FunctionSignature::single(VT::Number, VT::Number),
247 }),
248
249 IF::Round => Rc::new(CompositeFunction {
251 implementation: Rc::new(imp::round),
252 signatures: vec![
253 FunctionSignature {
254 parameters: vec![VT::Number],
255 return_type: VT::Number,
256 },
257 FunctionSignature {
258 parameters: vec![VT::Number, VT::Number],
259 return_type: VT::Number,
260 },
261 ],
262 }),
263
264 IF::Trunc => Rc::new(CompositeFunction {
266 implementation: Rc::new(imp::trunc),
267 signatures: vec![
268 FunctionSignature {
269 parameters: vec![VT::Number],
270 return_type: VT::Number,
271 },
272 FunctionSignature {
273 parameters: vec![VT::Number, VT::Number],
274 return_type: VT::Number,
275 },
276 ],
277 }),
278
279 IF::Sum => Rc::new(StaticFunction {
281 implementation: Rc::new(imp::sum),
282 signature: FunctionSignature::single(
283 VT::Number.array(),
284 VT::Number,
285 ),
286 }),
287
288 IF::Avg => Rc::new(StaticFunction {
290 implementation: Rc::new(imp::avg),
291 signature: FunctionSignature::single(
292 VT::Number.array(),
293 VT::Number,
294 ),
295 }),
296
297 IF::Min => Rc::new(CompositeFunction {
299 implementation: Rc::new(imp::min),
300 signatures: vec![
301 FunctionSignature::single(VT::Number.array(), VT::Number),
302 FunctionSignature::single(VT::Date.array(), VT::Date),
303 ],
304 }),
305
306 IF::Max => Rc::new(CompositeFunction {
308 implementation: Rc::new(imp::max),
309 signatures: vec![
310 FunctionSignature::single(VT::Number.array(), VT::Number),
311 FunctionSignature::single(VT::Date.array(), VT::Date),
312 ],
313 }),
314
315 IF::Median => Rc::new(StaticFunction {
317 implementation: Rc::new(imp::median),
318 signature: FunctionSignature::single(
319 VT::Number.array(),
320 VT::Number,
321 ),
322 }),
323
324 IF::Mode => Rc::new(StaticFunction {
326 implementation: Rc::new(imp::mode),
327 signature: FunctionSignature::single(
328 VT::Number.array(),
329 VT::Number,
330 ),
331 }),
332
333 IF::Type => Rc::new(StaticFunction {
335 implementation: Rc::new(imp::to_type),
336 signature: FunctionSignature::single(VT::Any, VT::String),
337 }),
338
339 IF::String => Rc::new(StaticFunction {
341 implementation: Rc::new(imp::to_string),
342 signature: FunctionSignature::single(VT::Any, VT::String),
343 }),
344
345 IF::Bool => Rc::new(StaticFunction {
347 implementation: Rc::new(imp::to_bool),
348 signature: FunctionSignature::single(VT::Any, VT::Bool),
349 }),
350
351 IF::IsNumeric => Rc::new(StaticFunction {
353 implementation: Rc::new(imp::is_numeric),
354 signature: FunctionSignature::single(VT::Any, VT::Bool),
355 }),
356
357 IF::Number => Rc::new(StaticFunction {
359 implementation: Rc::new(imp::to_number),
360 signature: FunctionSignature::single(VT::Any, VT::Number),
361 }),
362
363 IF::Keys => Rc::new(CompositeFunction {
365 implementation: Rc::new(imp::keys),
366 signatures: vec![
367 FunctionSignature::single(
368 VT::Object(Default::default()),
369 VT::String.array(),
370 ),
371 FunctionSignature::single(
372 VT::Any.array(),
373 VT::Number.array(),
374 ),
375 ],
376 }),
377
378 IF::Values => Rc::new(StaticFunction {
380 implementation: Rc::new(imp::values),
381 signature: FunctionSignature::single(
382 VT::Object(Default::default()),
383 VT::Any.array(),
384 ),
385 }),
386
387 IF::Date => Rc::new(CompositeFunction {
389 implementation: Rc::new(imp::date),
390 signatures: vec![
391 FunctionSignature {
392 parameters: vec![],
393 return_type: VT::Date,
394 },
395 FunctionSignature {
396 parameters: vec![VT::Any],
397 return_type: VT::Date,
398 },
399 FunctionSignature {
400 parameters: vec![VT::Any, VT::String],
401 return_type: VT::Date,
402 },
403 ],
404 }),
405 };
406
407 s
408 }
409}
410
411pub(crate) mod imp {
415 use crate::functions::arguments::Arguments;
416 use crate::vm::VmDate;
417 use crate::{Variable as V, Variable};
418 use anyhow::{anyhow, Context};
419 use chrono_tz::Tz;
420 #[cfg(not(feature = "regex-lite"))]
421 use regex::Regex;
422 #[cfg(feature = "regex-lite")]
423 use regex_lite::Regex;
424 use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
425 use rust_decimal::{Decimal, RoundingStrategy};
426 use rust_decimal_macros::dec;
427 use std::collections::BTreeMap;
428 use std::rc::Rc;
429 use std::str::FromStr;
430
431 fn __internal_number_array(
441 args: &Arguments,
442 pos: usize,
443 ) -> anyhow::Result<Vec<Decimal>> {
444 let a = args.array(pos)?;
445 let arr = a.borrow();
446
447 arr.iter()
448 .map(|v| v.as_number())
449 .collect::<Option<Vec<_>>>()
450 .context("Expected a number array")
451 }
452
453 enum Either<A, B> {
455 Left(A),
456 Right(B),
457 }
458
459 fn __internal_number_or_date_array(
472 args: &Arguments,
473 pos: usize,
474 ) -> anyhow::Result<Either<Vec<Decimal>, Vec<VmDate>>> {
475 let a = args.array(pos)?;
476 let arr = a.borrow();
477
478 let is_number = arr.first().map(|v| v.as_number()).flatten().is_some();
479 if is_number {
480 Ok(Either::Left(
481 arr.iter()
482 .map(|v| v.as_number())
483 .collect::<Option<Vec<_>>>()
484 .context("Expected a number array")?,
485 ))
486 } else {
487 Ok(Either::Right(
488 arr.iter()
489 .map(|v| match v {
490 Variable::Dynamic(d) => d.as_date().cloned(),
491 _ => None,
492 })
493 .collect::<Option<Vec<_>>>()
494 .context("Expected a number array")?,
495 ))
496 }
497 }
498
499 pub fn starts_with(args: Arguments) -> anyhow::Result<V> {
503 let a = args.str(0)?;
504 let b = args.str(1)?;
505
506 Ok(V::Bool(a.starts_with(b)))
507 }
508
509 pub fn ends_with(args: Arguments) -> anyhow::Result<V> {
513 let a = args.str(0)?;
514 let b = args.str(1)?;
515
516 Ok(V::Bool(a.ends_with(b)))
517 }
518
519 pub fn matches(args: Arguments) -> anyhow::Result<V> {
523 let a = args.str(0)?;
524 let b = args.str(1)?;
525
526 let regex =
527 Regex::new(b.as_ref()).context("Invalid regular expression")?;
528
529 Ok(V::Bool(regex.is_match(a.as_ref())))
530 }
531
532 pub fn upper(args: Arguments) -> anyhow::Result<V> {
536 let a = args.str(0)?;
537 Ok(V::String(a.to_uppercase().into()))
538 }
539
540 pub fn lower(args: Arguments) -> anyhow::Result<V> {
544 let a = args.str(0)?;
545 Ok(V::String(a.to_lowercase().into()))
546 }
547
548 pub fn trim(args: Arguments) -> anyhow::Result<V> {
552 let a = args.str(0)?;
553 Ok(V::String(a.trim().into()))
554 }
555
556 pub fn extract(args: Arguments) -> anyhow::Result<V> {
561 let a = args.str(0)?;
562 let b = args.str(1)?;
563
564 let regex =
565 Regex::new(b.as_ref()).context("Invalid regular expression")?;
566
567 let captures = regex
568 .captures(a.as_ref())
569 .map(|capture| {
570 capture
571 .iter()
572 .map(|c| c.map(|c| c.as_str()))
573 .filter_map(|c| c)
574 .map(|s| V::String(Rc::from(s)))
575 .collect()
576 })
577 .unwrap_or_default();
578
579 Ok(V::from_array(captures))
580 }
581
582 pub fn split(args: Arguments) -> anyhow::Result<V> {
586 let a = args.str(0)?;
587 let b = args.str(1)?;
588
589 let arr = Vec::from_iter(
590 a.split(b).into_iter().map(|s| V::String(s.to_string().into())),
591 );
592
593 Ok(V::from_array(arr))
594 }
595
596 pub fn flatten(args: Arguments) -> anyhow::Result<V> {
600 let a = args.array(0)?;
601
602 let arr = a.borrow();
603 let mut flat_arr = Vec::with_capacity(arr.len());
604 arr.iter().for_each(|v| match v {
605 V::Array(b) => {
606 let arr = b.borrow();
607 arr.iter().for_each(|v| flat_arr.push(v.clone()))
608 },
609 _ => flat_arr.push(v.clone()),
610 });
611
612 Ok(V::from_array(flat_arr))
613 }
614
615 pub fn abs(args: Arguments) -> anyhow::Result<V> {
619 let a = args.number(0)?;
620 Ok(V::Number(a.abs()))
621 }
622
623 pub fn ceil(args: Arguments) -> anyhow::Result<V> {
627 let a = args.number(0)?;
628 Ok(V::Number(a.ceil()))
629 }
630
631 pub fn floor(args: Arguments) -> anyhow::Result<V> {
635 let a = args.number(0)?;
636 Ok(V::Number(a.floor()))
637 }
638
639 pub fn round(args: Arguments) -> anyhow::Result<V> {
643 let a = args.number(0)?;
644 let dp = args
645 .onumber(1)?
646 .map(|v| v.to_u32().context("Invalid number of decimal places"))
647 .transpose()?
648 .unwrap_or(0);
649
650 Ok(V::Number(a.round_dp_with_strategy(
651 dp,
652 RoundingStrategy::MidpointAwayFromZero,
653 )))
654 }
655
656 pub fn trunc(args: Arguments) -> anyhow::Result<V> {
660 let a = args.number(0)?;
661 let dp = args
662 .onumber(1)?
663 .map(|v| v.to_u32().context("Invalid number of decimal places"))
664 .transpose()?
665 .unwrap_or(0);
666
667 Ok(V::Number(a.trunc_with_scale(dp)))
668 }
669
670 pub fn rand(args: Arguments) -> anyhow::Result<V> {
674 let a = args.number(0)?;
675 let upper_range = a.round().to_i64().context("Invalid upper range")?;
676
677 let random_number = fastrand::i64(0..=upper_range);
678 Ok(V::Number(Decimal::from(random_number)))
679 }
680
681 pub fn min(args: Arguments) -> anyhow::Result<V> {
685 let a = __internal_number_or_date_array(&args, 0)?;
686
687 match a {
688 Either::Left(arr) => {
689 let min = arr.into_iter().min().context("Empty array")?;
690 Ok(V::Number(Decimal::from(min)))
691 },
692 Either::Right(arr) => {
693 let min = arr.into_iter().min().context("Empty array")?;
694 Ok(V::Dynamic(Rc::new(min)))
695 },
696 }
697 }
698
699 pub fn max(args: Arguments) -> anyhow::Result<V> {
703 let a = __internal_number_or_date_array(&args, 0)?;
704
705 match a {
706 Either::Left(arr) => {
707 let max = arr.into_iter().max().context("Empty array")?;
708 Ok(V::Number(Decimal::from(max)))
709 },
710 Either::Right(arr) => {
711 let max = arr.into_iter().max().context("Empty array")?;
712 Ok(V::Dynamic(Rc::new(max)))
713 },
714 }
715 }
716
717 pub fn avg(args: Arguments) -> anyhow::Result<V> {
721 let a = __internal_number_array(&args, 0)?;
722 let sum = a.iter().fold(Decimal::ZERO, |acc, x| acc + x);
723
724 Ok(V::Number(Decimal::from(
725 sum.checked_div(Decimal::from(a.len())).context("Empty array")?,
726 )))
727 }
728
729 pub fn sum(args: Arguments) -> anyhow::Result<V> {
733 let a = __internal_number_array(&args, 0)?;
734 let sum = a.iter().fold(Decimal::ZERO, |acc, v| acc + v);
735
736 Ok(V::Number(Decimal::from(sum)))
737 }
738
739 pub fn median(args: Arguments) -> anyhow::Result<V> {
743 let mut a = __internal_number_array(&args, 0)?;
744 a.sort();
745
746 let center = a.len() / 2;
747 if a.len() % 2 == 1 {
748 let center_num = a.get(center).context("Index out of bounds")?;
750 Ok(V::Number(*center_num))
751 } else {
752 let center_left =
754 a.get(center - 1).context("Index out of bounds")?;
755 let center_right = a.get(center).context("Index out of bounds")?;
756
757 let median = ((*center_left) + (*center_right)) / dec!(2);
758 Ok(V::Number(median))
759 }
760 }
761
762 pub fn mode(args: Arguments) -> anyhow::Result<V> {
766 let a = __internal_number_array(&args, 0)?;
767 let mut counts = BTreeMap::new();
768 for num in a {
769 *counts.entry(num).or_insert(0) += 1;
770 }
771
772 let most_common = counts
773 .into_iter()
774 .max_by_key(|&(_, count)| count)
775 .map(|(num, _)| num)
776 .context("Empty array")?;
777
778 Ok(V::Number(most_common))
779 }
780
781 pub fn to_type(args: Arguments) -> anyhow::Result<V> {
782 let a = args.var(0)?;
783 Ok(V::String(a.type_name().into()))
784 }
785
786 pub fn to_bool(args: Arguments) -> anyhow::Result<V> {
787 let a = args.var(0)?;
788 let val = match a {
789 V::Null => false,
790 V::Bool(v) => *v,
791 V::Number(n) => !n.is_zero(),
792 V::Array(_) | V::Object(_) | V::Dynamic(_) => true,
793 V::String(s) => match (*s).trim() {
794 "true" => true,
795 "false" => false,
796 _ => s.is_empty(),
797 },
798 };
799
800 Ok(V::Bool(val))
801 }
802
803 pub fn to_string(args: Arguments) -> anyhow::Result<V> {
804 let a = args.var(0)?;
805 let val = match a {
806 V::Null => Rc::from("null"),
807 V::Bool(v) => Rc::from(v.to_string().as_str()),
808 V::Number(n) => Rc::from(n.to_string().as_str()),
809 V::String(s) => s.clone(),
810 _ => {
811 return Err(anyhow!(
812 "Cannot convert type {} to string",
813 a.type_name()
814 ));
815 },
816 };
817
818 Ok(V::String(val))
819 }
820
821 pub fn to_number(args: Arguments) -> anyhow::Result<V> {
822 let a = args.var(0)?;
823 let val = match a {
824 V::Number(n) => *n,
825 V::String(str) => {
826 let s = str.trim();
827 Decimal::from_str_exact(s)
828 .or_else(|_| Decimal::from_scientific(s))
829 .context("Invalid number")?
830 },
831 V::Bool(b) => match *b {
832 true => Decimal::ONE,
833 false => Decimal::ZERO,
834 },
835 _ => {
836 return Err(anyhow!(
837 "Cannot convert type {} to number",
838 a.type_name()
839 ));
840 },
841 };
842
843 Ok(V::Number(val))
844 }
845
846 pub fn is_numeric(args: Arguments) -> anyhow::Result<V> {
847 let a = args.var(0)?;
848 let is_ok = match a {
849 V::Number(_) => true,
850 V::String(str) => {
851 let s = str.trim();
852 Decimal::from_str_exact(s)
853 .or_else(|_| Decimal::from_scientific(s))
854 .is_ok()
855 },
856 _ => false,
857 };
858
859 Ok(V::Bool(is_ok))
860 }
861
862 pub fn len(args: Arguments) -> anyhow::Result<V> {
863 let a = args.var(0)?;
864 let len = match a {
865 V::String(s) => s.len(),
866 V::Array(s) => {
867 let arr = s.borrow();
868 arr.len()
869 },
870 _ => {
871 return Err(anyhow!(
872 "Cannot determine len of type {}",
873 a.type_name()
874 ));
875 },
876 };
877
878 Ok(V::Number(len.into()))
879 }
880
881 pub fn contains(args: Arguments) -> anyhow::Result<V> {
882 let a = args.var(0)?;
883 let b = args.var(1)?;
884
885 let val = match (a, b) {
886 (V::String(a), V::String(b)) => a.contains(b.as_ref()),
887 (V::Array(a), _) => {
888 let arr = a.borrow();
889
890 arr.iter().any(|a| match (a, b) {
891 (V::Number(a), V::Number(b)) => a == b,
892 (V::String(a), V::String(b)) => a == b,
893 (V::Bool(a), V::Bool(b)) => a == b,
894 (V::Null, V::Null) => true,
895 _ => false,
896 })
897 },
898 _ => {
899 return Err(anyhow!(
900 "无法确定类型 {} 和 {} 的包含关系",
901 a.type_name(),
902 b.type_name()
903 ));
904 },
905 };
906
907 Ok(V::Bool(val))
908 }
909
910 pub fn fuzzy_match(args: Arguments) -> anyhow::Result<V> {
911 let a = args.var(0)?;
912 let b = args.str(1)?;
913
914 let val = match a {
915 V::String(a) => {
916 let sim = strsim::normalized_damerau_levenshtein(
917 a.as_ref(),
918 b.as_ref(),
919 );
920 V::Number(Decimal::from_f64(sim).unwrap_or(dec!(0)))
922 },
923 V::Array(_a) => {
924 let a = _a.borrow();
925 let mut sims = Vec::with_capacity(a.len());
926 for v in a.iter() {
927 let s = v.as_str().context("期望字符串数组")?;
928
929 let sim = Decimal::from_f64(
930 strsim::normalized_damerau_levenshtein(
931 s.as_ref(),
932 b.as_ref(),
933 ),
934 )
935 .unwrap_or(dec!(0));
936 sims.push(V::Number(sim));
937 }
938
939 V::from_array(sims)
940 },
941 _ => {
942 return Err(anyhow!("模糊匹配不适用于类型 {} ", a.type_name()));
943 },
944 };
945
946 Ok(val)
947 }
948
949 pub fn keys(args: Arguments) -> anyhow::Result<V> {
950 let a = args.var(0)?;
951 let var = match a {
952 V::Array(a) => {
953 let arr = a.borrow();
954 let indices = arr
955 .iter()
956 .enumerate()
957 .map(|(index, _)| V::Number(index.into()))
958 .collect();
959
960 V::from_array(indices)
961 },
962 V::Object(a) => {
963 let obj = a.borrow();
964 let keys =
965 obj.iter().map(|(key, _)| V::String(key.clone())).collect();
966
967 V::from_array(keys)
968 },
969 _ => {
970 return Err(anyhow!("无法确定类型 {} 的键", a.type_name()));
971 },
972 };
973
974 Ok(var)
975 }
976
977 pub fn values(args: Arguments) -> anyhow::Result<V> {
978 let a = args.object(0)?;
979 let obj = a.borrow();
980 let values: Vec<_> = obj.values().cloned().collect();
981
982 Ok(V::from_array(values))
983 }
984
985 pub fn date(args: Arguments) -> anyhow::Result<V> {
986 let provided = args.ovar(0);
987 let tz = args
988 .ostr(1)?
989 .map(|v| Tz::from_str(v).context("无效的时区"))
990 .transpose()?;
991
992 let date_time = match provided {
993 Some(v) => VmDate::new(v.clone(), tz),
994 None => VmDate::now(),
995 };
996
997 Ok(V::Dynamic(Rc::new(date_time)))
998 }
999}