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}