gitql_std/datetime/
mod.rs

1use std::collections::HashMap;
2
3extern crate chrono;
4use chrono::DateTime;
5use chrono::Datelike;
6use chrono::NaiveDate;
7use chrono::TimeZone;
8use chrono::Timelike;
9use chrono::Utc;
10use chrono::Weekday;
11
12use gitql_ast::types::any::AnyType;
13use gitql_ast::types::boolean::BoolType;
14use gitql_ast::types::date::DateType;
15use gitql_ast::types::datetime::DateTimeType;
16use gitql_ast::types::integer::IntType;
17use gitql_ast::types::interval::IntervalType;
18use gitql_ast::types::text::TextType;
19use gitql_ast::types::time::TimeType;
20use gitql_ast::types::variant::VariantType;
21use gitql_core::signature::Signature;
22use gitql_core::signature::StandardFunction;
23use gitql_core::values::boolean::BoolValue;
24use gitql_core::values::date::DateValue;
25use gitql_core::values::datetime::DateTimeValue;
26use gitql_core::values::integer::IntValue;
27use gitql_core::values::interval::IntervalValue;
28use gitql_core::values::text::TextValue;
29use gitql_core::values::time::TimeValue;
30use gitql_core::values::Value;
31
32#[inline(always)]
33pub fn register_std_datetime_functions(map: &mut HashMap<&'static str, StandardFunction>) {
34    map.insert("date", date_extract_date);
35    map.insert("current_date", date_current_date);
36    map.insert("current_time", date_current_time);
37    map.insert("current_timestamp", date_current_timestamp);
38    map.insert("now", date_current_timestamp);
39    map.insert("makedate", date_make_date);
40    map.insert("maketime", date_make_time);
41    map.insert("day", date_day);
42    map.insert("dayname", date_dayname);
43    map.insert("monthname", date_monthname);
44    map.insert("hour", date_hour);
45    map.insert("minute", date_minute);
46    map.insert("isdate", date_is_date);
47    map.insert("dayofweek", date_day_of_week);
48    map.insert("dayofmonth", date_day_of_month);
49    map.insert("dayofyear", date_day_of_year);
50    map.insert("weekofyear", date_week_of_year);
51    map.insert("quarter", date_quarter);
52    map.insert("year", date_year);
53    map.insert("month", date_month);
54    map.insert("weekday", date_weekday);
55    map.insert("to_days", date_to_days);
56    map.insert("last_day", date_last_day);
57    map.insert("yearweek", date_year_and_week);
58
59    map.insert("justify_days", interval_justify_days);
60    map.insert("justify_hours", interval_justify_hours);
61}
62
63#[inline(always)]
64pub fn register_std_datetime_function_signatures(map: &mut HashMap<&'static str, Signature>) {
65    map.insert(
66        "date",
67        Signature {
68            parameters: vec![Box::new(VariantType {
69                variants: vec![Box::new(DateType), Box::new(DateTimeType)],
70            })],
71            return_type: Box::new(DateType),
72        },
73    );
74    map.insert(
75        "current_date",
76        Signature {
77            parameters: vec![],
78            return_type: Box::new(DateType),
79        },
80    );
81    map.insert(
82        "current_time",
83        Signature {
84            parameters: vec![],
85            return_type: Box::new(TimeType),
86        },
87    );
88    map.insert(
89        "current_timestamp",
90        Signature {
91            parameters: vec![],
92            return_type: Box::new(DateTimeType),
93        },
94    );
95    map.insert(
96        "now",
97        Signature {
98            parameters: vec![],
99            return_type: Box::new(DateTimeType),
100        },
101    );
102    map.insert(
103        "makedate",
104        Signature {
105            parameters: vec![Box::new(IntType), Box::new(IntType)],
106            return_type: Box::new(DateType),
107        },
108    );
109    map.insert(
110        "maketime",
111        Signature {
112            parameters: vec![Box::new(IntType), Box::new(IntType), Box::new(IntType)],
113            return_type: Box::new(TimeType),
114        },
115    );
116    map.insert(
117        "dayname",
118        Signature {
119            parameters: vec![Box::new(DateType)],
120            return_type: Box::new(TextType),
121        },
122    );
123    map.insert(
124        "day",
125        Signature {
126            parameters: vec![Box::new(DateType)],
127            return_type: Box::new(IntType),
128        },
129    );
130    map.insert(
131        "monthname",
132        Signature {
133            parameters: vec![Box::new(DateType)],
134            return_type: Box::new(TextType),
135        },
136    );
137    map.insert(
138        "hour",
139        Signature {
140            parameters: vec![Box::new(DateTimeType)],
141            return_type: Box::new(IntType),
142        },
143    );
144    map.insert(
145        "minute",
146        Signature {
147            parameters: vec![Box::new(DateTimeType)],
148            return_type: Box::new(IntType),
149        },
150    );
151    map.insert(
152        "isdate",
153        Signature {
154            parameters: vec![Box::new(AnyType)],
155            return_type: Box::new(BoolType),
156        },
157    );
158    map.insert(
159        "dayofweek",
160        Signature {
161            parameters: vec![Box::new(DateType)],
162            return_type: Box::new(IntType),
163        },
164    );
165    map.insert(
166        "dayofmonth",
167        Signature {
168            parameters: vec![Box::new(DateType)],
169            return_type: Box::new(IntType),
170        },
171    );
172    map.insert(
173        "dayofyear",
174        Signature {
175            parameters: vec![Box::new(DateType)],
176            return_type: Box::new(IntType),
177        },
178    );
179    map.insert(
180        "weekofyear",
181        Signature {
182            parameters: vec![Box::new(DateType)],
183            return_type: Box::new(IntType),
184        },
185    );
186    map.insert(
187        "quarter",
188        Signature {
189            parameters: vec![Box::new(DateType)],
190            return_type: Box::new(IntType),
191        },
192    );
193    map.insert(
194        "year",
195        Signature {
196            parameters: vec![Box::new(DateType)],
197            return_type: Box::new(IntType),
198        },
199    );
200    map.insert(
201        "month",
202        Signature {
203            parameters: vec![Box::new(DateType)],
204            return_type: Box::new(IntType),
205        },
206    );
207    map.insert(
208        "weekday",
209        Signature {
210            parameters: vec![Box::new(DateType)],
211            return_type: Box::new(IntType),
212        },
213    );
214    map.insert(
215        "to_days",
216        Signature {
217            parameters: vec![Box::new(DateType)],
218            return_type: Box::new(IntType),
219        },
220    );
221    map.insert(
222        "last_day",
223        Signature {
224            parameters: vec![Box::new(DateType)],
225            return_type: Box::new(DateType),
226        },
227    );
228    map.insert(
229        "yearweek",
230        Signature {
231            parameters: vec![Box::new(DateType)],
232            return_type: Box::new(TextType),
233        },
234    );
235
236    map.insert(
237        "justify_days",
238        Signature {
239            parameters: vec![Box::new(IntervalType)],
240            return_type: Box::new(IntervalType),
241        },
242    );
243
244    map.insert(
245        "justify_hours",
246        Signature {
247            parameters: vec![Box::new(IntervalType)],
248            return_type: Box::new(IntervalType),
249        },
250    );
251}
252
253pub fn date_extract_date(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
254    let argument_type = inputs[0].data_type();
255    if argument_type.is_date() {
256        return inputs[0].clone();
257    }
258    let timestamp = inputs[0].as_date_time().unwrap();
259    Box::new(DateValue::new(timestamp))
260}
261
262pub fn date_current_date(_inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
263    let timestamp = Utc::now().timestamp();
264    Box::new(DateValue::new(timestamp))
265}
266
267pub fn date_current_time(_inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
268    let time_stamp = Utc::now().timestamp();
269    let datetime = DateTime::from_timestamp(time_stamp, 0).unwrap();
270    let time = datetime.format("%H:%M:%S").to_string();
271    Box::new(TimeValue::new(time))
272}
273
274pub fn date_current_timestamp(_inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
275    let timestamp = Utc::now().timestamp();
276    Box::new(DateTimeValue::new(timestamp))
277}
278
279pub fn date_make_date(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
280    let year = inputs[0].as_int().unwrap() as i32;
281    let day_of_year = inputs[1].as_int().unwrap() as u32;
282    let date = NaiveDate::from_yo_opt(year, day_of_year).unwrap();
283    let datetime = date.and_hms_opt(0, 0, 0).unwrap();
284    let timestamp = Utc.from_utc_datetime(&datetime).timestamp();
285    Box::new(DateValue::new(timestamp))
286}
287
288pub fn date_make_time(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
289    let hour = inputs[0].as_int().unwrap();
290    let minute = inputs[1].as_int().unwrap();
291    let second = inputs[2].as_int().unwrap();
292    Box::new(TimeValue::new(format!("{hour}:{minute:02}:{second:02}")))
293}
294
295pub fn date_day(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
296    let date = inputs[0].as_date().unwrap();
297    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
298    Box::new(IntValue::new(parsed_date.day().into()))
299}
300
301pub fn date_dayname(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
302    let date = inputs[0].as_date().unwrap();
303    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
304    let day_name = match parsed_date.weekday() {
305        Weekday::Mon => "Monday",
306        Weekday::Tue => "Tuesday",
307        Weekday::Wed => "Wednesday",
308        Weekday::Thu => "Thursday",
309        Weekday::Fri => "Friday",
310        Weekday::Sat => "Saturday",
311        Weekday::Sun => "Sunday",
312    }
313    .to_string();
314    Box::new(TextValue::new(day_name))
315}
316
317pub fn date_monthname(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
318    let date = inputs[0].as_date().unwrap();
319    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
320    let month_name = match parsed_date.month() {
321        1 => "January",
322        2 => "February",
323        3 => "March",
324        4 => "April",
325        5 => "May",
326        6 => "June",
327        7 => "July",
328        8 => "August",
329        9 => "September",
330        10 => "October",
331        11 => "November",
332        12 => "December",
333        _ => "",
334    }
335    .to_string();
336
337    Box::new(TextValue::new(month_name))
338}
339
340pub fn date_hour(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
341    let date = inputs[0].as_date_time().unwrap();
342    let date_time = DateTime::from_timestamp(date, 0);
343    let dt = date_time.unwrap().time();
344    Box::new(IntValue::new(dt.hour() as i64))
345}
346
347pub fn date_minute(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
348    let date = inputs[0].as_date_time().unwrap();
349    let date_time = DateTime::from_timestamp(date, 0);
350    let dt = date_time.unwrap().time();
351    Box::new(IntValue::new(dt.minute() as i64))
352}
353
354pub fn date_is_date(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
355    let is_date = inputs[0].data_type().is_date();
356    Box::new(BoolValue::new(is_date))
357}
358
359pub fn date_day_of_week(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
360    let date = inputs[0].as_date().unwrap();
361    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
362    let value = parsed_date.weekday().number_from_sunday().into();
363    Box::new(IntValue::new(value))
364}
365
366pub fn date_day_of_month(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
367    let date = inputs[0].as_date().unwrap();
368    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
369    Box::new(IntValue::new(parsed_date.day().into()))
370}
371
372pub fn date_day_of_year(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
373    let date = inputs[0].as_date().unwrap();
374    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
375    Box::new(IntValue::new(parsed_date.ordinal().into()))
376}
377
378pub fn date_week_of_year(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
379    let date = inputs[0].as_date().unwrap();
380
381    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
382    let native_date = parsed_date.date_naive();
383    let first_day_of_year = NaiveDate::from_ymd_opt(native_date.year(), 1, 1).unwrap();
384    let days_diff = native_date
385        .signed_duration_since(first_day_of_year)
386        .num_days();
387
388    let week_offset = match first_day_of_year.weekday() {
389        Weekday::Mon => 0,
390        _ => 1,
391    };
392
393    let days_with_offset = days_diff + week_offset;
394    let value = ((days_with_offset / 7) as u32 + 1).into();
395    Box::new(IntValue::new(value))
396}
397
398pub fn date_year_and_week(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
399    let date = inputs[0].as_date().unwrap();
400    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
401    let year = parsed_date.year();
402    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
403    let native_date = parsed_date.date_naive();
404    let first_day_of_year = NaiveDate::from_ymd_opt(native_date.year(), 1, 1).unwrap();
405    let days_diff = native_date
406        .signed_duration_since(first_day_of_year)
407        .num_days();
408
409    let week_offset = match first_day_of_year.weekday() {
410        Weekday::Mon => 0,
411        _ => 1,
412    };
413
414    let days_with_offset = days_diff + week_offset;
415    let week_number = (days_with_offset / 7) as u32 + 1;
416    Box::new(TextValue::new(format!("{year}{week_number}")))
417}
418
419pub fn date_quarter(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
420    let date = inputs[0].as_date().unwrap();
421    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
422    let month = parsed_date.month() as i64;
423    Box::new(IntValue::new((month - 1) / 3 + 1))
424}
425
426pub fn date_year(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
427    let date = inputs[0].as_date().unwrap();
428    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
429    Box::new(IntValue::new(parsed_date.year().into()))
430}
431
432pub fn date_month(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
433    let date = inputs[0].as_date().unwrap();
434    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
435    Box::new(IntValue::new(parsed_date.month().into()))
436}
437
438pub fn date_weekday(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
439    let date = inputs[0].as_date().unwrap();
440    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
441    let value = (parsed_date.weekday().number_from_monday() - 1) as i64;
442    Box::new(IntValue::new(value))
443}
444
445pub fn date_to_days(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
446    let date = inputs[0].as_date().unwrap();
447    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
448    let days_since_year_0 = parsed_date.ordinal0() as i64;
449    let year = parsed_date.year() as i64;
450    let leap_years = (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
451    let non_leap_years = year - leap_years;
452    let days = 365 * non_leap_years + 366 * leap_years + days_since_year_0;
453    Box::new(IntValue::new(days))
454}
455
456pub fn date_last_day(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
457    let date = inputs[0].as_date().unwrap();
458    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
459    let (year, month) = (parsed_date.year(), parsed_date.month());
460
461    let curr_month_start = NaiveDate::from_ymd_opt(year, month, 1).unwrap();
462    let next_month_start = if month < 12 {
463        NaiveDate::from_ymd_opt(year, month + 1, 1)
464    } else {
465        NaiveDate::from_ymd_opt(year + 1, 1, 1)
466    }
467    .unwrap();
468
469    let days_in_month = next_month_start - curr_month_start;
470
471    let parsed_date = parsed_date.with_day(1).unwrap();
472    let last_day = parsed_date + days_in_month - chrono::Duration::days(1);
473
474    Box::new(DateValue::new(last_day.timestamp()))
475}
476
477pub fn interval_justify_days(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
478    let mut input_interval = inputs[0].as_interval().unwrap();
479    while input_interval.days >= 30 {
480        input_interval.months += 1;
481        input_interval.days -= 30;
482    }
483    Box::new(IntervalValue::new(input_interval))
484}
485
486pub fn interval_justify_hours(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
487    let mut input_interval = inputs[0].as_interval().unwrap();
488    while input_interval.days >= 24 {
489        input_interval.days += 1;
490        input_interval.hours -= 24;
491    }
492    Box::new(IntervalValue::new(input_interval))
493}