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    let time = format!("{}:{:02}:{:02}", hour, minute, second);
293    Box::new(TimeValue::new(time))
294}
295
296pub fn date_day(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
297    let date = inputs[0].as_date().unwrap();
298    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
299    Box::new(IntValue::new(parsed_date.day().into()))
300}
301
302pub fn date_dayname(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
303    let date = inputs[0].as_date().unwrap();
304    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
305    let day_name = match parsed_date.weekday() {
306        Weekday::Mon => "Monday",
307        Weekday::Tue => "Tuesday",
308        Weekday::Wed => "Wednesday",
309        Weekday::Thu => "Thursday",
310        Weekday::Fri => "Friday",
311        Weekday::Sat => "Saturday",
312        Weekday::Sun => "Sunday",
313    }
314    .to_string();
315    Box::new(TextValue::new(day_name))
316}
317
318pub fn date_monthname(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
319    let date = inputs[0].as_date().unwrap();
320    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
321    let month_name = match parsed_date.month() {
322        1 => "January",
323        2 => "February",
324        3 => "March",
325        4 => "April",
326        5 => "May",
327        6 => "June",
328        7 => "July",
329        8 => "August",
330        9 => "September",
331        10 => "October",
332        11 => "November",
333        12 => "December",
334        _ => "",
335    }
336    .to_string();
337
338    Box::new(TextValue::new(month_name))
339}
340
341pub fn date_hour(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
342    let date = inputs[0].as_date_time().unwrap();
343    let date_time = DateTime::from_timestamp(date, 0);
344    let dt = date_time.unwrap().time();
345    Box::new(IntValue::new(dt.hour() as i64))
346}
347
348pub fn date_minute(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
349    let date = inputs[0].as_date_time().unwrap();
350    let date_time = DateTime::from_timestamp(date, 0);
351    let dt = date_time.unwrap().time();
352    Box::new(IntValue::new(dt.minute() as i64))
353}
354
355pub fn date_is_date(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
356    let is_date = inputs[0].data_type().is_date();
357    Box::new(BoolValue::new(is_date))
358}
359
360pub fn date_day_of_week(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
361    let date = inputs[0].as_date().unwrap();
362    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
363    let value = parsed_date.weekday().number_from_sunday().into();
364    Box::new(IntValue::new(value))
365}
366
367pub fn date_day_of_month(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
368    let date = inputs[0].as_date().unwrap();
369    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
370    Box::new(IntValue::new(parsed_date.day().into()))
371}
372
373pub fn date_day_of_year(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
374    let date = inputs[0].as_date().unwrap();
375    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
376    Box::new(IntValue::new(parsed_date.ordinal().into()))
377}
378
379pub fn date_week_of_year(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
380    let date = inputs[0].as_date().unwrap();
381
382    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
383    let native_date = parsed_date.date_naive();
384    let first_day_of_year = NaiveDate::from_ymd_opt(native_date.year(), 1, 1).unwrap();
385    let days_diff = native_date
386        .signed_duration_since(first_day_of_year)
387        .num_days();
388
389    let week_offset = match first_day_of_year.weekday() {
390        Weekday::Mon => 0,
391        _ => 1,
392    };
393
394    let days_with_offset = days_diff + week_offset;
395    let value = ((days_with_offset / 7) as u32 + 1).into();
396    Box::new(IntValue::new(value))
397}
398
399pub fn date_year_and_week(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
400    let date = inputs[0].as_date().unwrap();
401    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
402    let year = parsed_date.year();
403    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
404    let native_date = parsed_date.date_naive();
405    let first_day_of_year = NaiveDate::from_ymd_opt(native_date.year(), 1, 1).unwrap();
406    let days_diff = native_date
407        .signed_duration_since(first_day_of_year)
408        .num_days();
409
410    let week_offset = match first_day_of_year.weekday() {
411        Weekday::Mon => 0,
412        _ => 1,
413    };
414
415    let days_with_offset = days_diff + week_offset;
416    let week_number = (days_with_offset / 7) as u32 + 1;
417    let formatted_value = format!("{}{}", year, week_number);
418    Box::new(TextValue::new(formatted_value))
419}
420
421pub fn date_quarter(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
422    let date = inputs[0].as_date().unwrap();
423    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
424    let month = parsed_date.month() as i64;
425    Box::new(IntValue::new((month - 1) / 3 + 1))
426}
427
428pub fn date_year(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
429    let date = inputs[0].as_date().unwrap();
430    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
431    Box::new(IntValue::new(parsed_date.year().into()))
432}
433
434pub fn date_month(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
435    let date = inputs[0].as_date().unwrap();
436    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
437    Box::new(IntValue::new(parsed_date.month().into()))
438}
439
440pub fn date_weekday(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
441    let date = inputs[0].as_date().unwrap();
442    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
443    let value = (parsed_date.weekday().number_from_monday() - 1) as i64;
444    Box::new(IntValue::new(value))
445}
446
447pub fn date_to_days(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
448    let date = inputs[0].as_date().unwrap();
449    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
450    let days_since_year_0 = parsed_date.ordinal0() as i64;
451    let year = parsed_date.year() as i64;
452    let leap_years = (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
453    let non_leap_years = year - leap_years;
454    let days = 365 * non_leap_years + 366 * leap_years + days_since_year_0;
455    Box::new(IntValue::new(days))
456}
457
458pub fn date_last_day(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
459    let date = inputs[0].as_date().unwrap();
460    let parsed_date = DateTime::from_timestamp(date, 0).unwrap();
461    let (year, month) = (parsed_date.year(), parsed_date.month());
462
463    let curr_month_start = NaiveDate::from_ymd_opt(year, month, 1).unwrap();
464    let next_month_start = if month < 12 {
465        NaiveDate::from_ymd_opt(year, month + 1, 1)
466    } else {
467        NaiveDate::from_ymd_opt(year + 1, 1, 1)
468    }
469    .unwrap();
470
471    let days_in_month = next_month_start - curr_month_start;
472
473    let parsed_date = parsed_date.with_day(1).unwrap();
474    let last_day = parsed_date + days_in_month - chrono::Duration::days(1);
475
476    Box::new(DateValue::new(last_day.timestamp()))
477}
478
479pub fn interval_justify_days(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
480    let mut input_interval = inputs[0].as_interval().unwrap();
481    while input_interval.days >= 30 {
482        input_interval.months += 1;
483        input_interval.days -= 30;
484    }
485    Box::new(IntervalValue::new(input_interval))
486}
487
488pub fn interval_justify_hours(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
489    let mut input_interval = inputs[0].as_interval().unwrap();
490    while input_interval.days >= 24 {
491        input_interval.days += 1;
492        input_interval.hours -= 24;
493    }
494    Box::new(IntervalValue::new(input_interval))
495}