1 | #include "duckdb/function/scalar/date_functions.hpp" |
2 | #include "duckdb/common/enums/date_part_specifier.hpp" |
3 | #include "duckdb/common/exception.hpp" |
4 | #include "duckdb/common/types/date.hpp" |
5 | #include "duckdb/common/types/timestamp.hpp" |
6 | #include "duckdb/common/vector_operations/vector_operations.hpp" |
7 | #include "duckdb/common/string_util.hpp" |
8 | using namespace std; |
9 | |
10 | namespace duckdb { |
11 | |
12 | DatePartSpecifier GetDatePartSpecifier(string specifier) { |
13 | specifier = StringUtil::Lower(specifier); |
14 | if (specifier == "year" ) { |
15 | return DatePartSpecifier::YEAR; |
16 | } else if (specifier == "month" ) { |
17 | return DatePartSpecifier::MONTH; |
18 | } else if (specifier == "day" ) { |
19 | return DatePartSpecifier::DAY; |
20 | } else if (specifier == "decade" ) { |
21 | return DatePartSpecifier::DECADE; |
22 | } else if (specifier == "century" ) { |
23 | return DatePartSpecifier::CENTURY; |
24 | } else if (specifier == "millennium" ) { |
25 | return DatePartSpecifier::MILLENNIUM; |
26 | } else if (specifier == "microseconds" ) { |
27 | return DatePartSpecifier::MICROSECONDS; |
28 | } else if (specifier == "milliseconds" ) { |
29 | return DatePartSpecifier::MILLISECONDS; |
30 | } else if (specifier == "second" ) { |
31 | return DatePartSpecifier::SECOND; |
32 | } else if (specifier == "minute" ) { |
33 | return DatePartSpecifier::MINUTE; |
34 | } else if (specifier == "hour" ) { |
35 | return DatePartSpecifier::HOUR; |
36 | } else if (specifier == "epoch" ) { |
37 | // seconds since 1970-01-01 |
38 | return DatePartSpecifier::EPOCH; |
39 | } else if (specifier == "dow" ) { |
40 | // day of the week (Sunday = 0, Saturday = 6) |
41 | return DatePartSpecifier::DOW; |
42 | } else if (specifier == "isodow" ) { |
43 | // isodow (Monday = 1, Sunday = 7) |
44 | return DatePartSpecifier::ISODOW; |
45 | } else if (specifier == "week" ) { |
46 | // week number |
47 | return DatePartSpecifier::WEEK; |
48 | } else if (specifier == "doy" ) { |
49 | // day of the year (1-365/366) |
50 | return DatePartSpecifier::DOY; |
51 | } else if (specifier == "quarter" ) { |
52 | // quarter of the year (1-4) |
53 | return DatePartSpecifier::QUARTER; |
54 | } else { |
55 | throw ConversionException("extract specifier \"%s\" not recognized" , specifier.c_str()); |
56 | } |
57 | } |
58 | |
59 | struct YearOperator { |
60 | template <class TA, class TR> static inline TR Operation(TA input) { |
61 | return Date::ExtractYear(input); |
62 | } |
63 | }; |
64 | |
65 | template <> int64_t YearOperator::Operation(timestamp_t input) { |
66 | return YearOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
67 | } |
68 | |
69 | struct MonthOperator { |
70 | template <class TA, class TR> static inline TR Operation(TA input) { |
71 | return Date::ExtractMonth(input); |
72 | } |
73 | }; |
74 | |
75 | template <> int64_t MonthOperator::Operation(timestamp_t input) { |
76 | return MonthOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
77 | } |
78 | |
79 | struct DayOperator { |
80 | template <class TA, class TR> static inline TR Operation(TA input) { |
81 | return Date::ExtractDay(input); |
82 | } |
83 | }; |
84 | |
85 | template <> int64_t DayOperator::Operation(timestamp_t input) { |
86 | return DayOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
87 | } |
88 | |
89 | struct DecadeOperator { |
90 | template <class TA, class TR> static inline TR Operation(TA input) { |
91 | return Date::ExtractYear(input) / 10; |
92 | } |
93 | }; |
94 | |
95 | template <> int64_t DecadeOperator::Operation(timestamp_t input) { |
96 | return DecadeOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
97 | } |
98 | |
99 | struct CenturyOperator { |
100 | template <class TA, class TR> static inline TR Operation(TA input) { |
101 | return ((Date::ExtractYear(input) - 1) / 100) + 1; |
102 | } |
103 | }; |
104 | |
105 | template <> int64_t CenturyOperator::Operation(timestamp_t input) { |
106 | return CenturyOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
107 | } |
108 | |
109 | struct MilleniumOperator { |
110 | template <class TA, class TR> static inline TR Operation(TA input) { |
111 | return ((Date::ExtractYear(input) - 1) / 1000) + 1; |
112 | } |
113 | }; |
114 | |
115 | template <> int64_t MilleniumOperator::Operation(timestamp_t input) { |
116 | return MilleniumOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
117 | } |
118 | |
119 | struct QuarterOperator { |
120 | template <class TA, class TR> static inline TR Operation(TA input) { |
121 | return Date::ExtractMonth(input) / 4; |
122 | } |
123 | }; |
124 | |
125 | template <> int64_t QuarterOperator::Operation(timestamp_t input) { |
126 | return QuarterOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
127 | } |
128 | |
129 | struct DayOfWeekOperator { |
130 | template <class TA, class TR> static inline TR Operation(TA input) { |
131 | // day of the week (Sunday = 0, Saturday = 6) |
132 | // turn sunday into 0 by doing mod 7 |
133 | return Date::ExtractISODayOfTheWeek(input) % 7; |
134 | } |
135 | }; |
136 | |
137 | template <> int64_t DayOfWeekOperator::Operation(timestamp_t input) { |
138 | return DayOfWeekOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
139 | } |
140 | |
141 | struct ISODayOfWeekOperator { |
142 | template <class TA, class TR> static inline TR Operation(TA input) { |
143 | // isodow (Monday = 1, Sunday = 7) |
144 | return Date::ExtractISODayOfTheWeek(input); |
145 | } |
146 | }; |
147 | |
148 | template <> int64_t ISODayOfWeekOperator::Operation(timestamp_t input) { |
149 | return ISODayOfWeekOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
150 | } |
151 | |
152 | struct DayOfYearOperator { |
153 | template <class TA, class TR> static inline TR Operation(TA input) { |
154 | return Date::ExtractDayOfTheYear(input); |
155 | } |
156 | }; |
157 | |
158 | template <> int64_t DayOfYearOperator::Operation(timestamp_t input) { |
159 | return DayOfYearOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
160 | } |
161 | |
162 | struct WeekOperator { |
163 | template <class TA, class TR> static inline TR Operation(TA input) { |
164 | return Date::ExtractWeekNumber(input); |
165 | } |
166 | }; |
167 | |
168 | template <> int64_t WeekOperator::Operation(timestamp_t input) { |
169 | return WeekOperator::Operation<date_t, int64_t>(Timestamp::GetDate(input)); |
170 | } |
171 | |
172 | struct YearWeekOperator { |
173 | template <class TA, class TR> static inline TR Operation(TA input) { |
174 | return YearOperator::Operation<TA, TR>(input) * 100 + WeekOperator::Operation<TA, TR>(input); |
175 | } |
176 | }; |
177 | |
178 | struct EpochOperator { |
179 | template <class TA, class TR> static inline TR Operation(TA input) { |
180 | return Date::Epoch(input); |
181 | } |
182 | }; |
183 | |
184 | template <> int64_t EpochOperator::Operation(timestamp_t input) { |
185 | return Timestamp::GetEpoch(input); |
186 | } |
187 | |
188 | struct MicrosecondsOperator { |
189 | template <class TA, class TR> static inline TR Operation(TA input) { |
190 | return 0; |
191 | } |
192 | }; |
193 | |
194 | template <> int64_t MicrosecondsOperator::Operation(timestamp_t input) { |
195 | return Timestamp::GetMilliseconds(input) * 1000; |
196 | } |
197 | |
198 | struct MillisecondsOperator { |
199 | template <class TA, class TR> static inline TR Operation(TA input) { |
200 | return 0; |
201 | } |
202 | }; |
203 | |
204 | template <> int64_t MillisecondsOperator::Operation(timestamp_t input) { |
205 | return Timestamp::GetMilliseconds(input); |
206 | } |
207 | |
208 | struct SecondsOperator { |
209 | template <class TA, class TR> static inline TR Operation(TA input) { |
210 | return 0; |
211 | } |
212 | }; |
213 | |
214 | template <> int64_t SecondsOperator::Operation(timestamp_t input) { |
215 | return Timestamp::GetSeconds(input); |
216 | } |
217 | |
218 | struct MinutesOperator { |
219 | template <class TA, class TR> static inline TR Operation(TA input) { |
220 | return 0; |
221 | } |
222 | }; |
223 | |
224 | template <> int64_t MinutesOperator::Operation(timestamp_t input) { |
225 | return Timestamp::GetMinutes(input); |
226 | } |
227 | |
228 | struct HoursOperator { |
229 | template <class TA, class TR> static inline TR Operation(TA input) { |
230 | return 0; |
231 | } |
232 | }; |
233 | |
234 | template <> int64_t HoursOperator::Operation(timestamp_t input) { |
235 | return Timestamp::GetHours(input); |
236 | } |
237 | |
238 | template <class T> static int64_t (DatePartSpecifier type, T element) { |
239 | switch (type) { |
240 | case DatePartSpecifier::YEAR: |
241 | return YearOperator::Operation<T, int64_t>(element); |
242 | case DatePartSpecifier::MONTH: |
243 | return MonthOperator::Operation<T, int64_t>(element); |
244 | case DatePartSpecifier::DAY: |
245 | return DayOperator::Operation<T, int64_t>(element); |
246 | case DatePartSpecifier::DECADE: |
247 | return DecadeOperator::Operation<T, int64_t>(element); |
248 | case DatePartSpecifier::CENTURY: |
249 | return CenturyOperator::Operation<T, int64_t>(element); |
250 | case DatePartSpecifier::MILLENNIUM: |
251 | return MilleniumOperator::Operation<T, int64_t>(element); |
252 | case DatePartSpecifier::QUARTER: |
253 | return QuarterOperator::Operation<T, int64_t>(element); |
254 | case DatePartSpecifier::DOW: |
255 | return DayOfWeekOperator::Operation<T, int64_t>(element); |
256 | case DatePartSpecifier::ISODOW: |
257 | return ISODayOfWeekOperator::Operation<T, int64_t>(element); |
258 | case DatePartSpecifier::DOY: |
259 | return DayOfYearOperator::Operation<T, int64_t>(element); |
260 | case DatePartSpecifier::WEEK: |
261 | return WeekOperator::Operation<T, int64_t>(element); |
262 | case DatePartSpecifier::EPOCH: |
263 | return EpochOperator::Operation<T, int64_t>(element); |
264 | case DatePartSpecifier::MICROSECONDS: |
265 | return MicrosecondsOperator::Operation<T, int64_t>(element); |
266 | case DatePartSpecifier::MILLISECONDS: |
267 | return MillisecondsOperator::Operation<T, int64_t>(element); |
268 | case DatePartSpecifier::SECOND: |
269 | return SecondsOperator::Operation<T, int64_t>(element); |
270 | case DatePartSpecifier::MINUTE: |
271 | return MinutesOperator::Operation<T, int64_t>(element); |
272 | case DatePartSpecifier::HOUR: |
273 | return HoursOperator::Operation<T, int64_t>(element); |
274 | default: |
275 | throw NotImplementedException("Specifier type not implemented" ); |
276 | } |
277 | } |
278 | |
279 | struct DatePartOperator { |
280 | template <class TA, class TB, class TR> static inline TR Operation(TA specifier, TB date) { |
281 | return extract_element<TB>(GetDatePartSpecifier(specifier.GetString()), date); |
282 | } |
283 | }; |
284 | |
285 | template <class OP> static void AddDatePartOperator(BuiltinFunctions &set, string name) { |
286 | ScalarFunctionSet operator_set(name); |
287 | operator_set.AddFunction( |
288 | ScalarFunction({SQLType::DATE}, SQLType::BIGINT, ScalarFunction::UnaryFunction<date_t, int64_t, OP>)); |
289 | operator_set.AddFunction( |
290 | ScalarFunction({SQLType::TIMESTAMP}, SQLType::BIGINT, ScalarFunction::UnaryFunction<timestamp_t, int64_t, OP>)); |
291 | set.AddFunction(operator_set); |
292 | } |
293 | |
294 | struct LastDayOperator { |
295 | template <class TA, class TR> static inline TR Operation(TA input) { |
296 | int32_t yyyy, mm, dd; |
297 | Date::Convert(input, yyyy, mm, dd); |
298 | yyyy += (mm / 12); |
299 | mm %= 12; |
300 | ++mm; |
301 | return Date::FromDate(yyyy, mm, 1) - 1; |
302 | } |
303 | }; |
304 | |
305 | template <> date_t LastDayOperator::Operation(timestamp_t input) { |
306 | return LastDayOperator::Operation<date_t, date_t>(Timestamp::GetDate(input)); |
307 | } |
308 | |
309 | static string_t s_monthNames[] = {"January" , "February" , "March" , "April" , "May" , "June" , |
310 | "July" , "August" , "September" , "October" , "November" , "December" }; |
311 | |
312 | struct MonthNameOperator { |
313 | template <class TA, class TR> static inline TR Operation(TA input) { |
314 | return s_monthNames[MonthOperator::Operation<TA, int64_t>(input) - 1]; |
315 | } |
316 | }; |
317 | |
318 | static string_t s_dayNames[] = {"Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" }; |
319 | |
320 | struct DayNameOperator { |
321 | template <class TA, class TR> static inline TR Operation(TA input) { |
322 | return s_dayNames[DayOfWeekOperator::Operation<TA, int64_t>(input)]; |
323 | } |
324 | }; |
325 | |
326 | void DatePartFun::RegisterFunction(BuiltinFunctions &set) { |
327 | // register the individual operators |
328 | AddDatePartOperator<YearOperator>(set, "year" ); |
329 | AddDatePartOperator<MonthOperator>(set, "month" ); |
330 | AddDatePartOperator<DayOperator>(set, "day" ); |
331 | AddDatePartOperator<DecadeOperator>(set, "decade" ); |
332 | AddDatePartOperator<CenturyOperator>(set, "century" ); |
333 | AddDatePartOperator<MilleniumOperator>(set, "millenium" ); |
334 | AddDatePartOperator<QuarterOperator>(set, "quarter" ); |
335 | AddDatePartOperator<DayOfWeekOperator>(set, "dayofweek" ); |
336 | AddDatePartOperator<ISODayOfWeekOperator>(set, "isodow" ); |
337 | AddDatePartOperator<DayOfYearOperator>(set, "dayofyear" ); |
338 | AddDatePartOperator<WeekOperator>(set, "week" ); |
339 | AddDatePartOperator<EpochOperator>(set, "epoch" ); |
340 | AddDatePartOperator<MicrosecondsOperator>(set, "microsecond" ); |
341 | AddDatePartOperator<MillisecondsOperator>(set, "millisecond" ); |
342 | AddDatePartOperator<SecondsOperator>(set, "second" ); |
343 | AddDatePartOperator<MinutesOperator>(set, "minute" ); |
344 | AddDatePartOperator<HoursOperator>(set, "hour" ); |
345 | |
346 | // register combinations |
347 | AddDatePartOperator<YearWeekOperator>(set, "yearweek" ); |
348 | |
349 | // register various aliases |
350 | AddDatePartOperator<DayOperator>(set, "dayofmonth" ); |
351 | AddDatePartOperator<DayOfWeekOperator>(set, "weekday" ); |
352 | AddDatePartOperator<WeekOperator>(set, "weekofyear" ); // Note that WeekOperator is ISO-8601, not US |
353 | |
354 | // register the last_day function |
355 | ScalarFunctionSet last_day("last_day" ); |
356 | last_day.AddFunction(ScalarFunction({SQLType::DATE}, SQLType::DATE, |
357 | ScalarFunction::UnaryFunction<date_t, date_t, LastDayOperator, true>)); |
358 | last_day.AddFunction(ScalarFunction({SQLType::TIMESTAMP}, SQLType::DATE, |
359 | ScalarFunction::UnaryFunction<timestamp_t, date_t, LastDayOperator, true>)); |
360 | set.AddFunction(last_day); |
361 | |
362 | // register the monthname function |
363 | ScalarFunctionSet monthname("monthname" ); |
364 | monthname.AddFunction(ScalarFunction({SQLType::DATE}, SQLType::VARCHAR, |
365 | ScalarFunction::UnaryFunction<date_t, string_t, MonthNameOperator, true>)); |
366 | monthname.AddFunction( |
367 | ScalarFunction({SQLType::TIMESTAMP}, SQLType::VARCHAR, |
368 | ScalarFunction::UnaryFunction<timestamp_t, string_t, MonthNameOperator, true>)); |
369 | set.AddFunction(monthname); |
370 | |
371 | // register the dayname function |
372 | ScalarFunctionSet dayname("dayname" ); |
373 | dayname.AddFunction(ScalarFunction({SQLType::DATE}, SQLType::VARCHAR, |
374 | ScalarFunction::UnaryFunction<date_t, string_t, DayNameOperator, true>)); |
375 | dayname.AddFunction(ScalarFunction({SQLType::TIMESTAMP}, SQLType::VARCHAR, |
376 | ScalarFunction::UnaryFunction<timestamp_t, string_t, DayNameOperator, true>)); |
377 | set.AddFunction(dayname); |
378 | |
379 | // finally the actual date_part function |
380 | ScalarFunctionSet date_part("date_part" ); |
381 | date_part.AddFunction( |
382 | ScalarFunction({SQLType::VARCHAR, SQLType::DATE}, SQLType::BIGINT, |
383 | ScalarFunction::BinaryFunction<string_t, date_t, int64_t, DatePartOperator, true>)); |
384 | date_part.AddFunction( |
385 | ScalarFunction({SQLType::VARCHAR, SQLType::TIMESTAMP}, SQLType::BIGINT, |
386 | ScalarFunction::BinaryFunction<string_t, timestamp_t, int64_t, DatePartOperator, true>)); |
387 | set.AddFunction(date_part); |
388 | date_part.name = "datepart" ; |
389 | set.AddFunction(date_part); |
390 | } |
391 | |
392 | } // namespace duckdb |
393 | |