mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-18 12:22:12 +00:00
add an SQL function formatReadableTimeDelta to format time delta
This commit is contained in:
parent
9808d0be81
commit
077ee81177
@ -538,6 +538,26 @@ SELECT
|
|||||||
└────────────────┴───────────────────┘
|
└────────────────┴───────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## formatReadableTimeDelta(x) {#formatreadabletimedeltax}
|
||||||
|
|
||||||
|
Accepts the time delta in seconds. Returns a time delta with (year, month, day, hour, minute, second) as a string.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` sql
|
||||||
|
SELECT
|
||||||
|
arrayJoin([100, 12345, 432546534]) AS number,
|
||||||
|
formatReadableTimeDelta(number) AS time_delta
|
||||||
|
```
|
||||||
|
|
||||||
|
``` text
|
||||||
|
┌─────number─┬─time_delta ─────────────────────────────────────────────────────┐
|
||||||
|
│ 100 │ 1 minute and 40 seconds │
|
||||||
|
│ 12345 │ 3 hours, 25 minutes and 45 seconds │
|
||||||
|
│ 432546534 │ 13 years, 8 months, 17 days, 7 hours, 48 minutes and 54 seconds │
|
||||||
|
└────────────┴─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
## least(a, b) {#leasta-b}
|
## least(a, b) {#leasta-b}
|
||||||
|
|
||||||
Returns the smallest value from a and b.
|
Returns the smallest value from a and b.
|
||||||
|
@ -10,6 +10,7 @@ void registerFunctionsFormatting(FunctionFactory & factory)
|
|||||||
factory.registerFunction<FunctionBitmaskToList>();
|
factory.registerFunction<FunctionBitmaskToList>();
|
||||||
factory.registerFunction<FunctionFormatReadableSize>();
|
factory.registerFunction<FunctionFormatReadableSize>();
|
||||||
factory.registerFunction<FunctionFormatReadableQuantity>();
|
factory.registerFunction<FunctionFormatReadableQuantity>();
|
||||||
|
factory.registerFunction<FunctionFormatReadableTimeDelta>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -278,4 +278,155 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionFormatReadableTimeDelta : public IFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr auto name = "formatReadableTimeDelta";
|
||||||
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionFormatReadableTimeDelta>(); }
|
||||||
|
|
||||||
|
String getName() const override
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getNumberOfArguments() const override { return 1; }
|
||||||
|
|
||||||
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||||
|
{
|
||||||
|
const IDataType & type = *arguments[0];
|
||||||
|
|
||||||
|
if (!isNativeNumber(type))
|
||||||
|
throw Exception("Cannot format " + type.getName() + " as time delta", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||||
|
|
||||||
|
return std::make_shared<DataTypeString>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useDefaultImplementationForConstants() const override { return true; }
|
||||||
|
|
||||||
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) const override
|
||||||
|
{
|
||||||
|
if (!(executeType<UInt8>(block, arguments, result)
|
||||||
|
|| executeType<UInt16>(block, arguments, result)
|
||||||
|
|| executeType<UInt32>(block, arguments, result)
|
||||||
|
|| executeType<UInt64>(block, arguments, result)
|
||||||
|
|| executeType<Int8>(block, arguments, result)
|
||||||
|
|| executeType<Int16>(block, arguments, result)
|
||||||
|
|| executeType<Int32>(block, arguments, result)
|
||||||
|
|| executeType<Int64>(block, arguments, result)
|
||||||
|
|| executeType<Float32>(block, arguments, result)
|
||||||
|
|| executeType<Float64>(block, arguments, result)))
|
||||||
|
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
|
||||||
|
+ " of argument of function " + getName(),
|
||||||
|
ErrorCodes::ILLEGAL_COLUMN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void formatReadableTimeDelta(double value, DB::WriteBuffer & out) const
|
||||||
|
{
|
||||||
|
// 60 SECONDS = 1 MINUTE
|
||||||
|
// 3600 SECONDS = 1 HOUR
|
||||||
|
// 86400 SECONDS = 1 DAY
|
||||||
|
// 30.5 DAYS = 1 MONTH
|
||||||
|
// 365 DAYS = 1 YEAR
|
||||||
|
|
||||||
|
char sig = value<0?-1:1;
|
||||||
|
value *= sig;
|
||||||
|
|
||||||
|
long long int date = value / 86400;
|
||||||
|
double time = value - date * 86400;
|
||||||
|
|
||||||
|
long long int hours = time / 3600;
|
||||||
|
long long int minutes = (time - hours * 3600) / 60;
|
||||||
|
double seconds = time - hours * 3600 - minutes * 60;
|
||||||
|
|
||||||
|
long long int years = date / 365;
|
||||||
|
long long int months = (date - years * 365) / 30.5;
|
||||||
|
long long int days = date - years * 365 - months * 30.5;
|
||||||
|
|
||||||
|
std::vector<String> parts;
|
||||||
|
|
||||||
|
if (years)
|
||||||
|
{
|
||||||
|
parts.push_back(std::to_string(years) + (years==1?" year":" years"));
|
||||||
|
}
|
||||||
|
if (months)
|
||||||
|
{
|
||||||
|
parts.push_back(std::to_string(months) + (months==1?"month":" months"));
|
||||||
|
}
|
||||||
|
if (days)
|
||||||
|
{
|
||||||
|
parts.push_back(std::to_string(days) + (days==1?" day":" days"));
|
||||||
|
}
|
||||||
|
// Test overflow 2^64
|
||||||
|
// If overflow happens then hide the time
|
||||||
|
if (time < 9223372036854775808.0)
|
||||||
|
{
|
||||||
|
if (hours)
|
||||||
|
{
|
||||||
|
parts.push_back(std::to_string(hours) + (hours==1?" hour":" hours"));
|
||||||
|
}
|
||||||
|
if (minutes)
|
||||||
|
{
|
||||||
|
parts.push_back(std::to_string(minutes) + (minutes==1?" minute":" minutes"));
|
||||||
|
}
|
||||||
|
if (seconds)
|
||||||
|
{
|
||||||
|
std::string seconds_str = std::to_string(seconds);
|
||||||
|
seconds_str.erase(seconds_str.find_last_not_of('0') + 1, std::string::npos);
|
||||||
|
seconds_str.erase(seconds_str.find_last_not_of('.') + 1, std::string::npos);
|
||||||
|
parts.push_back(seconds_str + (seconds==1?" second":" seconds"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String str_value;
|
||||||
|
for(size_t i=0; i<parts.size(); i++)
|
||||||
|
{
|
||||||
|
if(!str_value.empty())
|
||||||
|
{
|
||||||
|
if (i == parts.size()-1)
|
||||||
|
str_value += " and ";
|
||||||
|
else
|
||||||
|
str_value += ", ";
|
||||||
|
}
|
||||||
|
str_value += parts[i];
|
||||||
|
}
|
||||||
|
if (sig < 0)
|
||||||
|
str_value = "- " + str_value;
|
||||||
|
writeCString(str_value.c_str(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) const
|
||||||
|
{
|
||||||
|
if (const ColumnVector<T> * col_from = checkAndGetColumn<ColumnVector<T>>(block.getByPosition(arguments[0]).column.get()))
|
||||||
|
{
|
||||||
|
auto col_to = ColumnString::create();
|
||||||
|
|
||||||
|
const typename ColumnVector<T>::Container & vec_from = col_from->getData();
|
||||||
|
ColumnString::Chars & data_to = col_to->getChars();
|
||||||
|
ColumnString::Offsets & offsets_to = col_to->getOffsets();
|
||||||
|
size_t size = vec_from.size();
|
||||||
|
data_to.resize(size * 2);
|
||||||
|
offsets_to.resize(size);
|
||||||
|
|
||||||
|
WriteBufferFromVector<ColumnString::Chars> buf_to(data_to);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
formatReadableTimeDelta(static_cast<double>(vec_from[i]), buf_to);
|
||||||
|
writeChar(0, buf_to);
|
||||||
|
offsets_to[i] = buf_to.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_to.finalize();
|
||||||
|
block.getByPosition(result).column = std::move(col_to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,7 @@ SELECT protocol(NULL);
|
|||||||
SELECT toInt16OrZero(NULL);
|
SELECT toInt16OrZero(NULL);
|
||||||
SELECT formatReadableSize(NULL);
|
SELECT formatReadableSize(NULL);
|
||||||
SELECT formatReadableQuantity(NULL);
|
SELECT formatReadableQuantity(NULL);
|
||||||
|
SELECT formatReadableTimeDelta(NULL);
|
||||||
SELECT concatAssumeInjective(NULL);
|
SELECT concatAssumeInjective(NULL);
|
||||||
SELECT toString(NULL);
|
SELECT toString(NULL);
|
||||||
SELECT MACStringToNum(NULL);
|
SELECT MACStringToNum(NULL);
|
||||||
|
30
tests/queries/0_stateless/01511_format_readable_timedelta.reference
Executable file
30
tests/queries/0_stateless/01511_format_readable_timedelta.reference
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
1 second 1 second
|
||||||
|
2 seconds 2 seconds
|
||||||
|
7 seconds 7 seconds
|
||||||
|
20 seconds 20 seconds
|
||||||
|
54 seconds 54 seconds
|
||||||
|
2 minutes and 28 seconds 2 minutes and 28 seconds
|
||||||
|
6 minutes and 43 seconds 6 minutes and 43 seconds
|
||||||
|
18 minutes and 16 seconds 18 minutes and 16 seconds
|
||||||
|
49 minutes and 40 seconds 49 minutes and 40 seconds
|
||||||
|
2 hours, 15 minutes and 3 seconds 2 hours, 15 minutes and 3 seconds
|
||||||
|
6 hours, 7 minutes and 6 seconds 6 hours, 7 minutes and 6 seconds
|
||||||
|
16 hours, 37 minutes and 54 seconds 16 hours, 37 minutes and 54 seconds
|
||||||
|
1 day, 21 hours, 12 minutes and 34 seconds 1 day, 21 hours, 12 minutes and 34 seconds
|
||||||
|
5 days, 2 hours, 53 minutes and 33 seconds 5 days, 2 hours, 53 minutes and 33 seconds
|
||||||
|
13 days, 22 hours, 3 minutes and 24 seconds 13 days, 22 hours, 3 minutes and 24 seconds
|
||||||
|
1month, 6 days, 20 hours, 3 minutes and 37 seconds 1month, 6 days, 20 hours, 3 minutes and 37 seconds
|
||||||
|
3 months, 10 days, 20 hours, 21 minutes and 50 seconds 3 months, 10 days, 20 hours, 21 minutes and 50 seconds
|
||||||
|
9 months, 4 days, 13 hours, 42 minutes and 32 seconds 9 months, 4 days, 13 hours, 42 minutes and 32 seconds
|
||||||
|
2 years, 29 days, 22 hours, 52 minutes and 49 seconds 2 years, 29 days, 22 hours, 52 minutes and 49 seconds
|
||||||
|
5 years, 7 months, 26 days, 18 hours and 25 minutes 5 years, 7 months, 26 days, 18 hours and 25 minutes
|
||||||
|
15 years, 4 months, 18 days, 8 hours, 6 minutes and 35 seconds 15 years, 4 months, 18 days, 8 hours, 6 minutes and 35 seconds
|
||||||
|
41 years, 9 months, 24 days, 1 hour, 42 minutes and 14 seconds 41 years, 9 months, 24 days, 1 hour, 42 minutes and 14 seconds
|
||||||
|
113 years, 8 months, 3 days, 1 hour, 7 minutes and 26 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
||||||
|
309 years, 2 days, 1 hour, 50 minutes and 46 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
||||||
|
839 years, 11 months, 16 days, 1 hour, 28 minutes and 49 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
||||||
|
2283 years, 3 months, 3 days, 55 minutes and 37 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
||||||
|
6206 years, 6 months, 15 days, 23 hours, 57 minutes and 8 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
||||||
|
16871 years, 1month, 19 days, 17 hours, 56 minutes and 42 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
||||||
|
45860 years, 6 months, 3 days, 9 hours, 24 minutes and 52 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
||||||
|
124661 years, 9 months, 14 days, 8 hours, 45 minutes and 42 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds
|
6
tests/queries/0_stateless/01511_format_readable_timedelta.sql
Executable file
6
tests/queries/0_stateless/01511_format_readable_timedelta.sql
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
WITH round(exp(number), 6) AS x, toUInt64(x) AS y, toInt32(x) AS z
|
||||||
|
SELECT
|
||||||
|
formatReadableTimeDelta(y),
|
||||||
|
formatReadableTimeDelta(z)
|
||||||
|
FROM system.numbers
|
||||||
|
LIMIT 30;
|
Loading…
Reference in New Issue
Block a user