提交 7ecc63fe 编写于 作者: A Alexey Milovidov

Functions for interval arithmetic on DateTime and Date: development [#CLICKHOUSE-2].

上级 7bdbd71d
...@@ -94,6 +94,8 @@ struct ConvertImpl ...@@ -94,6 +94,8 @@ struct ConvertImpl
*/ */
struct ToDateTimeImpl struct ToDateTimeImpl
{ {
static constexpr auto name = "toDateTime";
static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.fromDayNum(DayNum_t(d)); return time_zone.fromDayNum(DayNum_t(d));
...@@ -101,7 +103,7 @@ struct ToDateTimeImpl ...@@ -101,7 +103,7 @@ struct ToDateTimeImpl
}; };
template <typename Name> struct ConvertImpl<DataTypeDate, DataTypeDateTime, Name> template <typename Name> struct ConvertImpl<DataTypeDate, DataTypeDateTime, Name>
: DateTimeTransformImpl<UInt16, UInt32, ToDateTimeImpl, Name> {}; : DateTimeTransformImpl<UInt16, UInt32, ToDateTimeImpl> {};
/// Implementation of toDate function. /// Implementation of toDate function.
...@@ -109,6 +111,8 @@ template <typename Name> struct ConvertImpl<DataTypeDate, DataTypeDateTime, Name ...@@ -109,6 +111,8 @@ template <typename Name> struct ConvertImpl<DataTypeDate, DataTypeDateTime, Name
template <typename FromType, typename ToType> template <typename FromType, typename ToType>
struct ToDateTransform32Or64 struct ToDateTransform32Or64
{ {
static constexpr auto name = "toDate";
static inline ToType execute(const FromType & from, const DateLUTImpl & time_zone) static inline ToType execute(const FromType & from, const DateLUTImpl & time_zone)
{ {
return (from < 0xFFFF) ? from : time_zone.toDayNum(from); return (from < 0xFFFF) ? from : time_zone.toDayNum(from);
...@@ -118,7 +122,7 @@ struct ToDateTransform32Or64 ...@@ -118,7 +122,7 @@ struct ToDateTransform32Or64
/** Conversion of DateTime to Date: throw off time component. /** Conversion of DateTime to Date: throw off time component.
*/ */
template <typename Name> struct ConvertImpl<DataTypeDateTime, DataTypeDate, Name> template <typename Name> struct ConvertImpl<DataTypeDateTime, DataTypeDate, Name>
: DateTimeTransformImpl<UInt32, UInt16, ToDateImpl, Name> {}; : DateTimeTransformImpl<UInt32, UInt16, ToDateImpl> {};
/** Special case of converting (U)Int32 or (U)Int64 (and also, for convenience, Float32, Float64) to Date. /** Special case of converting (U)Int32 or (U)Int64 (and also, for convenience, Float32, Float64) to Date.
* If number is less than 65536, then it is treated as DayNum, and if greater or equals, then as unix timestamp. * If number is less than 65536, then it is treated as DayNum, and if greater or equals, then as unix timestamp.
...@@ -128,17 +132,17 @@ template <typename Name> struct ConvertImpl<DataTypeDateTime, DataTypeDate, Name ...@@ -128,17 +132,17 @@ template <typename Name> struct ConvertImpl<DataTypeDateTime, DataTypeDate, Name
* (otherwise such usage would be frequent mistake). * (otherwise such usage would be frequent mistake).
*/ */
template <typename Name> struct ConvertImpl<DataTypeUInt32, DataTypeDate, Name> template <typename Name> struct ConvertImpl<DataTypeUInt32, DataTypeDate, Name>
: DateTimeTransformImpl<UInt32, UInt16, ToDateTransform32Or64<UInt32, UInt16>, Name> {}; : DateTimeTransformImpl<UInt32, UInt16, ToDateTransform32Or64<UInt32, UInt16>> {};
template <typename Name> struct ConvertImpl<DataTypeUInt64, DataTypeDate, Name> template <typename Name> struct ConvertImpl<DataTypeUInt64, DataTypeDate, Name>
: DateTimeTransformImpl<UInt64, UInt16, ToDateTransform32Or64<UInt64, UInt16>, Name> {}; : DateTimeTransformImpl<UInt64, UInt16, ToDateTransform32Or64<UInt64, UInt16>> {};
template <typename Name> struct ConvertImpl<DataTypeInt32, DataTypeDate, Name> template <typename Name> struct ConvertImpl<DataTypeInt32, DataTypeDate, Name>
: DateTimeTransformImpl<Int32, UInt16, ToDateTransform32Or64<Int32, UInt16>, Name> {}; : DateTimeTransformImpl<Int32, UInt16, ToDateTransform32Or64<Int32, UInt16>> {};
template <typename Name> struct ConvertImpl<DataTypeInt64, DataTypeDate, Name> template <typename Name> struct ConvertImpl<DataTypeInt64, DataTypeDate, Name>
: DateTimeTransformImpl<Int64, UInt16, ToDateTransform32Or64<Int64, UInt16>, Name> {}; : DateTimeTransformImpl<Int64, UInt16, ToDateTransform32Or64<Int64, UInt16>> {};
template <typename Name> struct ConvertImpl<DataTypeFloat32, DataTypeDate, Name> template <typename Name> struct ConvertImpl<DataTypeFloat32, DataTypeDate, Name>
: DateTimeTransformImpl<Float32, UInt16, ToDateTransform32Or64<Float32, UInt16>, Name> {}; : DateTimeTransformImpl<Float32, UInt16, ToDateTransform32Or64<Float32, UInt16>> {};
template <typename Name> struct ConvertImpl<DataTypeFloat64, DataTypeDate, Name> template <typename Name> struct ConvertImpl<DataTypeFloat64, DataTypeDate, Name>
: DateTimeTransformImpl<Float64, UInt16, ToDateTransform32Or64<Float64, UInt16>, Name> {}; : DateTimeTransformImpl<Float64, UInt16, ToDateTransform32Or64<Float64, UInt16>> {};
/** Transformation of numbers, dates, datetimes to strings: through formatting. /** Transformation of numbers, dates, datetimes to strings: through formatting.
......
...@@ -37,6 +37,13 @@ void registerFunctionsDateTime(FunctionFactory & factory) ...@@ -37,6 +37,13 @@ void registerFunctionsDateTime(FunctionFactory & factory)
factory.registerFunction<FunctionToYYYYMM>(); factory.registerFunction<FunctionToYYYYMM>();
factory.registerFunction<FunctionToYYYYMMDD>(); factory.registerFunction<FunctionToYYYYMMDD>();
factory.registerFunction<FunctionToYYYYMMDDhhmmss>(); factory.registerFunction<FunctionToYYYYMMDDhhmmss>();
factory.registerFunction<FunctionAddSeconds>();
factory.registerFunction<FunctionAddMinutes>();
factory.registerFunction<FunctionAddHours>();
factory.registerFunction<FunctionAddDays>();
factory.registerFunction<FunctionAddWeeks>();
factory.registerFunction<FunctionAddMonths>();
factory.registerFunction<FunctionAddYears>();
} }
} }
...@@ -44,6 +44,10 @@ public: ...@@ -44,6 +44,10 @@ public:
UInt8 day_of_month; UInt8 day_of_month;
UInt8 day_of_week; UInt8 day_of_week;
/// Total number of days in current month. Actually we can use separate table that is independent of time zone.
/// But due to alignment, this field is totally zero cost.
UInt8 days_in_month;
/// For days, when offset from UTC was changed due to daylight saving time or permanent change, following values could be non zero. /// For days, when offset from UTC was changed due to daylight saving time or permanent change, following values could be non zero.
UInt16 time_at_offset_change; /// In seconds from beginning of the day. Assuming offset never changed close to the end of day (so, value < 65536). UInt16 time_at_offset_change; /// In seconds from beginning of the day. Assuming offset never changed close to the end of day (so, value < 65536).
Int16 amount_of_offset_change; /// Usually -3600 or 3600, but look at Lord Howe Island. Int16 amount_of_offset_change; /// Usually -3600 or 3600, but look at Lord Howe Island.
...@@ -217,13 +221,20 @@ public: ...@@ -217,13 +221,20 @@ public:
return lut[index - (lut[index].day_of_month - 1)].date; return lut[index - (lut[index].day_of_month - 1)].date;
} }
inline size_t daysInMonth(DayNum_t d) const
{
return lut[d].days_in_month;
}
inline size_t daysInMonth(time_t t) const inline size_t daysInMonth(time_t t) const
{ {
size_t today = findIndex(t); return find(t).days_in_month;
size_t start_of_month = today - (lut[today].day_of_month - 1); }
size_t next_month = start_of_month + 31;
size_t start_of_next_month = next_month - (lut[next_month].day_of_month - 1); inline size_t daysInMonth(short year, char month) const
return start_of_next_month - start_of_month; {
auto any_day_of_month = years_lut[year - DATE_LUT_MIN_YEAR] + 31 * (month - 1);
return lut[any_day_of_month].days_in_month;
} }
/** Round to start of day, then shift for specified amount of days. /** Round to start of day, then shift for specified amount of days.
...@@ -445,6 +456,122 @@ public: ...@@ -445,6 +456,122 @@ public:
num % 100); num % 100);
} }
/// Adding calendar intervals.
/// Implementation specific behaviour when delta is too big.
inline time_t addDays(time_t t, ssize_t delta) const
{
size_t index = findIndex(t);
time_t time_offset = toHour(t) * 3600 + toMinute(t) * 60 + toSecond(t);
index += delta;
if (time_offset >= lut[index].time_at_offset_change)
time_offset -= lut[index].amount_of_offset_change;
return lut[index].date + time_offset;
}
inline time_t addWeeks(time_t t, ssize_t delta) const
{
return addDays(t, delta * 7);
}
inline char saturateDayOfMonth(short year, char month, char day_of_month) const
{
if (likely(day_of_month <= 28))
return day_of_month;
auto days_in_month = daysInMonth(year, month);
if (day_of_month > days_in_month)
day_of_month = days_in_month;
return day_of_month;
}
/// If resulting month has less deys than source month, then saturation can happen.
/// Example: 31 Aug + 1 month = 30 Sep.
inline time_t addMonths(time_t t, ssize_t delta) const
{
size_t index = findIndex(t);
const Values & values = lut[index];
time_t time_offset = toHour(t) * 3600 + toMinute(t) * 60 + toSecond(t);
auto month = values.month + delta;
bool year_will_be_next = month > 12;
if (year_will_be_next)
month -= 12;
auto year = values.year + year_will_be_next;
auto day_of_month = saturateDayOfMonth(year, month, values.day_of_month);
DayNum_t result_day = makeDayNum(year, month, day_of_month);
if (time_offset >= lut[result_day].time_at_offset_change)
time_offset -= lut[result_day].amount_of_offset_change;
return lut[result_day].date + time_offset;
}
inline DayNum_t addMonths(DayNum_t d, ssize_t delta) const
{
const Values & values = lut[d];
auto month = values.month + delta;
bool year_will_be_next = month > 12;
if (year_will_be_next)
month -= 12;
auto year = values.year + year_will_be_next;
auto day_of_month = saturateDayOfMonth(year, month, values.day_of_month);
return makeDayNum(year, month, day_of_month);
}
/// Saturation can occur if 29 Feb is mapped to non-leap year.
inline time_t addYears(time_t t, ssize_t delta) const
{
size_t index = findIndex(t);
const Values & values = lut[index];
time_t time_offset = toHour(t) * 3600 + toMinute(t) * 60 + toSecond(t);
auto year = values.year + delta;
auto month = values.month;
auto day_of_month = values.day_of_month;
/// Saturation to 28 Feb can happen.
if (unlikely(day_of_month == 29 && month == 2))
day_of_month = saturateDayOfMonth(year, month, day_of_month);
DayNum_t result_day = makeDayNum(year, month, day_of_month);
if (time_offset >= lut[result_day].time_at_offset_change)
time_offset -= lut[result_day].amount_of_offset_change;
return lut[result_day].date + time_offset;
}
inline DayNum_t addYears(DayNum_t d, ssize_t delta) const
{
const Values & values = lut[d];
auto year = values.year + delta;
auto month = values.month;
auto day_of_month = values.day_of_month;
/// Saturation to 28 Feb can happen.
if (unlikely(day_of_month == 29 && month == 2))
day_of_month = saturateDayOfMonth(year, month, day_of_month);
return makeDayNum(year, month, day_of_month);
}
inline std::string timeToString(time_t t) const inline std::string timeToString(time_t t) const
{ {
const Values & values = find(t); const Values & values = find(t);
......
...@@ -64,6 +64,14 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_) ...@@ -64,6 +64,14 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
values.day_of_week = getDayOfWeek(date); values.day_of_week = getDayOfWeek(date);
values.date = start_of_day; values.date = start_of_day;
if (values.day_of_month == 1)
{
cctz::civil_month month(date);
values.days_in_month = cctz::civil_day(month + 1) - cctz::civil_day(month);
}
else
values.days_in_month = i != 0 ? lut[i - 1].days_in_month : 31;
values.time_at_offset_change = 0; values.time_at_offset_change = 0;
values.amount_of_offset_change = 0; values.amount_of_offset_change = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册