| 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 | |