add an SQL function formatReadableTimeDelta to format time delta

This commit is contained in:
filipe 2020-09-30 23:55:46 -03:00
parent 9808d0be81
commit 077ee81177
6 changed files with 209 additions and 0 deletions

View File

@ -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}
Returns the smallest value from a and b.

View File

@ -10,6 +10,7 @@ void registerFunctionsFormatting(FunctionFactory & factory)
factory.registerFunction<FunctionBitmaskToList>();
factory.registerFunction<FunctionFormatReadableSize>();
factory.registerFunction<FunctionFormatReadableQuantity>();
factory.registerFunction<FunctionFormatReadableTimeDelta>();
}
}

View File

@ -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;
}
};
}

View File

@ -175,6 +175,7 @@ SELECT protocol(NULL);
SELECT toInt16OrZero(NULL);
SELECT formatReadableSize(NULL);
SELECT formatReadableQuantity(NULL);
SELECT formatReadableTimeDelta(NULL);
SELECT concatAssumeInjective(NULL);
SELECT toString(NULL);
SELECT MACStringToNum(NULL);

View 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

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