1#pragma once
2#include <Core/Types.h>
3#include <Core/DecimalFunctions.h>
4#include <Common/Exception.h>
5#include <common/DateLUTImpl.h>
6#include <Columns/ColumnVector.h>
7#include <Columns/ColumnDecimal.h>
8#include <Functions/FunctionHelpers.h>
9#include <Functions/extractTimeZoneFromFunctionArguments.h>
10#include <DataTypes/DataTypeDateTime.h>
11#include <DataTypes/DataTypeDateTime64.h>
12
13namespace DB
14{
15
16namespace ErrorCodes
17{
18 extern const int ILLEGAL_TYPE_OF_ARGUMENT;
19 extern const int ILLEGAL_COLUMN;
20}
21
22/** Transformations.
23 * Represents two functions - from datetime (UInt32) and from date (UInt16).
24 *
25 * Also, the "factor transformation" F is defined for the T transformation.
26 * This is a transformation of F such that its value identifies the region of monotonicity for T
27 * (for a fixed value of F, the transformation T is monotonic).
28 *
29 * Or, figuratively, if T is similar to taking the remainder of division, then F is similar to division.
30 *
31 * Example: for transformation T "get the day number in the month" (2015-02-03 -> 3),
32 * factor-transformation F is "round to the nearest month" (2015-02-03 -> 2015-02-01).
33 */
34
35static inline UInt32 dateIsNotSupported(const char * name)
36{
37 throw Exception("Illegal type Date of argument for function " + std::string(name), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
38}
39
40/// This factor transformation will say that the function is monotone everywhere.
41struct ZeroTransform
42{
43 static inline UInt16 execute(UInt32, const DateLUTImpl &) { return 0; }
44 static inline UInt16 execute(UInt16, const DateLUTImpl &) { return 0; }
45};
46
47struct ToDateImpl
48{
49 static constexpr auto name = "toDate";
50
51 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
52 {
53 return UInt16(time_zone.toDayNum(t));
54 }
55 static inline UInt16 execute(UInt16 d, const DateLUTImpl &)
56 {
57 return d;
58 }
59
60 using FactorTransform = ZeroTransform;
61};
62
63struct ToStartOfDayImpl
64{
65 static constexpr auto name = "toStartOfDay";
66
67 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
68 {
69 return time_zone.toDate(t);
70 }
71 static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
72 {
73 return time_zone.toDate(DayNum(d));
74 }
75
76 using FactorTransform = ZeroTransform;
77};
78
79struct ToMondayImpl
80{
81 static constexpr auto name = "toMonday";
82
83 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
84 {
85 return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t));
86 }
87 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
88 {
89 return time_zone.toFirstDayNumOfWeek(DayNum(d));
90 }
91
92 using FactorTransform = ZeroTransform;
93};
94
95struct ToStartOfMonthImpl
96{
97 static constexpr auto name = "toStartOfMonth";
98
99 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
100 {
101 return time_zone.toFirstDayNumOfMonth(time_zone.toDayNum(t));
102 }
103 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
104 {
105 return time_zone.toFirstDayNumOfMonth(DayNum(d));
106 }
107
108 using FactorTransform = ZeroTransform;
109};
110
111struct ToStartOfQuarterImpl
112{
113 static constexpr auto name = "toStartOfQuarter";
114
115 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
116 {
117 return time_zone.toFirstDayNumOfQuarter(time_zone.toDayNum(t));
118 }
119 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
120 {
121 return time_zone.toFirstDayNumOfQuarter(DayNum(d));
122 }
123
124 using FactorTransform = ZeroTransform;
125};
126
127struct ToStartOfYearImpl
128{
129 static constexpr auto name = "toStartOfYear";
130
131 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
132 {
133 return time_zone.toFirstDayNumOfYear(time_zone.toDayNum(t));
134 }
135 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
136 {
137 return time_zone.toFirstDayNumOfYear(DayNum(d));
138 }
139
140 using FactorTransform = ZeroTransform;
141};
142
143
144struct ToTimeImpl
145{
146 static constexpr auto name = "toTime";
147
148 /// When transforming to time, the date will be equated to 1970-01-02.
149 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
150 {
151 return time_zone.toTime(t) + 86400;
152 }
153
154 static inline UInt32 execute(UInt16, const DateLUTImpl &)
155 {
156 return dateIsNotSupported(name);
157 }
158
159 using FactorTransform = ToDateImpl;
160};
161
162struct ToStartOfMinuteImpl
163{
164 static constexpr auto name = "toStartOfMinute";
165
166 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
167 {
168 return time_zone.toStartOfMinute(t);
169 }
170 static inline UInt32 execute(UInt16, const DateLUTImpl &)
171 {
172 return dateIsNotSupported(name);
173 }
174
175 using FactorTransform = ZeroTransform;
176};
177
178struct ToStartOfFiveMinuteImpl
179{
180 static constexpr auto name = "toStartOfFiveMinute";
181
182 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
183 {
184 return time_zone.toStartOfFiveMinute(t);
185 }
186 static inline UInt32 execute(UInt16, const DateLUTImpl &)
187 {
188 return dateIsNotSupported(name);
189 }
190
191 using FactorTransform = ZeroTransform;
192};
193
194struct ToStartOfTenMinutesImpl
195{
196 static constexpr auto name = "toStartOfTenMinutes";
197
198 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
199 {
200 return time_zone.toStartOfTenMinutes(t);
201 }
202 static inline UInt32 execute(UInt16, const DateLUTImpl &)
203 {
204 return dateIsNotSupported(name);
205 }
206
207 using FactorTransform = ZeroTransform;
208};
209
210struct ToStartOfFifteenMinutesImpl
211{
212 static constexpr auto name = "toStartOfFifteenMinutes";
213
214 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
215 {
216 return time_zone.toStartOfFifteenMinutes(t);
217 }
218 static inline UInt32 execute(UInt16, const DateLUTImpl &)
219 {
220 return dateIsNotSupported(name);
221 }
222
223 using FactorTransform = ZeroTransform;
224};
225
226/// Round to start of half-an-hour length interval with unspecified offset. This transform is specific for Yandex.Metrica.
227struct TimeSlotImpl
228{
229 static constexpr auto name = "timeSlot";
230
231 static inline UInt32 execute(UInt32 t, const DateLUTImpl &)
232 {
233 return t / 1800 * 1800;
234 }
235
236 static inline UInt32 execute(UInt16, const DateLUTImpl &)
237 {
238 return dateIsNotSupported(name);
239 }
240
241 using FactorTransform = ZeroTransform;
242};
243
244struct ToStartOfHourImpl
245{
246 static constexpr auto name = "toStartOfHour";
247
248 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
249 {
250 return time_zone.toStartOfHour(t);
251 }
252
253 static inline UInt32 execute(UInt16, const DateLUTImpl &)
254 {
255 return dateIsNotSupported(name);
256 }
257
258 using FactorTransform = ZeroTransform;
259};
260
261struct ToYearImpl
262{
263 static constexpr auto name = "toYear";
264
265 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
266 {
267 return time_zone.toYear(t);
268 }
269 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
270 {
271 return time_zone.toYear(DayNum(d));
272 }
273
274 using FactorTransform = ZeroTransform;
275};
276
277struct ToQuarterImpl
278{
279 static constexpr auto name = "toQuarter";
280
281 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
282 {
283 return time_zone.toQuarter(t);
284 }
285 static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
286 {
287 return time_zone.toQuarter(DayNum(d));
288 }
289
290 using FactorTransform = ToStartOfYearImpl;
291};
292
293struct ToMonthImpl
294{
295 static constexpr auto name = "toMonth";
296
297 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
298 {
299 return time_zone.toMonth(t);
300 }
301 static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
302 {
303 return time_zone.toMonth(DayNum(d));
304 }
305
306 using FactorTransform = ToStartOfYearImpl;
307};
308
309struct ToDayOfMonthImpl
310{
311 static constexpr auto name = "toDayOfMonth";
312
313 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
314 {
315 return time_zone.toDayOfMonth(t);
316 }
317 static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
318 {
319 return time_zone.toDayOfMonth(DayNum(d));
320 }
321
322 using FactorTransform = ToStartOfMonthImpl;
323};
324
325struct ToDayOfWeekImpl
326{
327 static constexpr auto name = "toDayOfWeek";
328
329 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
330 {
331 return time_zone.toDayOfWeek(t);
332 }
333 static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
334 {
335 return time_zone.toDayOfWeek(DayNum(d));
336 }
337
338 using FactorTransform = ToMondayImpl;
339};
340
341struct ToDayOfYearImpl
342{
343 static constexpr auto name = "toDayOfYear";
344
345 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
346 {
347 return time_zone.toDayOfYear(t);
348 }
349 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
350 {
351 return time_zone.toDayOfYear(DayNum(d));
352 }
353
354 using FactorTransform = ToStartOfYearImpl;
355};
356
357struct ToHourImpl
358{
359 static constexpr auto name = "toHour";
360
361 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
362 {
363 return time_zone.toHour(t);
364 }
365
366 static inline UInt8 execute(UInt16, const DateLUTImpl &)
367 {
368 return dateIsNotSupported(name);
369 }
370
371 using FactorTransform = ToDateImpl;
372};
373
374struct ToMinuteImpl
375{
376 static constexpr auto name = "toMinute";
377
378 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
379 {
380 return time_zone.toMinute(t);
381 }
382 static inline UInt8 execute(UInt16, const DateLUTImpl &)
383 {
384 return dateIsNotSupported(name);
385 }
386
387 using FactorTransform = ToStartOfHourImpl;
388};
389
390struct ToSecondImpl
391{
392 static constexpr auto name = "toSecond";
393
394 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
395 {
396 return time_zone.toSecond(t);
397 }
398 static inline UInt8 execute(UInt16, const DateLUTImpl &)
399 {
400 return dateIsNotSupported(name);
401 }
402
403 using FactorTransform = ToStartOfMinuteImpl;
404};
405
406struct ToISOYearImpl
407{
408 static constexpr auto name = "toISOYear";
409
410 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
411 {
412 return time_zone.toISOYear(time_zone.toDayNum(t));
413 }
414 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
415 {
416 return time_zone.toISOYear(DayNum(d));
417 }
418
419 using FactorTransform = ZeroTransform;
420};
421
422struct ToStartOfISOYearImpl
423{
424 static constexpr auto name = "toStartOfISOYear";
425
426 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
427 {
428 return time_zone.toFirstDayNumOfISOYear(time_zone.toDayNum(t));
429 }
430 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
431 {
432 return time_zone.toFirstDayNumOfISOYear(DayNum(d));
433 }
434
435 using FactorTransform = ZeroTransform;
436};
437
438struct ToISOWeekImpl
439{
440 static constexpr auto name = "toISOWeek";
441
442 static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
443 {
444 return time_zone.toISOWeek(time_zone.toDayNum(t));
445 }
446 static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
447 {
448 return time_zone.toISOWeek(DayNum(d));
449 }
450
451 using FactorTransform = ToISOYearImpl;
452};
453
454struct ToRelativeYearNumImpl
455{
456 static constexpr auto name = "toRelativeYearNum";
457
458 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
459 {
460 return time_zone.toYear(t);
461 }
462 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
463 {
464 return time_zone.toYear(DayNum(d));
465 }
466
467 using FactorTransform = ZeroTransform;
468};
469
470struct ToRelativeQuarterNumImpl
471{
472 static constexpr auto name = "toRelativeQuarterNum";
473
474 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
475 {
476 return time_zone.toRelativeQuarterNum(t);
477 }
478 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
479 {
480 return time_zone.toRelativeQuarterNum(DayNum(d));
481 }
482
483 using FactorTransform = ZeroTransform;
484};
485
486struct ToRelativeMonthNumImpl
487{
488 static constexpr auto name = "toRelativeMonthNum";
489
490 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
491 {
492 return time_zone.toRelativeMonthNum(t);
493 }
494 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
495 {
496 return time_zone.toRelativeMonthNum(DayNum(d));
497 }
498
499 using FactorTransform = ZeroTransform;
500};
501
502struct ToRelativeWeekNumImpl
503{
504 static constexpr auto name = "toRelativeWeekNum";
505
506 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
507 {
508 return time_zone.toRelativeWeekNum(t);
509 }
510 static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
511 {
512 return time_zone.toRelativeWeekNum(DayNum(d));
513 }
514
515 using FactorTransform = ZeroTransform;
516};
517
518struct ToRelativeDayNumImpl
519{
520 static constexpr auto name = "toRelativeDayNum";
521
522 static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
523 {
524 return time_zone.toDayNum(t);
525 }
526 static inline UInt16 execute(UInt16 d, const DateLUTImpl &)
527 {
528 return static_cast<DayNum>(d);
529 }
530
531 using FactorTransform = ZeroTransform;
532};
533
534
535struct ToRelativeHourNumImpl
536{
537 static constexpr auto name = "toRelativeHourNum";
538
539 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
540 {
541 return time_zone.toRelativeHourNum(t);
542 }
543 static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
544 {
545 return time_zone.toRelativeHourNum(DayNum(d));
546 }
547
548 using FactorTransform = ZeroTransform;
549};
550
551struct ToRelativeMinuteNumImpl
552{
553 static constexpr auto name = "toRelativeMinuteNum";
554
555 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
556 {
557 return time_zone.toRelativeMinuteNum(t);
558 }
559 static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
560 {
561 return time_zone.toRelativeMinuteNum(DayNum(d));
562 }
563
564 using FactorTransform = ZeroTransform;
565};
566
567struct ToRelativeSecondNumImpl
568{
569 static constexpr auto name = "toRelativeSecondNum";
570
571 static inline UInt32 execute(UInt32 t, const DateLUTImpl &)
572 {
573 return t;
574 }
575 static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
576 {
577 return time_zone.fromDayNum(DayNum(d));
578 }
579
580 using FactorTransform = ZeroTransform;
581};
582
583struct ToYYYYMMImpl
584{
585 static constexpr auto name = "toYYYYMM";
586
587 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
588 {
589 return time_zone.toNumYYYYMM(t);
590 }
591 static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
592 {
593 return time_zone.toNumYYYYMM(static_cast<DayNum>(d));
594 }
595
596 using FactorTransform = ZeroTransform;
597};
598
599struct ToYYYYMMDDImpl
600{
601 static constexpr auto name = "toYYYYMMDD";
602
603 static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
604 {
605 return time_zone.toNumYYYYMMDD(t);
606 }
607 static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
608 {
609 return time_zone.toNumYYYYMMDD(static_cast<DayNum>(d));
610 }
611
612 using FactorTransform = ZeroTransform;
613};
614
615struct ToYYYYMMDDhhmmssImpl
616{
617 static constexpr auto name = "toYYYYMMDDhhmmss";
618
619 static inline UInt64 execute(UInt32 t, const DateLUTImpl & time_zone)
620 {
621 return time_zone.toNumYYYYMMDDhhmmss(t);
622 }
623 static inline UInt64 execute(UInt16 d, const DateLUTImpl & time_zone)
624 {
625 return time_zone.toNumYYYYMMDDhhmmss(time_zone.toDate(static_cast<DayNum>(d)));
626 }
627
628 using FactorTransform = ZeroTransform;
629};
630
631
632template <typename FromType, typename ToType, typename Transform>
633struct Transformer
634{
635 template <typename FromTypeVector, typename ToTypeVector>
636 static void vector(const FromTypeVector & vec_from, ToTypeVector & vec_to, const DateLUTImpl & time_zone, const Transform & transform)
637 {
638 size_t size = vec_from.size();
639 vec_to.resize(size);
640
641 for (size_t i = 0; i < size; ++i)
642 vec_to[i] = transform.execute(vec_from[i], time_zone);
643 }
644};
645
646
647template <typename FromDataType, typename ToDataType, typename Transform>
648struct DateTimeTransformImpl
649{
650 static void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/, const Transform & transform = {})
651 {
652 using Op = Transformer<typename FromDataType::FieldType, typename ToDataType::FieldType, Transform>;
653
654 const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 1, 0);
655
656 const ColumnPtr source_col = block.getByPosition(arguments[0]).column;
657 if (const auto * sources = checkAndGetColumn<typename FromDataType::ColumnType>(source_col.get()))
658 {
659 auto mutable_result_col = block.getByPosition(result).type->createColumn();
660 auto * col_to = assert_cast<typename ToDataType::ColumnType *>(mutable_result_col.get());
661
662 Op::vector(sources->getData(), col_to->getData(), time_zone, transform);
663
664 block.getByPosition(result).column = std::move(mutable_result_col);
665 }
666 else
667 {
668 throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
669 + " of first argument of function " + Transform::name,
670 ErrorCodes::ILLEGAL_COLUMN);
671 }
672 }
673};
674
675}
676