1use anyhow::{anyhow, Result};
2use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, TimeZone, Utc};
3
4use super::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
5use crate::config::global::get_date_notation;
6use crate::data::datatable::DataValue;
7
8pub fn parse_datetime(s: &str) -> Result<DateTime<Utc>> {
10 if let Ok(dt) = s.parse::<DateTime<Utc>>() {
12 return Ok(dt);
13 }
14
15 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y%m%d-%H:%M:%S%.3f") {
18 return Ok(Utc.from_utc_datetime(&dt));
19 }
20 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y%m%d-%H:%M:%S") {
22 return Ok(Utc.from_utc_datetime(&dt));
23 }
24
25 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") {
28 return Ok(Utc.from_utc_datetime(&dt));
29 }
30 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.3f") {
31 return Ok(Utc.from_utc_datetime(&dt));
32 }
33 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.3f") {
35 return Ok(Utc.from_utc_datetime(&dt));
36 }
37 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f") {
38 return Ok(Utc.from_utc_datetime(&dt));
39 }
40 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
42 return Ok(Utc.from_utc_datetime(&dt));
43 }
44 if let Ok(dt) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
45 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
46 }
47
48 let date_notation = get_date_notation();
50
51 if date_notation == "european" {
53 if let Ok(dt) = NaiveDate::parse_from_str(s, "%d/%m/%Y") {
56 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
57 }
58 if let Ok(dt) = NaiveDate::parse_from_str(s, "%d-%m-%Y") {
59 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
60 }
61 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%d/%m/%Y %H:%M:%S%.3f") {
63 return Ok(Utc.from_utc_datetime(&dt));
64 }
65 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%d/%m/%Y %H:%M:%S") {
66 return Ok(Utc.from_utc_datetime(&dt));
67 }
68 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%d-%m-%Y %H:%M:%S") {
69 return Ok(Utc.from_utc_datetime(&dt));
70 }
71
72 if let Ok(dt) = NaiveDate::parse_from_str(s, "%m/%d/%Y") {
75 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
76 }
77 if let Ok(dt) = NaiveDate::parse_from_str(s, "%m-%d-%Y") {
78 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
79 }
80 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%m/%d/%Y %H:%M:%S%.3f") {
82 return Ok(Utc.from_utc_datetime(&dt));
83 }
84 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%m/%d/%Y %H:%M:%S") {
85 return Ok(Utc.from_utc_datetime(&dt));
86 }
87 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%m-%d-%Y %H:%M:%S") {
88 return Ok(Utc.from_utc_datetime(&dt));
89 }
90 } else {
91 if let Ok(dt) = NaiveDate::parse_from_str(s, "%m/%d/%Y") {
94 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
95 }
96 if let Ok(dt) = NaiveDate::parse_from_str(s, "%m-%d-%Y") {
97 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
98 }
99 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%m/%d/%Y %H:%M:%S%.3f") {
101 return Ok(Utc.from_utc_datetime(&dt));
102 }
103 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%m/%d/%Y %H:%M:%S") {
104 return Ok(Utc.from_utc_datetime(&dt));
105 }
106 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%m-%d-%Y %H:%M:%S") {
107 return Ok(Utc.from_utc_datetime(&dt));
108 }
109
110 if let Ok(dt) = NaiveDate::parse_from_str(s, "%d/%m/%Y") {
113 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
114 }
115 if let Ok(dt) = NaiveDate::parse_from_str(s, "%d-%m-%Y") {
116 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
117 }
118 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%d/%m/%Y %H:%M:%S%.3f") {
120 return Ok(Utc.from_utc_datetime(&dt));
121 }
122 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%d/%m/%Y %H:%M:%S") {
123 return Ok(Utc.from_utc_datetime(&dt));
124 }
125 if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%d-%m-%Y %H:%M:%S") {
126 return Ok(Utc.from_utc_datetime(&dt));
127 }
128 }
129
130 if let Ok(dt) = NaiveDate::parse_from_str(s, "%d-%b-%Y") {
132 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
133 }
134
135 if let Ok(dt) = NaiveDate::parse_from_str(s, "%B %d, %Y") {
137 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
138 }
139 if let Ok(dt) = NaiveDate::parse_from_str(s, "%d %B %Y") {
140 return Ok(Utc.from_utc_datetime(&dt.and_hms_opt(0, 0, 0).unwrap()));
141 }
142
143 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
145 return Ok(dt.with_timezone(&Utc));
146 }
147
148 Err(anyhow!("Could not parse date: {}. Supported formats: YYYYMMDD-HH:MM:SS.sss (FIX), YYYY-MM-DD, MM/DD/YYYY, DD/MM/YYYY, DD-MMM-YYYY, Month DD YYYY", s))
149}
150
151pub struct NowFunction;
153
154impl SqlFunction for NowFunction {
155 fn signature(&self) -> FunctionSignature {
156 FunctionSignature {
157 name: "NOW",
158 category: FunctionCategory::Date,
159 arg_count: ArgCount::Fixed(0),
160 description: "Returns the current date and time",
161 returns: "DATETIME",
162 examples: vec![
163 "SELECT NOW()",
164 "SELECT * FROM orders WHERE created_at > NOW() - 7",
165 ],
166 }
167 }
168
169 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
170 self.validate_args(args)?;
171 let now = Utc::now();
172 Ok(DataValue::DateTime(
173 now.format("%Y-%m-%d %H:%M:%S").to_string(),
174 ))
175 }
176}
177
178pub struct TodayFunction;
180
181impl SqlFunction for TodayFunction {
182 fn signature(&self) -> FunctionSignature {
183 FunctionSignature {
184 name: "TODAY",
185 category: FunctionCategory::Date,
186 arg_count: ArgCount::Fixed(0),
187 description: "Returns today's date",
188 returns: "DATE",
189 examples: vec![
190 "SELECT TODAY()",
191 "SELECT * FROM events WHERE event_date = TODAY()",
192 ],
193 }
194 }
195
196 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
197 self.validate_args(args)?;
198 let today = Utc::now().date_naive();
199 Ok(DataValue::String(today.format("%Y-%m-%d").to_string()))
200 }
201}
202
203pub struct DateDiffFunction;
205
206impl SqlFunction for DateDiffFunction {
207 fn signature(&self) -> FunctionSignature {
208 FunctionSignature {
209 name: "DATEDIFF",
210 category: FunctionCategory::Date,
211 arg_count: ArgCount::Range(2, 3),
212 description: "Days between two dates (MySQL style, 2 args) or difference in a specified unit (3 args)",
213 returns: "INTEGER",
214 examples: vec![
215 "SELECT DATEDIFF('2024-01-15', '2024-01-01')", "SELECT DATEDIFF('day', '2024-01-01', '2024-01-15')", "SELECT DATEDIFF('month', start_date, end_date) FROM projects",
218 "SELECT DATEDIFF('year', birth_date, TODAY()) as age",
219 ],
220 }
221 }
222
223 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
224 self.validate_args(args)?;
225
226 if args.len() == 2 {
229 let date1 = match &args[0] {
230 DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
231 DataValue::InternedString(s) => parse_datetime(s.as_str())?,
232 DataValue::Null => return Ok(DataValue::Null),
233 _ => return Err(anyhow!("DATEDIFF requires date/datetime values")),
234 };
235 let date2 = match &args[1] {
236 DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
237 DataValue::InternedString(s) => parse_datetime(s.as_str())?,
238 DataValue::Null => return Ok(DataValue::Null),
239 _ => return Err(anyhow!("DATEDIFF requires date/datetime values")),
240 };
241 let duration = date1.signed_duration_since(date2);
242 return Ok(DataValue::Integer(duration.num_days()));
243 }
244
245 let unit = match &args[0] {
248 DataValue::String(s) => s.to_lowercase(),
249 DataValue::InternedString(s) => s.to_lowercase(),
250 _ => return Err(anyhow!("DATEDIFF unit must be a string")),
251 };
252
253 let date1 = match &args[1] {
255 DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
256 DataValue::InternedString(s) => parse_datetime(s.as_str())?,
257 DataValue::Null => return Ok(DataValue::Null),
258 _ => return Err(anyhow!("DATEDIFF requires date/datetime values")),
259 };
260
261 let date2 = match &args[2] {
263 DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
264 DataValue::InternedString(s) => parse_datetime(s.as_str())?,
265 DataValue::Null => return Ok(DataValue::Null),
266 _ => return Err(anyhow!("DATEDIFF requires date/datetime values")),
267 };
268
269 let diff = match unit.as_str() {
271 "day" | "days" => {
272 let duration = date2.signed_duration_since(date1);
273 duration.num_days()
274 }
275 "month" | "months" => {
276 let duration = date2.signed_duration_since(date1);
278 duration.num_days() / 30
279 }
280 "year" | "years" => {
281 let duration = date2.signed_duration_since(date1);
283 duration.num_days() / 365
284 }
285 "hour" | "hours" => {
286 let duration = date2.signed_duration_since(date1);
287 duration.num_hours()
288 }
289 "minute" | "minutes" => {
290 let duration = date2.signed_duration_since(date1);
291 duration.num_minutes()
292 }
293 "second" | "seconds" => {
294 let duration = date2.signed_duration_since(date1);
295 duration.num_seconds()
296 }
297 "millisecond" | "milliseconds" | "ms" => {
298 let duration = date2.signed_duration_since(date1);
299 duration.num_milliseconds()
300 }
301 "microsecond" | "microseconds" | "us" => {
302 let duration = date2.signed_duration_since(date1);
303 duration.num_microseconds().unwrap_or(0)
304 }
305 _ => {
306 return Err(anyhow!(
307 "Unknown DATEDIFF unit: {}. Use: day, month, year, hour, minute, second, millisecond, microsecond",
308 unit
309 ))
310 }
311 };
312
313 Ok(DataValue::Integer(diff))
314 }
315}
316
317pub struct DateAddFunction;
319
320impl SqlFunction for DateAddFunction {
321 fn signature(&self) -> FunctionSignature {
322 FunctionSignature {
323 name: "DATEADD",
324 category: FunctionCategory::Date,
325 arg_count: ArgCount::Fixed(3),
326 description: "Add a specified interval to a date",
327 returns: "DATETIME",
328 examples: vec![
329 "SELECT DATEADD('day', 7, '2024-01-01')",
330 "SELECT DATEADD('month', -1, NOW())",
331 "SELECT DATEADD('year', 1, hire_date) FROM employees",
332 ],
333 }
334 }
335
336 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
337 self.validate_args(args)?;
338
339 let unit = match &args[0] {
341 DataValue::String(s) => s.to_lowercase(),
342 DataValue::InternedString(s) => s.to_lowercase(),
343 _ => return Err(anyhow!("DATEADD unit must be a string")),
344 };
345
346 let amount = match &args[1] {
348 DataValue::Integer(i) => *i,
349 DataValue::Float(f) => *f as i64,
350 DataValue::Null => return Ok(DataValue::Null),
351 _ => return Err(anyhow!("DATEADD amount must be a number")),
352 };
353
354 let base_date = match &args[2] {
356 DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
357 DataValue::InternedString(s) => parse_datetime(s.as_str())?,
358 DataValue::Null => return Ok(DataValue::Null),
359 _ => return Err(anyhow!("DATEADD requires date/datetime values")),
360 };
361
362 let result_date = match unit.as_str() {
364 "day" | "days" => base_date + chrono::Duration::days(amount),
365 "month" | "months" => {
366 let naive = base_date.naive_utc();
368 let mut year = naive.year();
369 let mut month = naive.month() as i32;
370 let day = naive.day();
371
372 month += amount as i32;
373
374 while month > 12 {
376 month -= 12;
377 year += 1;
378 }
379 while month < 1 {
380 month += 12;
381 year -= 1;
382 }
383
384 let target_date =
386 NaiveDate::from_ymd_opt(year, month as u32, day).unwrap_or_else(|| {
387 for test_day in (1..=day).rev() {
390 if let Some(date) =
391 NaiveDate::from_ymd_opt(year, month as u32, test_day)
392 {
393 return date;
394 }
395 }
396 NaiveDate::from_ymd_opt(year, month as u32, 1).unwrap()
397 });
398
399 Utc.from_utc_datetime(&target_date.and_time(base_date.naive_utc().time()))
400 }
401 "year" | "years" => {
402 let naive = base_date.naive_utc();
403 let new_year = naive.year() + amount as i32;
404 let month = naive.month();
405 let day = naive.day();
406
407 let target_date =
409 NaiveDate::from_ymd_opt(new_year, month, day).unwrap_or_else(|| {
410 NaiveDate::from_ymd_opt(new_year, month, day - 1).unwrap()
412 });
413
414 Utc.from_utc_datetime(&target_date.and_time(base_date.naive_utc().time()))
415 }
416 "hour" | "hours" => base_date + chrono::Duration::hours(amount),
417 "minute" | "minutes" => base_date + chrono::Duration::minutes(amount),
418 "second" | "seconds" => base_date + chrono::Duration::seconds(amount),
419 _ => {
420 return Err(anyhow!(
421 "Unknown DATEADD unit: {}. Use: day, month, year, hour, minute, second",
422 unit
423 ))
424 }
425 };
426
427 Ok(DataValue::DateTime(
429 result_date.format("%Y-%m-%d %H:%M:%S").to_string(),
430 ))
431 }
432}
433
434pub struct UnixTimestamp;
436
437impl SqlFunction for UnixTimestamp {
438 fn signature(&self) -> FunctionSignature {
439 FunctionSignature {
440 name: "UNIX_TIMESTAMP",
441 category: FunctionCategory::Date,
442 arg_count: ArgCount::Fixed(1),
443 description:
444 "Convert datetime to Unix epoch timestamp (seconds since 1970-01-01 00:00:00 UTC)",
445 returns: "INTEGER (seconds since epoch)",
446 examples: vec![
447 "SELECT UNIX_TIMESTAMP('2024-01-01 00:00:00')",
448 "SELECT UNIX_TIMESTAMP('2024-01-01T12:30:45')",
449 "SELECT UNIX_TIMESTAMP(trade_time) FROM trades",
450 ],
451 }
452 }
453
454 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
455 if args.len() != 1 {
456 return Err(anyhow!("UNIX_TIMESTAMP expects exactly 1 argument"));
457 }
458
459 match &args[0] {
460 DataValue::DateTime(dt_str) | DataValue::String(dt_str) => {
461 let dt = parse_datetime(dt_str)?;
462 Ok(DataValue::Integer(dt.timestamp()))
463 }
464 DataValue::InternedString(dt_str) => {
465 let dt = parse_datetime(dt_str)?;
466 Ok(DataValue::Integer(dt.timestamp()))
467 }
468 DataValue::Null => Ok(DataValue::Null),
469 _ => Err(anyhow!(
470 "UNIX_TIMESTAMP expects a datetime or string argument"
471 )),
472 }
473 }
474}
475
476pub struct FromUnixTime;
478
479impl SqlFunction for FromUnixTime {
480 fn signature(&self) -> FunctionSignature {
481 FunctionSignature {
482 name: "FROM_UNIXTIME",
483 category: FunctionCategory::Date,
484 arg_count: ArgCount::Fixed(1),
485 description: "Convert Unix epoch timestamp to datetime string",
486 returns: "DATETIME string in ISO format",
487 examples: vec![
488 "SELECT FROM_UNIXTIME(1704067200)",
489 "SELECT FROM_UNIXTIME(timestamp_col) FROM data",
490 ],
491 }
492 }
493
494 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
495 if args.len() != 1 {
496 return Err(anyhow!("FROM_UNIXTIME expects exactly 1 argument"));
497 }
498
499 match &args[0] {
500 DataValue::Integer(timestamp) => {
501 let dt = DateTime::<Utc>::from_timestamp(*timestamp, 0)
502 .ok_or_else(|| anyhow!("Invalid timestamp: {}", timestamp))?;
503 Ok(DataValue::DateTime(
504 dt.format("%Y-%m-%d %H:%M:%S").to_string(),
505 ))
506 }
507 DataValue::Float(timestamp) => {
508 let secs = timestamp.floor() as i64;
509 let nanos = ((timestamp - timestamp.floor()) * 1_000_000_000.0) as u32;
510 let dt = DateTime::<Utc>::from_timestamp(secs, nanos)
511 .ok_or_else(|| anyhow!("Invalid timestamp: {}", timestamp))?;
512 Ok(DataValue::DateTime(
513 dt.format("%Y-%m-%d %H:%M:%S%.f").to_string(),
514 ))
515 }
516 DataValue::Null => Ok(DataValue::Null),
517 _ => Err(anyhow!("FROM_UNIXTIME expects a numeric timestamp")),
518 }
519 }
520}
521
522pub struct TimeBucket;
524
525impl SqlFunction for TimeBucket {
526 fn signature(&self) -> FunctionSignature {
527 FunctionSignature {
528 name: "TIME_BUCKET",
529 category: FunctionCategory::Date,
530 arg_count: ArgCount::Fixed(2),
531 description: "Round timestamp down to bucket boundary (for time-based grouping)",
532 returns: "INTEGER (bucket timestamp)",
533 examples: vec![
534 "SELECT TIME_BUCKET(300, UNIX_TIMESTAMP(trade_time)) as bucket FROM trades -- 5 minute buckets",
535 "SELECT TIME_BUCKET(3600, UNIX_TIMESTAMP(trade_time)) as hour FROM trades -- 1 hour buckets",
536 "SELECT TIME_BUCKET(60, timestamp_col) as minute FROM data -- 1 minute buckets",
537 ],
538 }
539 }
540
541 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
542 if args.len() != 2 {
543 return Err(anyhow!(
544 "TIME_BUCKET expects exactly 2 arguments: bucket_size, timestamp"
545 ));
546 }
547
548 let bucket_size = match &args[0] {
549 DataValue::Integer(size) => *size,
550 DataValue::Float(size) => *size as i64,
551 _ => return Err(anyhow!("TIME_BUCKET bucket_size must be numeric")),
552 };
553
554 if bucket_size <= 0 {
555 return Err(anyhow!("TIME_BUCKET bucket_size must be positive"));
556 }
557
558 match &args[1] {
559 DataValue::Integer(timestamp) => {
560 let bucket = (timestamp / bucket_size) * bucket_size;
561 Ok(DataValue::Integer(bucket))
562 }
563 DataValue::Float(timestamp) => {
564 let ts = *timestamp as i64;
565 let bucket = (ts / bucket_size) * bucket_size;
566 Ok(DataValue::Integer(bucket))
567 }
568 DataValue::Null => Ok(DataValue::Null),
569 _ => Err(anyhow!("TIME_BUCKET timestamp must be numeric")),
570 }
571 }
572}
573
574pub struct DayOfWeekFunction;
576
577impl SqlFunction for DayOfWeekFunction {
578 fn signature(&self) -> FunctionSignature {
579 FunctionSignature {
580 name: "DAYOFWEEK",
581 category: FunctionCategory::Date,
582 arg_count: ArgCount::Fixed(1),
583 description: "Returns day of week as number (0=Sunday, 6=Saturday)",
584 returns: "INTEGER",
585 examples: vec![
586 "SELECT DAYOFWEEK('2024-01-01')", "SELECT DAYOFWEEK(NOW())",
588 "SELECT DAYOFWEEK(date_column) FROM table",
589 ],
590 }
591 }
592
593 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
594 if args.len() != 1 {
595 return Err(anyhow!("DAYOFWEEK expects exactly 1 argument"));
596 }
597
598 let date_str = match &args[0] {
599 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
600 DataValue::InternedString(s) => s.as_str(),
601 DataValue::Null => return Ok(DataValue::Null),
602 _ => return Err(anyhow!("DAYOFWEEK expects a date/datetime string")),
603 };
604
605 let dt = parse_datetime(date_str)?;
606 let chrono_weekday = dt.weekday().num_days_from_monday(); let our_weekday = (chrono_weekday + 1) % 7; Ok(DataValue::Integer(our_weekday as i64))
612 }
613}
614
615pub struct DayNameFunction;
617
618impl SqlFunction for DayNameFunction {
619 fn signature(&self) -> FunctionSignature {
620 FunctionSignature {
621 name: "DAYNAME",
622 category: FunctionCategory::Date,
623 arg_count: ArgCount::Range(1, 2),
624 description: "Returns day name. Optional second arg: 'full' (default) or 'short'",
625 returns: "STRING",
626 examples: vec![
627 "SELECT DAYNAME('2024-01-01')", "SELECT DAYNAME('2024-01-01', 'short')", "SELECT DAYNAME(NOW(), 'full')",
630 ],
631 }
632 }
633
634 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
635 if args.is_empty() || args.len() > 2 {
636 return Err(anyhow!("DAYNAME expects 1 or 2 arguments"));
637 }
638
639 let date_str = match &args[0] {
640 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
641 DataValue::InternedString(s) => s.as_str(),
642 DataValue::Null => return Ok(DataValue::Null),
643 _ => return Err(anyhow!("DAYNAME expects a date/datetime string")),
644 };
645
646 let format = if args.len() == 2 {
647 match &args[1] {
648 DataValue::String(s) => s.as_str(),
649 DataValue::InternedString(s) => s.as_str(),
650 DataValue::Null => "full",
651 _ => return Err(anyhow!("DAYNAME format must be 'full' or 'short'")),
652 }
653 } else {
654 "full"
655 };
656
657 let dt = parse_datetime(date_str)?;
658
659 let day_name = match format {
660 "short" => dt.format("%a").to_string(), "full" | _ => dt.format("%A").to_string(), };
663
664 Ok(DataValue::String(day_name))
665 }
666}
667
668pub struct IsLeapYearFunction;
670
671impl SqlFunction for IsLeapYearFunction {
672 fn signature(&self) -> FunctionSignature {
673 FunctionSignature {
674 name: "ISLEAPYEAR",
675 category: FunctionCategory::Date,
676 arg_count: ArgCount::Fixed(1),
677 description: "Returns true if the year is a leap year",
678 returns: "BOOLEAN",
679 examples: vec![
680 "SELECT ISLEAPYEAR('2024-01-01')", "SELECT ISLEAPYEAR(2024)", "SELECT ISLEAPYEAR(2023)", ],
684 }
685 }
686
687 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
688 if args.len() != 1 {
689 return Err(anyhow!("ISLEAPYEAR expects exactly 1 argument"));
690 }
691
692 let year = match &args[0] {
693 DataValue::Integer(y) => *y as i32,
694 DataValue::Float(f) => *f as i32,
695 DataValue::String(s) | DataValue::DateTime(s) => {
696 if let Ok(y) = s.parse::<i32>() {
698 y
699 } else {
700 let dt = parse_datetime(s.as_str())?;
702 dt.year()
703 }
704 }
705 DataValue::InternedString(s) => {
706 if let Ok(y) = s.parse::<i32>() {
708 y
709 } else {
710 let dt = parse_datetime(s.as_str())?;
712 dt.year()
713 }
714 }
715 DataValue::Null => return Ok(DataValue::Null),
716 _ => return Err(anyhow!("ISLEAPYEAR expects a year number or date")),
717 };
718
719 let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
721
722 Ok(DataValue::Boolean(is_leap))
723 }
724}
725
726pub struct WeekOfYearFunction;
728
729impl SqlFunction for WeekOfYearFunction {
730 fn signature(&self) -> FunctionSignature {
731 FunctionSignature {
732 name: "WEEKOFYEAR",
733 category: FunctionCategory::Date,
734 arg_count: ArgCount::Fixed(1),
735 description: "Returns the ISO week number of the year (1-53)",
736 returns: "INTEGER",
737 examples: vec![
738 "SELECT WEEKOFYEAR('2024-01-01')", "SELECT WEEKOFYEAR(NOW())",
740 ],
741 }
742 }
743
744 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
745 if args.len() != 1 {
746 return Err(anyhow!("WEEKOFYEAR expects exactly 1 argument"));
747 }
748
749 let date_str = match &args[0] {
750 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
751 DataValue::InternedString(s) => s.as_str(),
752 DataValue::Null => return Ok(DataValue::Null),
753 _ => return Err(anyhow!("WEEKOFYEAR expects a date/datetime string")),
754 };
755
756 let dt = parse_datetime(date_str)?;
757 let week = dt.iso_week().week();
758
759 Ok(DataValue::Integer(week as i64))
760 }
761}
762
763pub struct QuarterFunction;
765
766impl SqlFunction for QuarterFunction {
767 fn signature(&self) -> FunctionSignature {
768 FunctionSignature {
769 name: "QUARTER",
770 category: FunctionCategory::Date,
771 arg_count: ArgCount::Fixed(1),
772 description: "Returns the quarter of the year (1-4)",
773 returns: "INTEGER",
774 examples: vec![
775 "SELECT QUARTER('2024-01-15')", "SELECT QUARTER('2024-07-01')", ],
778 }
779 }
780
781 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
782 if args.len() != 1 {
783 return Err(anyhow!("QUARTER expects exactly 1 argument"));
784 }
785
786 let date_str = match &args[0] {
787 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
788 DataValue::InternedString(s) => s.as_str(),
789 DataValue::Null => return Ok(DataValue::Null),
790 _ => return Err(anyhow!("QUARTER expects a date/datetime string")),
791 };
792
793 let dt = parse_datetime(date_str)?;
794 let month = dt.month();
795 let quarter = (month - 1) / 3 + 1;
796
797 Ok(DataValue::Integer(quarter as i64))
798 }
799}
800
801pub struct YearFunction;
803
804impl SqlFunction for YearFunction {
805 fn signature(&self) -> FunctionSignature {
806 FunctionSignature {
807 name: "YEAR",
808 category: FunctionCategory::Date,
809 arg_count: ArgCount::Fixed(1),
810 description: "Returns the year from a date",
811 returns: "INTEGER",
812 examples: vec![
813 "SELECT YEAR('2024-03-15')", "SELECT YEAR(NOW())", ],
816 }
817 }
818
819 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
820 if args.len() != 1 {
821 return Err(anyhow!("YEAR expects exactly 1 argument"));
822 }
823
824 let date_str = match &args[0] {
825 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
826 DataValue::InternedString(s) => s.as_str(),
827 DataValue::Null => return Ok(DataValue::Null),
828 _ => return Err(anyhow!("YEAR expects a date/datetime string")),
829 };
830
831 let dt = parse_datetime(date_str)?;
832 Ok(DataValue::Float(dt.year() as f64))
833 }
834}
835
836pub struct MonthFunction;
838
839impl SqlFunction for MonthFunction {
840 fn signature(&self) -> FunctionSignature {
841 FunctionSignature {
842 name: "MONTH",
843 category: FunctionCategory::Date,
844 arg_count: ArgCount::Fixed(1),
845 description: "Returns the month from a date (1-12)",
846 returns: "INTEGER",
847 examples: vec![
848 "SELECT MONTH('2024-03-15')", "SELECT MONTH(NOW())", ],
851 }
852 }
853
854 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
855 if args.len() != 1 {
856 return Err(anyhow!("MONTH expects exactly 1 argument"));
857 }
858
859 let date_str = match &args[0] {
860 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
861 DataValue::InternedString(s) => s.as_str(),
862 DataValue::Null => return Ok(DataValue::Null),
863 _ => return Err(anyhow!("MONTH expects a date/datetime string")),
864 };
865
866 let dt = parse_datetime(date_str)?;
867 Ok(DataValue::Float(dt.month() as f64))
868 }
869}
870
871pub struct DayFunction;
873
874impl SqlFunction for DayFunction {
875 fn signature(&self) -> FunctionSignature {
876 FunctionSignature {
877 name: "DAY",
878 category: FunctionCategory::Date,
879 arg_count: ArgCount::Fixed(1),
880 description: "Returns the day of month from a date (1-31)",
881 returns: "INTEGER",
882 examples: vec![
883 "SELECT DAY('2024-03-15')", "SELECT DAY(NOW())", ],
886 }
887 }
888
889 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
890 if args.len() != 1 {
891 return Err(anyhow!("DAY expects exactly 1 argument"));
892 }
893
894 let date_str = match &args[0] {
895 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
896 DataValue::InternedString(s) => s.as_str(),
897 DataValue::Null => return Ok(DataValue::Null),
898 _ => return Err(anyhow!("DAY expects a date/datetime string")),
899 };
900
901 let dt = parse_datetime(date_str)?;
902 Ok(DataValue::Float(dt.day() as f64))
903 }
904}
905
906pub struct MonthNameFunction;
908
909impl SqlFunction for MonthNameFunction {
910 fn signature(&self) -> FunctionSignature {
911 FunctionSignature {
912 name: "MONTHNAME",
913 category: FunctionCategory::Date,
914 arg_count: ArgCount::Range(1, 2),
915 description: "Returns month name. Optional second arg: 'full' (default) or 'short'",
916 returns: "STRING",
917 examples: vec![
918 "SELECT MONTHNAME('2024-01-15')", "SELECT MONTHNAME('2024-01-15', 'short')", ],
921 }
922 }
923
924 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
925 if args.is_empty() || args.len() > 2 {
926 return Err(anyhow!("MONTHNAME expects 1 or 2 arguments"));
927 }
928
929 let date_str = match &args[0] {
930 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
931 DataValue::InternedString(s) => s.as_str(),
932 DataValue::Null => return Ok(DataValue::Null),
933 _ => return Err(anyhow!("MONTHNAME expects a date/datetime string")),
934 };
935
936 let format = if args.len() == 2 {
937 match &args[1] {
938 DataValue::String(s) => s.as_str(),
939 DataValue::InternedString(s) => s.as_str(),
940 DataValue::Null => "full",
941 _ => return Err(anyhow!("MONTHNAME format must be 'full' or 'short'")),
942 }
943 } else {
944 "full"
945 };
946
947 let dt = parse_datetime(date_str)?;
948
949 let month_name = match format {
950 "short" => dt.format("%b").to_string(), "full" | _ => dt.format("%B").to_string(), };
953
954 Ok(DataValue::String(month_name))
955 }
956}
957
958pub struct ParseDateTimeFunction;
960
961impl SqlFunction for ParseDateTimeFunction {
962 fn signature(&self) -> FunctionSignature {
963 FunctionSignature {
964 name: "PARSE_DATETIME",
965 category: FunctionCategory::Date,
966 arg_count: ArgCount::Fixed(2),
967 description: "Parse datetime string with custom format (uses chrono strftime format)",
968 returns: "DATETIME",
969 examples: vec![
970 "SELECT PARSE_DATETIME('15/01/2024', '%d/%m/%Y')",
971 "SELECT PARSE_DATETIME('Jan 15, 2024 14:30', '%b %d, %Y %H:%M')",
972 "SELECT PARSE_DATETIME('2024-01-15T14:30:00', '%Y-%m-%dT%H:%M:%S')",
973 "SELECT PARSE_DATETIME(date_string, '%Y%m%d-%H:%M:%S%.3f') FROM data -- FIX format",
974 ],
975 }
976 }
977
978 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
979 self.validate_args(args)?;
980
981 let date_str = match &args[0] {
982 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
983 DataValue::InternedString(s) => s.as_str(),
984 DataValue::Null => return Ok(DataValue::Null),
985 _ => return Err(anyhow!("PARSE_DATETIME expects a string as first argument")),
986 };
987
988 let format_str = match &args[1] {
989 DataValue::String(s) => s.as_str(),
990 DataValue::InternedString(s) => s.as_str(),
991 DataValue::Null => return Ok(DataValue::Null),
992 _ => {
993 return Err(anyhow!(
994 "PARSE_DATETIME expects a format string as second argument"
995 ))
996 }
997 };
998
999 if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, format_str) {
1001 return Ok(DataValue::DateTime(
1002 Utc.from_utc_datetime(&dt)
1003 .format("%Y-%m-%d %H:%M:%S%.3f")
1004 .to_string(),
1005 ));
1006 }
1007
1008 if let Ok(d) = NaiveDate::parse_from_str(date_str, format_str) {
1010 return Ok(DataValue::DateTime(
1011 Utc.from_utc_datetime(&d.and_hms_opt(0, 0, 0).unwrap())
1012 .format("%Y-%m-%d %H:%M:%S%.3f")
1013 .to_string(),
1014 ));
1015 }
1016
1017 Err(anyhow!(
1018 "Failed to parse '{}' with format '{}'. See https://docs.rs/chrono/latest/chrono/format/strftime/index.html",
1019 date_str,
1020 format_str
1021 ))
1022 }
1023}
1024
1025pub struct ParseDateTimeUtcFunction;
1027
1028impl SqlFunction for ParseDateTimeUtcFunction {
1029 fn signature(&self) -> FunctionSignature {
1030 FunctionSignature {
1031 name: "PARSE_DATETIME_UTC",
1032 category: FunctionCategory::Date,
1033 arg_count: ArgCount::Range(1, 2),
1034 description: "Parse datetime as UTC. With 1 arg: auto-detect format. With 2 args: use custom format",
1035 returns: "DATETIME (UTC)",
1036 examples: vec![
1037 "SELECT PARSE_DATETIME_UTC('2024-01-15 14:30:00')",
1038 "SELECT PARSE_DATETIME_UTC('20250925-14:52:15.567') -- FIX format auto-detected",
1039 "SELECT PARSE_DATETIME_UTC('15/01/2024 14:30', '%d/%m/%Y %H:%M')",
1040 ],
1041 }
1042 }
1043
1044 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1045 if args.is_empty() || args.len() > 2 {
1046 return Err(anyhow!("PARSE_DATETIME_UTC expects 1 or 2 arguments"));
1047 }
1048
1049 let date_str = match &args[0] {
1050 DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
1051 DataValue::InternedString(s) => s.as_str(),
1052 DataValue::Null => return Ok(DataValue::Null),
1053 _ => {
1054 return Err(anyhow!(
1055 "PARSE_DATETIME_UTC expects a string as first argument"
1056 ))
1057 }
1058 };
1059
1060 if args.len() == 2 {
1062 let format_str = match &args[1] {
1063 DataValue::String(s) => s.as_str(),
1064 DataValue::InternedString(s) => s.as_str(),
1065 DataValue::Null => return Ok(DataValue::Null),
1066 _ => {
1067 return Err(anyhow!(
1068 "PARSE_DATETIME_UTC expects a format string as second argument"
1069 ))
1070 }
1071 };
1072
1073 if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, format_str) {
1075 return Ok(DataValue::DateTime(
1076 Utc.from_utc_datetime(&dt)
1077 .format("%Y-%m-%d %H:%M:%S%.3f")
1078 .to_string(),
1079 ));
1080 }
1081
1082 if let Ok(d) = NaiveDate::parse_from_str(date_str, format_str) {
1084 return Ok(DataValue::DateTime(
1085 Utc.from_utc_datetime(&d.and_hms_opt(0, 0, 0).unwrap())
1086 .format("%Y-%m-%d %H:%M:%S%.3f")
1087 .to_string(),
1088 ));
1089 }
1090
1091 return Err(anyhow!(
1092 "Failed to parse '{}' with format '{}'",
1093 date_str,
1094 format_str
1095 ));
1096 }
1097
1098 let dt = parse_datetime(date_str)?;
1100 Ok(DataValue::DateTime(
1101 dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
1102 ))
1103 }
1104}
1105
1106pub struct DateTimeConstructor;
1108
1109impl SqlFunction for DateTimeConstructor {
1110 fn signature(&self) -> FunctionSignature {
1111 FunctionSignature {
1112 name: "DATETIME",
1113 category: FunctionCategory::Date,
1114 arg_count: ArgCount::Range(3, 7),
1115 description: "Create datetime from components: (year, month, day, [hour], [minute], [second], [is_utc])",
1116 returns: "DATETIME",
1117 examples: vec![
1118 "SELECT DATETIME(2024, 1, 15)",
1119 "SELECT DATETIME(2024, 1, 15, 14, 30, 0)",
1120 "SELECT DATETIME(2024, 12, 31, 23, 59, 59)",
1121 "-- Note: All times are interpreted as UTC",
1122 ],
1123 }
1124 }
1125
1126 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1127 if args.len() < 3 || args.len() > 7 {
1128 return Err(anyhow!("DATETIME expects 3-7 arguments: year, month, day, [hour], [minute], [second], [is_utc]"));
1129 }
1130
1131 let year = match &args[0] {
1133 DataValue::Integer(i) => *i as i32,
1134 DataValue::Float(f) => *f as i32,
1135 DataValue::Null => return Ok(DataValue::Null),
1136 _ => return Err(anyhow!("DATETIME year must be numeric")),
1137 };
1138
1139 let month = match &args[1] {
1140 DataValue::Integer(i) => *i as u32,
1141 DataValue::Float(f) => *f as u32,
1142 DataValue::Null => return Ok(DataValue::Null),
1143 _ => return Err(anyhow!("DATETIME month must be numeric")),
1144 };
1145
1146 let day = match &args[2] {
1147 DataValue::Integer(i) => *i as u32,
1148 DataValue::Float(f) => *f as u32,
1149 DataValue::Null => return Ok(DataValue::Null),
1150 _ => return Err(anyhow!("DATETIME day must be numeric")),
1151 };
1152
1153 let hour = if args.len() > 3 {
1154 match &args[3] {
1155 DataValue::Integer(i) => *i as u32,
1156 DataValue::Float(f) => *f as u32,
1157 DataValue::Null => return Ok(DataValue::Null),
1158 _ => return Err(anyhow!("DATETIME hour must be numeric")),
1159 }
1160 } else {
1161 0
1162 };
1163
1164 let minute = if args.len() > 4 {
1165 match &args[4] {
1166 DataValue::Integer(i) => *i as u32,
1167 DataValue::Float(f) => *f as u32,
1168 DataValue::Null => return Ok(DataValue::Null),
1169 _ => return Err(anyhow!("DATETIME minute must be numeric")),
1170 }
1171 } else {
1172 0
1173 };
1174
1175 let second = if args.len() > 5 {
1176 match &args[5] {
1177 DataValue::Integer(i) => *i as u32,
1178 DataValue::Float(f) => *f as u32,
1179 DataValue::Null => return Ok(DataValue::Null),
1180 _ => return Err(anyhow!("DATETIME second must be numeric")),
1181 }
1182 } else {
1183 0
1184 };
1185
1186 let _is_utc = if args.len() > 6 {
1188 match &args[6] {
1189 DataValue::Boolean(b) => *b,
1190 DataValue::Integer(i) => *i != 0,
1191 DataValue::Null => true,
1192 _ => return Err(anyhow!("DATETIME is_utc must be boolean")),
1193 }
1194 } else {
1195 true
1196 };
1197
1198 let date = NaiveDate::from_ymd_opt(year, month, day)
1200 .ok_or_else(|| anyhow!("Invalid date: {}-{}-{}", year, month, day))?;
1201
1202 let dt = date
1204 .and_hms_opt(hour, minute, second)
1205 .ok_or_else(|| anyhow!("Invalid time: {}:{}:{}", hour, minute, second))?;
1206
1207 let utc_dt = Utc.from_utc_datetime(&dt);
1209
1210 Ok(DataValue::DateTime(
1211 utc_dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
1212 ))
1213 }
1214}
1215
1216pub fn register_date_time_functions(registry: &mut super::FunctionRegistry) {
1218 registry.register(Box::new(NowFunction));
1219 registry.register(Box::new(TodayFunction));
1220 registry.register(Box::new(DateDiffFunction));
1221 registry.register(Box::new(DateAddFunction));
1222 registry.register(Box::new(UnixTimestamp));
1223 registry.register(Box::new(FromUnixTime));
1224 registry.register(Box::new(TimeBucket));
1225
1226 registry.register(Box::new(YearFunction));
1228 registry.register(Box::new(MonthFunction));
1229 registry.register(Box::new(DayFunction));
1230 registry.register(Box::new(DayOfWeekFunction));
1231 registry.register(Box::new(DayNameFunction));
1232 registry.register(Box::new(MonthNameFunction));
1233
1234 registry.register(Box::new(IsLeapYearFunction));
1236 registry.register(Box::new(WeekOfYearFunction));
1237 registry.register(Box::new(QuarterFunction));
1238
1239 registry.register(Box::new(ParseDateTimeFunction));
1241 registry.register(Box::new(ParseDateTimeUtcFunction));
1242 registry.register(Box::new(DateTimeConstructor));
1243}