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::Fixed(3),
212            description: "Calculate the difference between two dates in the specified unit",
213            returns: "INTEGER",
214            examples: vec![
215                "SELECT DATEDIFF('day', '2024-01-01', '2024-01-15')",
216                "SELECT DATEDIFF('month', start_date, end_date) FROM projects",
217                "SELECT DATEDIFF('year', birth_date, TODAY()) as age",
218            ],
219        }
220    }
221
222    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
223        self.validate_args(args)?;
224
225        // First argument: unit
226        let unit = match &args[0] {
227            DataValue::String(s) => s.to_lowercase(),
228            DataValue::InternedString(s) => s.to_lowercase(),
229            _ => return Err(anyhow!("DATEDIFF unit must be a string")),
230        };
231
232        // Second argument: date1
233        let date1 = match &args[1] {
234            DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
235            DataValue::InternedString(s) => parse_datetime(s.as_str())?,
236            DataValue::Null => return Ok(DataValue::Null),
237            _ => return Err(anyhow!("DATEDIFF requires date/datetime values")),
238        };
239
240        // Third argument: date2
241        let date2 = match &args[2] {
242            DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
243            DataValue::InternedString(s) => parse_datetime(s.as_str())?,
244            DataValue::Null => return Ok(DataValue::Null),
245            _ => return Err(anyhow!("DATEDIFF requires date/datetime values")),
246        };
247
248        // Calculate difference based on unit
249        let diff = match unit.as_str() {
250            "day" | "days" => {
251                let duration = date2.signed_duration_since(date1);
252                duration.num_days()
253            }
254            "month" | "months" => {
255                // Approximate months as 30.44 days
256                let duration = date2.signed_duration_since(date1);
257                duration.num_days() / 30
258            }
259            "year" | "years" => {
260                // Approximate years as 365.25 days
261                let duration = date2.signed_duration_since(date1);
262                duration.num_days() / 365
263            }
264            "hour" | "hours" => {
265                let duration = date2.signed_duration_since(date1);
266                duration.num_hours()
267            }
268            "minute" | "minutes" => {
269                let duration = date2.signed_duration_since(date1);
270                duration.num_minutes()
271            }
272            "second" | "seconds" => {
273                let duration = date2.signed_duration_since(date1);
274                duration.num_seconds()
275            }
276            "millisecond" | "milliseconds" | "ms" => {
277                let duration = date2.signed_duration_since(date1);
278                duration.num_milliseconds()
279            }
280            "microsecond" | "microseconds" | "us" => {
281                let duration = date2.signed_duration_since(date1);
282                duration.num_microseconds().unwrap_or(0)
283            }
284            _ => {
285                return Err(anyhow!(
286                    "Unknown DATEDIFF unit: {}. Use: day, month, year, hour, minute, second, millisecond, microsecond",
287                    unit
288                ))
289            }
290        };
291
292        Ok(DataValue::Integer(diff))
293    }
294}
295
296/// DATEADD function - Add interval to date
297pub struct DateAddFunction;
298
299impl SqlFunction for DateAddFunction {
300    fn signature(&self) -> FunctionSignature {
301        FunctionSignature {
302            name: "DATEADD",
303            category: FunctionCategory::Date,
304            arg_count: ArgCount::Fixed(3),
305            description: "Add a specified interval to a date",
306            returns: "DATETIME",
307            examples: vec![
308                "SELECT DATEADD('day', 7, '2024-01-01')",
309                "SELECT DATEADD('month', -1, NOW())",
310                "SELECT DATEADD('year', 1, hire_date) FROM employees",
311            ],
312        }
313    }
314
315    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
316        self.validate_args(args)?;
317
318        // First argument: unit
319        let unit = match &args[0] {
320            DataValue::String(s) => s.to_lowercase(),
321            DataValue::InternedString(s) => s.to_lowercase(),
322            _ => return Err(anyhow!("DATEADD unit must be a string")),
323        };
324
325        // Second argument: amount to add
326        let amount = match &args[1] {
327            DataValue::Integer(i) => *i,
328            DataValue::Float(f) => *f as i64,
329            DataValue::Null => return Ok(DataValue::Null),
330            _ => return Err(anyhow!("DATEADD amount must be a number")),
331        };
332
333        // Third argument: base date
334        let base_date = match &args[2] {
335            DataValue::String(s) | DataValue::DateTime(s) => parse_datetime(s)?,
336            DataValue::InternedString(s) => parse_datetime(s.as_str())?,
337            DataValue::Null => return Ok(DataValue::Null),
338            _ => return Err(anyhow!("DATEADD requires date/datetime values")),
339        };
340
341        // Add the specified amount based on unit
342        let result_date = match unit.as_str() {
343            "day" | "days" => base_date + chrono::Duration::days(amount),
344            "month" | "months" => {
345                // For months, we need to be careful about month boundaries
346                let naive = base_date.naive_utc();
347                let mut year = naive.year();
348                let mut month = naive.month() as i32;
349                let day = naive.day();
350
351                month += amount as i32;
352
353                // Handle month overflow/underflow
354                while month > 12 {
355                    month -= 12;
356                    year += 1;
357                }
358                while month < 1 {
359                    month += 12;
360                    year -= 1;
361                }
362
363                // Create new date, handling day overflow (e.g., Jan 31 + 1 month = Feb 28/29)
364                let target_date =
365                    NaiveDate::from_ymd_opt(year, month as u32, day).unwrap_or_else(|| {
366                        // If day doesn't exist in target month, use the last day of that month
367                        // Try decreasing days until we find a valid one
368                        for test_day in (1..=day).rev() {
369                            if let Some(date) =
370                                NaiveDate::from_ymd_opt(year, month as u32, test_day)
371                            {
372                                return date;
373                            }
374                        }
375                        NaiveDate::from_ymd_opt(year, month as u32, 1).unwrap()
376                    });
377
378                Utc.from_utc_datetime(&target_date.and_time(base_date.naive_utc().time()))
379            }
380            "year" | "years" => {
381                let naive = base_date.naive_utc();
382                let new_year = naive.year() + amount as i32;
383                let month = naive.month();
384                let day = naive.day();
385
386                // Handle leap year edge case (Feb 29 -> Feb 28 in non-leap year)
387                let target_date =
388                    NaiveDate::from_ymd_opt(new_year, month, day).unwrap_or_else(|| {
389                        // If the date doesn't exist (e.g., Feb 29 in non-leap year), use Feb 28
390                        NaiveDate::from_ymd_opt(new_year, month, day - 1).unwrap()
391                    });
392
393                Utc.from_utc_datetime(&target_date.and_time(base_date.naive_utc().time()))
394            }
395            "hour" | "hours" => base_date + chrono::Duration::hours(amount),
396            "minute" | "minutes" => base_date + chrono::Duration::minutes(amount),
397            "second" | "seconds" => base_date + chrono::Duration::seconds(amount),
398            _ => {
399                return Err(anyhow!(
400                    "Unknown DATEADD unit: {}. Use: day, month, year, hour, minute, second",
401                    unit
402                ))
403            }
404        };
405
406        // Return as datetime string
407        Ok(DataValue::DateTime(
408            result_date.format("%Y-%m-%d %H:%M:%S").to_string(),
409        ))
410    }
411}
412
413/// UNIX_TIMESTAMP - Convert datetime to Unix epoch timestamp
414pub struct UnixTimestamp;
415
416impl SqlFunction for UnixTimestamp {
417    fn signature(&self) -> FunctionSignature {
418        FunctionSignature {
419            name: "UNIX_TIMESTAMP",
420            category: FunctionCategory::Date,
421            arg_count: ArgCount::Fixed(1),
422            description:
423                "Convert datetime to Unix epoch timestamp (seconds since 1970-01-01 00:00:00 UTC)",
424            returns: "INTEGER (seconds since epoch)",
425            examples: vec![
426                "SELECT UNIX_TIMESTAMP('2024-01-01 00:00:00')",
427                "SELECT UNIX_TIMESTAMP('2024-01-01T12:30:45')",
428                "SELECT UNIX_TIMESTAMP(trade_time) FROM trades",
429            ],
430        }
431    }
432
433    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
434        if args.len() != 1 {
435            return Err(anyhow!("UNIX_TIMESTAMP expects exactly 1 argument"));
436        }
437
438        match &args[0] {
439            DataValue::DateTime(dt_str) | DataValue::String(dt_str) => {
440                let dt = parse_datetime(dt_str)?;
441                Ok(DataValue::Integer(dt.timestamp()))
442            }
443            DataValue::InternedString(dt_str) => {
444                let dt = parse_datetime(dt_str)?;
445                Ok(DataValue::Integer(dt.timestamp()))
446            }
447            DataValue::Null => Ok(DataValue::Null),
448            _ => Err(anyhow!(
449                "UNIX_TIMESTAMP expects a datetime or string argument"
450            )),
451        }
452    }
453}
454
455/// FROM_UNIXTIME - Convert Unix epoch timestamp to datetime
456pub struct FromUnixTime;
457
458impl SqlFunction for FromUnixTime {
459    fn signature(&self) -> FunctionSignature {
460        FunctionSignature {
461            name: "FROM_UNIXTIME",
462            category: FunctionCategory::Date,
463            arg_count: ArgCount::Fixed(1),
464            description: "Convert Unix epoch timestamp to datetime string",
465            returns: "DATETIME string in ISO format",
466            examples: vec![
467                "SELECT FROM_UNIXTIME(1704067200)",
468                "SELECT FROM_UNIXTIME(timestamp_col) FROM data",
469            ],
470        }
471    }
472
473    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
474        if args.len() != 1 {
475            return Err(anyhow!("FROM_UNIXTIME expects exactly 1 argument"));
476        }
477
478        match &args[0] {
479            DataValue::Integer(timestamp) => {
480                let dt = DateTime::<Utc>::from_timestamp(*timestamp, 0)
481                    .ok_or_else(|| anyhow!("Invalid timestamp: {}", timestamp))?;
482                Ok(DataValue::DateTime(
483                    dt.format("%Y-%m-%d %H:%M:%S").to_string(),
484                ))
485            }
486            DataValue::Float(timestamp) => {
487                let secs = timestamp.floor() as i64;
488                let nanos = ((timestamp - timestamp.floor()) * 1_000_000_000.0) as u32;
489                let dt = DateTime::<Utc>::from_timestamp(secs, nanos)
490                    .ok_or_else(|| anyhow!("Invalid timestamp: {}", timestamp))?;
491                Ok(DataValue::DateTime(
492                    dt.format("%Y-%m-%d %H:%M:%S%.f").to_string(),
493                ))
494            }
495            DataValue::Null => Ok(DataValue::Null),
496            _ => Err(anyhow!("FROM_UNIXTIME expects a numeric timestamp")),
497        }
498    }
499}
500
501/// TIME_BUCKET - Round timestamps down to bucket boundaries
502pub struct TimeBucket;
503
504impl SqlFunction for TimeBucket {
505    fn signature(&self) -> FunctionSignature {
506        FunctionSignature {
507            name: "TIME_BUCKET",
508            category: FunctionCategory::Date,
509            arg_count: ArgCount::Fixed(2),
510            description: "Round timestamp down to bucket boundary (for time-based grouping)",
511            returns: "INTEGER (bucket timestamp)",
512            examples: vec![
513                "SELECT TIME_BUCKET(300, UNIX_TIMESTAMP(trade_time)) as bucket FROM trades -- 5 minute buckets",
514                "SELECT TIME_BUCKET(3600, UNIX_TIMESTAMP(trade_time)) as hour FROM trades -- 1 hour buckets",
515                "SELECT TIME_BUCKET(60, timestamp_col) as minute FROM data -- 1 minute buckets",
516            ],
517        }
518    }
519
520    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
521        if args.len() != 2 {
522            return Err(anyhow!(
523                "TIME_BUCKET expects exactly 2 arguments: bucket_size, timestamp"
524            ));
525        }
526
527        let bucket_size = match &args[0] {
528            DataValue::Integer(size) => *size,
529            DataValue::Float(size) => *size as i64,
530            _ => return Err(anyhow!("TIME_BUCKET bucket_size must be numeric")),
531        };
532
533        if bucket_size <= 0 {
534            return Err(anyhow!("TIME_BUCKET bucket_size must be positive"));
535        }
536
537        match &args[1] {
538            DataValue::Integer(timestamp) => {
539                let bucket = (timestamp / bucket_size) * bucket_size;
540                Ok(DataValue::Integer(bucket))
541            }
542            DataValue::Float(timestamp) => {
543                let ts = *timestamp as i64;
544                let bucket = (ts / bucket_size) * bucket_size;
545                Ok(DataValue::Integer(bucket))
546            }
547            DataValue::Null => Ok(DataValue::Null),
548            _ => Err(anyhow!("TIME_BUCKET timestamp must be numeric")),
549        }
550    }
551}
552
553/// DAYOFWEEK function - returns day of week as number (0=Sunday, 6=Saturday)
554pub struct DayOfWeekFunction;
555
556impl SqlFunction for DayOfWeekFunction {
557    fn signature(&self) -> FunctionSignature {
558        FunctionSignature {
559            name: "DAYOFWEEK",
560            category: FunctionCategory::Date,
561            arg_count: ArgCount::Fixed(1),
562            description: "Returns day of week as number (0=Sunday, 6=Saturday)",
563            returns: "INTEGER",
564            examples: vec![
565                "SELECT DAYOFWEEK('2024-01-01')", // Returns 1 (Monday)
566                "SELECT DAYOFWEEK(NOW())",
567                "SELECT DAYOFWEEK(date_column) FROM table",
568            ],
569        }
570    }
571
572    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
573        if args.len() != 1 {
574            return Err(anyhow!("DAYOFWEEK expects exactly 1 argument"));
575        }
576
577        let date_str = match &args[0] {
578            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
579            DataValue::InternedString(s) => s.as_str(),
580            DataValue::Null => return Ok(DataValue::Null),
581            _ => return Err(anyhow!("DAYOFWEEK expects a date/datetime string")),
582        };
583
584        let dt = parse_datetime(date_str)?;
585        // chrono weekday: Mon=0, Tue=1, ..., Sun=6
586        // We want: Sun=0, Mon=1, ..., Sat=6
587        let chrono_weekday = dt.weekday().num_days_from_monday(); // 0=Mon, 6=Sun
588        let our_weekday = (chrono_weekday + 1) % 7; // Convert to 0=Sun, 6=Sat
589
590        Ok(DataValue::Integer(our_weekday as i64))
591    }
592}
593
594/// DAYNAME function - returns full day name (Monday, Tuesday, etc.)
595pub struct DayNameFunction;
596
597impl SqlFunction for DayNameFunction {
598    fn signature(&self) -> FunctionSignature {
599        FunctionSignature {
600            name: "DAYNAME",
601            category: FunctionCategory::Date,
602            arg_count: ArgCount::Range(1, 2),
603            description: "Returns day name. Optional second arg: 'full' (default) or 'short'",
604            returns: "STRING",
605            examples: vec![
606                "SELECT DAYNAME('2024-01-01')",          // Returns 'Monday'
607                "SELECT DAYNAME('2024-01-01', 'short')", // Returns 'Mon'
608                "SELECT DAYNAME(NOW(), 'full')",
609            ],
610        }
611    }
612
613    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
614        if args.is_empty() || args.len() > 2 {
615            return Err(anyhow!("DAYNAME expects 1 or 2 arguments"));
616        }
617
618        let date_str = match &args[0] {
619            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
620            DataValue::InternedString(s) => s.as_str(),
621            DataValue::Null => return Ok(DataValue::Null),
622            _ => return Err(anyhow!("DAYNAME expects a date/datetime string")),
623        };
624
625        let format = if args.len() == 2 {
626            match &args[1] {
627                DataValue::String(s) => s.as_str(),
628                DataValue::InternedString(s) => s.as_str(),
629                DataValue::Null => "full",
630                _ => return Err(anyhow!("DAYNAME format must be 'full' or 'short'")),
631            }
632        } else {
633            "full"
634        };
635
636        let dt = parse_datetime(date_str)?;
637
638        let day_name = match format {
639            "short" => dt.format("%a").to_string(), // Mon, Tue, Wed, etc.
640            "full" | _ => dt.format("%A").to_string(), // Monday, Tuesday, etc.
641        };
642
643        Ok(DataValue::String(day_name))
644    }
645}
646
647/// ISLEAPYEAR function - returns true if the year is a leap year
648pub struct IsLeapYearFunction;
649
650impl SqlFunction for IsLeapYearFunction {
651    fn signature(&self) -> FunctionSignature {
652        FunctionSignature {
653            name: "ISLEAPYEAR",
654            category: FunctionCategory::Date,
655            arg_count: ArgCount::Fixed(1),
656            description: "Returns true if the year is a leap year",
657            returns: "BOOLEAN",
658            examples: vec![
659                "SELECT ISLEAPYEAR('2024-01-01')", // Returns true
660                "SELECT ISLEAPYEAR(2024)",         // Returns true
661                "SELECT ISLEAPYEAR(2023)",         // Returns false
662            ],
663        }
664    }
665
666    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
667        if args.len() != 1 {
668            return Err(anyhow!("ISLEAPYEAR expects exactly 1 argument"));
669        }
670
671        let year = match &args[0] {
672            DataValue::Integer(y) => *y as i32,
673            DataValue::Float(f) => *f as i32,
674            DataValue::String(s) | DataValue::DateTime(s) => {
675                // Try to parse as a year number first
676                if let Ok(y) = s.parse::<i32>() {
677                    y
678                } else {
679                    // Try to parse as a date
680                    let dt = parse_datetime(s.as_str())?;
681                    dt.year()
682                }
683            }
684            DataValue::InternedString(s) => {
685                // Try to parse as a year number first
686                if let Ok(y) = s.parse::<i32>() {
687                    y
688                } else {
689                    // Try to parse as a date
690                    let dt = parse_datetime(s.as_str())?;
691                    dt.year()
692                }
693            }
694            DataValue::Null => return Ok(DataValue::Null),
695            _ => return Err(anyhow!("ISLEAPYEAR expects a year number or date")),
696        };
697
698        // Leap year calculation
699        let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
700
701        Ok(DataValue::Boolean(is_leap))
702    }
703}
704
705/// WEEKOFYEAR function - returns the ISO week number (1-53)
706pub struct WeekOfYearFunction;
707
708impl SqlFunction for WeekOfYearFunction {
709    fn signature(&self) -> FunctionSignature {
710        FunctionSignature {
711            name: "WEEKOFYEAR",
712            category: FunctionCategory::Date,
713            arg_count: ArgCount::Fixed(1),
714            description: "Returns the ISO week number of the year (1-53)",
715            returns: "INTEGER",
716            examples: vec![
717                "SELECT WEEKOFYEAR('2024-01-01')", // Returns week number
718                "SELECT WEEKOFYEAR(NOW())",
719            ],
720        }
721    }
722
723    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
724        if args.len() != 1 {
725            return Err(anyhow!("WEEKOFYEAR expects exactly 1 argument"));
726        }
727
728        let date_str = match &args[0] {
729            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
730            DataValue::InternedString(s) => s.as_str(),
731            DataValue::Null => return Ok(DataValue::Null),
732            _ => return Err(anyhow!("WEEKOFYEAR expects a date/datetime string")),
733        };
734
735        let dt = parse_datetime(date_str)?;
736        let week = dt.iso_week().week();
737
738        Ok(DataValue::Integer(week as i64))
739    }
740}
741
742/// QUARTER function - returns the quarter of the year (1-4)
743pub struct QuarterFunction;
744
745impl SqlFunction for QuarterFunction {
746    fn signature(&self) -> FunctionSignature {
747        FunctionSignature {
748            name: "QUARTER",
749            category: FunctionCategory::Date,
750            arg_count: ArgCount::Fixed(1),
751            description: "Returns the quarter of the year (1-4)",
752            returns: "INTEGER",
753            examples: vec![
754                "SELECT QUARTER('2024-01-15')", // Returns 1
755                "SELECT QUARTER('2024-07-01')", // Returns 3
756            ],
757        }
758    }
759
760    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
761        if args.len() != 1 {
762            return Err(anyhow!("QUARTER expects exactly 1 argument"));
763        }
764
765        let date_str = match &args[0] {
766            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
767            DataValue::InternedString(s) => s.as_str(),
768            DataValue::Null => return Ok(DataValue::Null),
769            _ => return Err(anyhow!("QUARTER expects a date/datetime string")),
770        };
771
772        let dt = parse_datetime(date_str)?;
773        let month = dt.month();
774        let quarter = (month - 1) / 3 + 1;
775
776        Ok(DataValue::Integer(quarter as i64))
777    }
778}
779
780/// YEAR function - extracts year from date
781pub struct YearFunction;
782
783impl SqlFunction for YearFunction {
784    fn signature(&self) -> FunctionSignature {
785        FunctionSignature {
786            name: "YEAR",
787            category: FunctionCategory::Date,
788            arg_count: ArgCount::Fixed(1),
789            description: "Returns the year from a date",
790            returns: "INTEGER",
791            examples: vec![
792                "SELECT YEAR('2024-03-15')", // Returns 2024
793                "SELECT YEAR(NOW())",        // Returns current year
794            ],
795        }
796    }
797
798    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
799        if args.len() != 1 {
800            return Err(anyhow!("YEAR expects exactly 1 argument"));
801        }
802
803        let date_str = match &args[0] {
804            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
805            DataValue::InternedString(s) => s.as_str(),
806            DataValue::Null => return Ok(DataValue::Null),
807            _ => return Err(anyhow!("YEAR expects a date/datetime string")),
808        };
809
810        let dt = parse_datetime(date_str)?;
811        Ok(DataValue::Float(dt.year() as f64))
812    }
813}
814
815/// MONTH function - extracts month from date (1-12)
816pub struct MonthFunction;
817
818impl SqlFunction for MonthFunction {
819    fn signature(&self) -> FunctionSignature {
820        FunctionSignature {
821            name: "MONTH",
822            category: FunctionCategory::Date,
823            arg_count: ArgCount::Fixed(1),
824            description: "Returns the month from a date (1-12)",
825            returns: "INTEGER",
826            examples: vec![
827                "SELECT MONTH('2024-03-15')", // Returns 3
828                "SELECT MONTH(NOW())",        // Returns current month
829            ],
830        }
831    }
832
833    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
834        if args.len() != 1 {
835            return Err(anyhow!("MONTH expects exactly 1 argument"));
836        }
837
838        let date_str = match &args[0] {
839            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
840            DataValue::InternedString(s) => s.as_str(),
841            DataValue::Null => return Ok(DataValue::Null),
842            _ => return Err(anyhow!("MONTH expects a date/datetime string")),
843        };
844
845        let dt = parse_datetime(date_str)?;
846        Ok(DataValue::Float(dt.month() as f64))
847    }
848}
849
850/// DAY function - extracts day of month from date (1-31)
851pub struct DayFunction;
852
853impl SqlFunction for DayFunction {
854    fn signature(&self) -> FunctionSignature {
855        FunctionSignature {
856            name: "DAY",
857            category: FunctionCategory::Date,
858            arg_count: ArgCount::Fixed(1),
859            description: "Returns the day of month from a date (1-31)",
860            returns: "INTEGER",
861            examples: vec![
862                "SELECT DAY('2024-03-15')", // Returns 15
863                "SELECT DAY(NOW())",        // Returns current day
864            ],
865        }
866    }
867
868    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
869        if args.len() != 1 {
870            return Err(anyhow!("DAY expects exactly 1 argument"));
871        }
872
873        let date_str = match &args[0] {
874            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
875            DataValue::InternedString(s) => s.as_str(),
876            DataValue::Null => return Ok(DataValue::Null),
877            _ => return Err(anyhow!("DAY expects a date/datetime string")),
878        };
879
880        let dt = parse_datetime(date_str)?;
881        Ok(DataValue::Float(dt.day() as f64))
882    }
883}
884
885/// MONTHNAME function - returns the month name
886pub struct MonthNameFunction;
887
888impl SqlFunction for MonthNameFunction {
889    fn signature(&self) -> FunctionSignature {
890        FunctionSignature {
891            name: "MONTHNAME",
892            category: FunctionCategory::Date,
893            arg_count: ArgCount::Range(1, 2),
894            description: "Returns month name. Optional second arg: 'full' (default) or 'short'",
895            returns: "STRING",
896            examples: vec![
897                "SELECT MONTHNAME('2024-01-15')",          // Returns 'January'
898                "SELECT MONTHNAME('2024-01-15', 'short')", // Returns 'Jan'
899            ],
900        }
901    }
902
903    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
904        if args.is_empty() || args.len() > 2 {
905            return Err(anyhow!("MONTHNAME expects 1 or 2 arguments"));
906        }
907
908        let date_str = match &args[0] {
909            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
910            DataValue::InternedString(s) => s.as_str(),
911            DataValue::Null => return Ok(DataValue::Null),
912            _ => return Err(anyhow!("MONTHNAME expects a date/datetime string")),
913        };
914
915        let format = if args.len() == 2 {
916            match &args[1] {
917                DataValue::String(s) => s.as_str(),
918                DataValue::InternedString(s) => s.as_str(),
919                DataValue::Null => "full",
920                _ => return Err(anyhow!("MONTHNAME format must be 'full' or 'short'")),
921            }
922        } else {
923            "full"
924        };
925
926        let dt = parse_datetime(date_str)?;
927
928        let month_name = match format {
929            "short" => dt.format("%b").to_string(), // Jan, Feb, Mar, etc.
930            "full" | _ => dt.format("%B").to_string(), // January, February, etc.
931        };
932
933        Ok(DataValue::String(month_name))
934    }
935}
936
937/// PARSE_DATETIME - Parse datetime with custom format string
938pub struct ParseDateTimeFunction;
939
940impl SqlFunction for ParseDateTimeFunction {
941    fn signature(&self) -> FunctionSignature {
942        FunctionSignature {
943            name: "PARSE_DATETIME",
944            category: FunctionCategory::Date,
945            arg_count: ArgCount::Fixed(2),
946            description: "Parse datetime string with custom format (uses chrono strftime format)",
947            returns: "DATETIME",
948            examples: vec![
949                "SELECT PARSE_DATETIME('15/01/2024', '%d/%m/%Y')",
950                "SELECT PARSE_DATETIME('Jan 15, 2024 14:30', '%b %d, %Y %H:%M')",
951                "SELECT PARSE_DATETIME('2024-01-15T14:30:00', '%Y-%m-%dT%H:%M:%S')",
952                "SELECT PARSE_DATETIME(date_string, '%Y%m%d-%H:%M:%S%.3f') FROM data -- FIX format",
953            ],
954        }
955    }
956
957    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
958        self.validate_args(args)?;
959
960        let date_str = match &args[0] {
961            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
962            DataValue::InternedString(s) => s.as_str(),
963            DataValue::Null => return Ok(DataValue::Null),
964            _ => return Err(anyhow!("PARSE_DATETIME expects a string as first argument")),
965        };
966
967        let format_str = match &args[1] {
968            DataValue::String(s) => s.as_str(),
969            DataValue::InternedString(s) => s.as_str(),
970            DataValue::Null => return Ok(DataValue::Null),
971            _ => {
972                return Err(anyhow!(
973                    "PARSE_DATETIME expects a format string as second argument"
974                ))
975            }
976        };
977
978        // Try parsing with time component first
979        if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, format_str) {
980            return Ok(DataValue::DateTime(
981                Utc.from_utc_datetime(&dt)
982                    .format("%Y-%m-%d %H:%M:%S%.3f")
983                    .to_string(),
984            ));
985        }
986
987        // Try parsing as date only
988        if let Ok(d) = NaiveDate::parse_from_str(date_str, format_str) {
989            return Ok(DataValue::DateTime(
990                Utc.from_utc_datetime(&d.and_hms_opt(0, 0, 0).unwrap())
991                    .format("%Y-%m-%d %H:%M:%S%.3f")
992                    .to_string(),
993            ));
994        }
995
996        Err(anyhow!(
997            "Failed to parse '{}' with format '{}'. See https://docs.rs/chrono/latest/chrono/format/strftime/index.html",
998            date_str,
999            format_str
1000        ))
1001    }
1002}
1003
1004/// PARSE_DATETIME_UTC - Parse datetime and explicitly interpret as UTC
1005pub struct ParseDateTimeUtcFunction;
1006
1007impl SqlFunction for ParseDateTimeUtcFunction {
1008    fn signature(&self) -> FunctionSignature {
1009        FunctionSignature {
1010            name: "PARSE_DATETIME_UTC",
1011            category: FunctionCategory::Date,
1012            arg_count: ArgCount::Range(1, 2),
1013            description: "Parse datetime as UTC. With 1 arg: auto-detect format. With 2 args: use custom format",
1014            returns: "DATETIME (UTC)",
1015            examples: vec![
1016                "SELECT PARSE_DATETIME_UTC('2024-01-15 14:30:00')",
1017                "SELECT PARSE_DATETIME_UTC('20250925-14:52:15.567') -- FIX format auto-detected",
1018                "SELECT PARSE_DATETIME_UTC('15/01/2024 14:30', '%d/%m/%Y %H:%M')",
1019            ],
1020        }
1021    }
1022
1023    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1024        if args.is_empty() || args.len() > 2 {
1025            return Err(anyhow!("PARSE_DATETIME_UTC expects 1 or 2 arguments"));
1026        }
1027
1028        let date_str = match &args[0] {
1029            DataValue::String(s) | DataValue::DateTime(s) => s.as_str(),
1030            DataValue::InternedString(s) => s.as_str(),
1031            DataValue::Null => return Ok(DataValue::Null),
1032            _ => {
1033                return Err(anyhow!(
1034                    "PARSE_DATETIME_UTC expects a string as first argument"
1035                ))
1036            }
1037        };
1038
1039        // If format string provided, use it
1040        if args.len() == 2 {
1041            let format_str = match &args[1] {
1042                DataValue::String(s) => s.as_str(),
1043                DataValue::InternedString(s) => s.as_str(),
1044                DataValue::Null => return Ok(DataValue::Null),
1045                _ => {
1046                    return Err(anyhow!(
1047                        "PARSE_DATETIME_UTC expects a format string as second argument"
1048                    ))
1049                }
1050            };
1051
1052            // Try parsing with time component first
1053            if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, format_str) {
1054                return Ok(DataValue::DateTime(
1055                    Utc.from_utc_datetime(&dt)
1056                        .format("%Y-%m-%d %H:%M:%S%.3f")
1057                        .to_string(),
1058                ));
1059            }
1060
1061            // Try parsing as date only
1062            if let Ok(d) = NaiveDate::parse_from_str(date_str, format_str) {
1063                return Ok(DataValue::DateTime(
1064                    Utc.from_utc_datetime(&d.and_hms_opt(0, 0, 0).unwrap())
1065                        .format("%Y-%m-%d %H:%M:%S%.3f")
1066                        .to_string(),
1067                ));
1068            }
1069
1070            return Err(anyhow!(
1071                "Failed to parse '{}' with format '{}'",
1072                date_str,
1073                format_str
1074            ));
1075        }
1076
1077        // No format string - use auto-detection (existing parse_datetime function)
1078        let dt = parse_datetime(date_str)?;
1079        Ok(DataValue::DateTime(
1080            dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
1081        ))
1082    }
1083}
1084
1085/// DATETIME constructor - Create datetime from components
1086pub struct DateTimeConstructor;
1087
1088impl SqlFunction for DateTimeConstructor {
1089    fn signature(&self) -> FunctionSignature {
1090        FunctionSignature {
1091            name: "DATETIME",
1092            category: FunctionCategory::Date,
1093            arg_count: ArgCount::Range(3, 7),
1094            description: "Create datetime from components: (year, month, day, [hour], [minute], [second], [is_utc])",
1095            returns: "DATETIME",
1096            examples: vec![
1097                "SELECT DATETIME(2024, 1, 15)",
1098                "SELECT DATETIME(2024, 1, 15, 14, 30, 0)",
1099                "SELECT DATETIME(2024, 12, 31, 23, 59, 59)",
1100                "-- Note: All times are interpreted as UTC",
1101            ],
1102        }
1103    }
1104
1105    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
1106        if args.len() < 3 || args.len() > 7 {
1107            return Err(anyhow!("DATETIME expects 3-7 arguments: year, month, day, [hour], [minute], [second], [is_utc]"));
1108        }
1109
1110        // Extract components
1111        let year = match &args[0] {
1112            DataValue::Integer(i) => *i as i32,
1113            DataValue::Float(f) => *f as i32,
1114            DataValue::Null => return Ok(DataValue::Null),
1115            _ => return Err(anyhow!("DATETIME year must be numeric")),
1116        };
1117
1118        let month = match &args[1] {
1119            DataValue::Integer(i) => *i as u32,
1120            DataValue::Float(f) => *f as u32,
1121            DataValue::Null => return Ok(DataValue::Null),
1122            _ => return Err(anyhow!("DATETIME month must be numeric")),
1123        };
1124
1125        let day = match &args[2] {
1126            DataValue::Integer(i) => *i as u32,
1127            DataValue::Float(f) => *f as u32,
1128            DataValue::Null => return Ok(DataValue::Null),
1129            _ => return Err(anyhow!("DATETIME day must be numeric")),
1130        };
1131
1132        let hour = if args.len() > 3 {
1133            match &args[3] {
1134                DataValue::Integer(i) => *i as u32,
1135                DataValue::Float(f) => *f as u32,
1136                DataValue::Null => return Ok(DataValue::Null),
1137                _ => return Err(anyhow!("DATETIME hour must be numeric")),
1138            }
1139        } else {
1140            0
1141        };
1142
1143        let minute = if args.len() > 4 {
1144            match &args[4] {
1145                DataValue::Integer(i) => *i as u32,
1146                DataValue::Float(f) => *f as u32,
1147                DataValue::Null => return Ok(DataValue::Null),
1148                _ => return Err(anyhow!("DATETIME minute must be numeric")),
1149            }
1150        } else {
1151            0
1152        };
1153
1154        let second = if args.len() > 5 {
1155            match &args[5] {
1156                DataValue::Integer(i) => *i as u32,
1157                DataValue::Float(f) => *f as u32,
1158                DataValue::Null => return Ok(DataValue::Null),
1159                _ => return Err(anyhow!("DATETIME second must be numeric")),
1160            }
1161        } else {
1162            0
1163        };
1164
1165        // is_utc parameter (default true)
1166        let _is_utc = if args.len() > 6 {
1167            match &args[6] {
1168                DataValue::Boolean(b) => *b,
1169                DataValue::Integer(i) => *i != 0,
1170                DataValue::Null => true,
1171                _ => return Err(anyhow!("DATETIME is_utc must be boolean")),
1172            }
1173        } else {
1174            true
1175        };
1176
1177        // Create date
1178        let date = NaiveDate::from_ymd_opt(year, month, day)
1179            .ok_or_else(|| anyhow!("Invalid date: {}-{}-{}", year, month, day))?;
1180
1181        // Create datetime
1182        let dt = date
1183            .and_hms_opt(hour, minute, second)
1184            .ok_or_else(|| anyhow!("Invalid time: {}:{}:{}", hour, minute, second))?;
1185
1186        // For now, always interpret as UTC (local timezone support would require additional dependencies)
1187        let utc_dt = Utc.from_utc_datetime(&dt);
1188
1189        Ok(DataValue::DateTime(
1190            utc_dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
1191        ))
1192    }
1193}
1194
1195/// Register all date/time functions
1196pub fn register_date_time_functions(registry: &mut super::FunctionRegistry) {
1197    registry.register(Box::new(NowFunction));
1198    registry.register(Box::new(TodayFunction));
1199    registry.register(Box::new(DateDiffFunction));
1200    registry.register(Box::new(DateAddFunction));
1201    registry.register(Box::new(UnixTimestamp));
1202    registry.register(Box::new(FromUnixTime));
1203    registry.register(Box::new(TimeBucket));
1204
1205    // Date extraction functions
1206    registry.register(Box::new(YearFunction));
1207    registry.register(Box::new(MonthFunction));
1208    registry.register(Box::new(DayFunction));
1209    registry.register(Box::new(DayOfWeekFunction));
1210    registry.register(Box::new(DayNameFunction));
1211    registry.register(Box::new(MonthNameFunction));
1212
1213    // Date utility functions
1214    registry.register(Box::new(IsLeapYearFunction));
1215    registry.register(Box::new(WeekOfYearFunction));
1216    registry.register(Box::new(QuarterFunction));
1217
1218    // Flexible parsing functions
1219    registry.register(Box::new(ParseDateTimeFunction));
1220    registry.register(Box::new(ParseDateTimeUtcFunction));
1221    registry.register(Box::new(DateTimeConstructor));
1222}