Skip to main content

sql_cli/sql/functions/
date_time.rs

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
8// Helper function for parsing dates with multiple format support
9pub fn parse_datetime(s: &str) -> Result<DateTime<Utc>> {
10    // Try parsing as ISO 8601 with timezone first (most unambiguous)
11    if let Ok(dt) = s.parse::<DateTime<Utc>>() {
12        return Ok(dt);
13    }
14
15    // FIX Protocol timestamp format: YYYYMMDD-HH:MM:SS.sss (UTC)
16    // This is a standard financial messaging format (tag 60, 52, etc.)
17    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    // FIX format without milliseconds
21    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    // ISO formats (most common and unambiguous)
26    // With T separator
27    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    // With space separator
34    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    // Without milliseconds
41    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    // Get date notation preference
49    let date_notation = get_date_notation();
50
51    // Date notation preference for ambiguous formats like 04/09/2025
52    if date_notation == "european" {
53        // European formats (DD/MM/YYYY) - try first
54        // Date only formats
55        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        // With time formats (with milliseconds)
62        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        // US formats (MM/DD/YYYY) - fallback
73        // Date only formats
74        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        // With time formats (with milliseconds)
81        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        // US formats (MM/DD/YYYY) - default, try first
92        // Date only formats
93        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        // With time formats (with milliseconds)
100        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        // European formats (DD/MM/YYYY) - fallback
111        // Date only formats
112        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        // With time formats (with milliseconds)
119        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    // Excel/Windows format: DD-MMM-YYYY (e.g., 15-Jan-2024)
131    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    // Full month names: January 15, 2024 or 15 January 2024
136    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    // RFC3339 (e.g., 2024-01-15T10:30:00Z)
144    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
151/// NOW function - Returns current datetime
152pub 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
178/// TODAY function - Returns current date
179pub 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
203/// DATEDIFF function - Calculate difference between dates
204pub 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')",         // 14 (MySQL style)
216                "SELECT DATEDIFF('day', '2024-01-01', '2024-01-15')",  // 14 (with unit)
217                "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        // MySQL-style 2-arg form: DATEDIFF(date1, date2) -> days between
227        // Returns date1 - date2, matching MySQL behaviour
228        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        // 3-arg form: DATEDIFF(unit, date1, date2)
246        // First argument: unit
247        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        // Second argument: date1
254        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        // Third argument: date2
262        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        // Calculate difference based on unit
270        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                // Approximate months as 30.44 days
277                let duration = date2.signed_duration_since(date1);
278                duration.num_days() / 30
279            }
280            "year" | "years" => {
281                // Approximate years as 365.25 days
282                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
317/// DATEADD function - Add interval to date
318pub 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        // First argument: unit
340        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        // Second argument: amount to add
347        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        // Third argument: base date
355        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        // Add the specified amount based on unit
363        let result_date = match unit.as_str() {
364            "day" | "days" => base_date + chrono::Duration::days(amount),
365            "month" | "months" => {
366                // For months, we need to be careful about month boundaries
367                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                // Handle month overflow/underflow
375                while month > 12 {
376                    month -= 12;
377                    year += 1;
378                }
379                while month < 1 {
380                    month += 12;
381                    year -= 1;
382                }
383
384                // Create new date, handling day overflow (e.g., Jan 31 + 1 month = Feb 28/29)
385                let target_date =
386                    NaiveDate::from_ymd_opt(year, month as u32, day).unwrap_or_else(|| {
387                        // If day doesn't exist in target month, use the last day of that month
388                        // Try decreasing days until we find a valid one
389                        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                // Handle leap year edge case (Feb 29 -> Feb 28 in non-leap year)
408                let target_date =
409                    NaiveDate::from_ymd_opt(new_year, month, day).unwrap_or_else(|| {
410                        // If the date doesn't exist (e.g., Feb 29 in non-leap year), use Feb 28
411                        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        // Return as datetime string
428        Ok(DataValue::DateTime(
429            result_date.format("%Y-%m-%d %H:%M:%S").to_string(),
430        ))
431    }
432}
433
434/// UNIX_TIMESTAMP - Convert datetime to Unix epoch timestamp
435pub 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
476/// FROM_UNIXTIME - Convert Unix epoch timestamp to datetime
477pub 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
522/// TIME_BUCKET - Round timestamps down to bucket boundaries
523pub 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
574/// DAYOFWEEK function - returns day of week as number (0=Sunday, 6=Saturday)
575pub 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')", // Returns 1 (Monday)
587                "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        // chrono weekday: Mon=0, Tue=1, ..., Sun=6
607        // We want: Sun=0, Mon=1, ..., Sat=6
608        let chrono_weekday = dt.weekday().num_days_from_monday(); // 0=Mon, 6=Sun
609        let our_weekday = (chrono_weekday + 1) % 7; // Convert to 0=Sun, 6=Sat
610
611        Ok(DataValue::Integer(our_weekday as i64))
612    }
613}
614
615/// DAYNAME function - returns full day name (Monday, Tuesday, etc.)
616pub 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')",          // Returns 'Monday'
628                "SELECT DAYNAME('2024-01-01', 'short')", // Returns 'Mon'
629                "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(), // Mon, Tue, Wed, etc.
661            "full" | _ => dt.format("%A").to_string(), // Monday, Tuesday, etc.
662        };
663
664        Ok(DataValue::String(day_name))
665    }
666}
667
668/// ISLEAPYEAR function - returns true if the year is a leap year
669pub 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')", // Returns true
681                "SELECT ISLEAPYEAR(2024)",         // Returns true
682                "SELECT ISLEAPYEAR(2023)",         // Returns false
683            ],
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                // Try to parse as a year number first
697                if let Ok(y) = s.parse::<i32>() {
698                    y
699                } else {
700                    // Try to parse as a date
701                    let dt = parse_datetime(s.as_str())?;
702                    dt.year()
703                }
704            }
705            DataValue::InternedString(s) => {
706                // Try to parse as a year number first
707                if let Ok(y) = s.parse::<i32>() {
708                    y
709                } else {
710                    // Try to parse as a date
711                    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        // Leap year calculation
720        let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
721
722        Ok(DataValue::Boolean(is_leap))
723    }
724}
725
726/// WEEKOFYEAR function - returns the ISO week number (1-53)
727pub 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')", // Returns week number
739                "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
763/// QUARTER function - returns the quarter of the year (1-4)
764pub 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')", // Returns 1
776                "SELECT QUARTER('2024-07-01')", // Returns 3
777            ],
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
801/// YEAR function - extracts year from date
802pub 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')", // Returns 2024
814                "SELECT YEAR(NOW())",        // Returns current year
815            ],
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
836/// MONTH function - extracts month from date (1-12)
837pub 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')", // Returns 3
849                "SELECT MONTH(NOW())",        // Returns current month
850            ],
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
871/// DAY function - extracts day of month from date (1-31)
872pub 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')", // Returns 15
884                "SELECT DAY(NOW())",        // Returns current day
885            ],
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
906/// MONTHNAME function - returns the month name
907pub 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')",          // Returns 'January'
919                "SELECT MONTHNAME('2024-01-15', 'short')", // Returns 'Jan'
920            ],
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(), // Jan, Feb, Mar, etc.
951            "full" | _ => dt.format("%B").to_string(), // January, February, etc.
952        };
953
954        Ok(DataValue::String(month_name))
955    }
956}
957
958/// PARSE_DATETIME - Parse datetime with custom format string
959pub 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        // Try parsing with time component first
1000        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        // Try parsing as date only
1009        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
1025/// PARSE_DATETIME_UTC - Parse datetime and explicitly interpret as UTC
1026pub 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 format string provided, use it
1061        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            // Try parsing with time component first
1074            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            // Try parsing as date only
1083            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        // No format string - use auto-detection (existing parse_datetime function)
1099        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
1106/// DATETIME constructor - Create datetime from components
1107pub 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        // Extract components
1132        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        // is_utc parameter (default true)
1187        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        // Create date
1199        let date = NaiveDate::from_ymd_opt(year, month, day)
1200            .ok_or_else(|| anyhow!("Invalid date: {}-{}-{}", year, month, day))?;
1201
1202        // Create datetime
1203        let dt = date
1204            .and_hms_opt(hour, minute, second)
1205            .ok_or_else(|| anyhow!("Invalid time: {}:{}:{}", hour, minute, second))?;
1206
1207        // For now, always interpret as UTC (local timezone support would require additional dependencies)
1208        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
1216/// Register all date/time functions
1217pub 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    // Date extraction functions
1227    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    // Date utility functions
1235    registry.register(Box::new(IsLeapYearFunction));
1236    registry.register(Box::new(WeekOfYearFunction));
1237    registry.register(Box::new(QuarterFunction));
1238
1239    // Flexible parsing functions
1240    registry.register(Box::new(ParseDateTimeFunction));
1241    registry.register(Box::new(ParseDateTimeUtcFunction));
1242    registry.register(Box::new(DateTimeConstructor));
1243}