add maximum_unit arg to formatReadableTimeDelta function

This commit is contained in:
filipe 2020-10-03 19:38:51 -03:00
parent 077ee81177
commit 146f973437
4 changed files with 172 additions and 75 deletions

View File

@ -538,26 +538,51 @@ SELECT
└────────────────┴───────────────────┘ └────────────────┴───────────────────┘
``` ```
## formatReadableTimeDelta(x) {#formatreadabletimedeltax} ## formatReadableTimeDelta {#formatreadabletimedelta}
Accepts the time delta in seconds. Returns a time delta with (year, month, day, hour, minute, second) as a string. Accepts the time delta in seconds. Returns a time delta with (year, month, day, hour, minute, second) as a string.
**Syntax**
``` sql
formatReadableTimeDelta(column[, maximum_unit])
```
**Parameters**
- `column` — A column with numeric time delta.
- `maximum_unit` — Optional. Maximum unit to show. Acceptable values seconds, minutes, hours, days, months, years.
Example: Example:
``` sql ``` sql
SELECT SELECT
arrayJoin([100, 12345, 432546534]) AS number, arrayJoin([100, 12345, 432546534]) AS elapsed,
formatReadableTimeDelta(number) AS time_delta formatReadableTimeDelta(elapsed) AS time_delta
``` ```
``` text ``` text
┌─────number─┬─time_delta ─────────────────────────────────────────────────────┐ ┌────elapsed─┬─time_delta ─────────────────────────────────────────────────────┐
│ 100 │ 1 minute and 40 seconds │ │ 100 │ 1 minute and 40 seconds │
│ 12345 │ 3 hours, 25 minutes and 45 seconds │ │ 12345 │ 3 hours, 25 minutes and 45 seconds │
│ 432546534 │ 13 years, 8 months, 17 days, 7 hours, 48 minutes and 54 seconds │ │ 432546534 │ 13 years, 8 months, 17 days, 7 hours, 48 minutes and 54 seconds │
└────────────┴─────────────────────────────────────────────────────────────────┘ └────────────┴─────────────────────────────────────────────────────────────────┘
``` ```
``` sql
SELECT
arrayJoin([100, 12345, 432546534]) AS elapsed,
formatReadableTimeDelta(elapsed, 'minutes') AS time_delta
```
``` text
┌────elapsed─┬─time_delta ─────────────────────────────────────────────────────┐
│ 100 │ 1 minute and 40 seconds │
│ 12345 │ 205 minutes and 45 seconds │
│ 432546534 │ 7209108 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.

View File

@ -18,6 +18,7 @@ namespace DB
namespace ErrorCodes namespace ErrorCodes
{ {
extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
} }
@ -285,23 +286,44 @@ public:
static constexpr auto name = "formatReadableTimeDelta"; static constexpr auto name = "formatReadableTimeDelta";
static FunctionPtr create(const Context &) { return std::make_shared<FunctionFormatReadableTimeDelta>(); } static FunctionPtr create(const Context &) { return std::make_shared<FunctionFormatReadableTimeDelta>(); }
String getName() const override String getName() const override { return name; }
{
return name;
}
size_t getNumberOfArguments() const override { return 1; } bool isVariadic() const override { return true; }
size_t getNumberOfArguments() const override { return 0; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{ {
if (arguments.size() < 1)
throw Exception(
"Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size())
+ ", should be at least 1.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
if (arguments.size() > 2)
throw Exception(
"Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size())
+ ", should be at most 2.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
const IDataType & type = *arguments[0]; const IDataType & type = *arguments[0];
if (!isNativeNumber(type)) if (!isNativeNumber(type))
throw Exception("Cannot format " + type.getName() + " as time delta", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); throw Exception("Cannot format " + type.getName() + " as time delta", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
if (arguments.size() == 2)
{
const auto * maximum_unit_arg = arguments[1].get();
if (!isStringOrFixedString(maximum_unit_arg))
throw Exception("Illegal type " + maximum_unit_arg->getName() + " of argument maximum_unit of function "
+ getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
return std::make_shared<DataTypeString>(); return std::make_shared<DataTypeString>();
} }
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; }
bool useDefaultImplementationForConstants() const override { return true; } bool useDefaultImplementationForConstants() const override { return true; }
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) const override void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) const override
@ -322,46 +344,73 @@ public:
} }
private: private:
void formatReadableTimeDelta(double value, DB::WriteBuffer & out) const
void formatReadableTimeDelta(double value, String maximum_unit, DB::WriteBuffer & out) const
{ {
// 60 SECONDS = 1 MINUTE // 60 SECONDS = 1 MINUTE
// 3600 SECONDS = 1 HOUR // 3600 SECONDS = 1 HOUR
// 86400 SECONDS = 1 DAY // 86400 SECONDS = 1 DAY
// 30.5 DAYS = 1 MONTH // 2635200 SECONDS = 30.5 DAYS = 1 MONTH
// 365 DAYS = 1 YEAR // 31536000 SECONDS = 365 DAYS = 1 YEAR
char sig = value<0?-1:1; char sig = value<0?-1:1;
int maximum_unit_int = 6;
if (maximum_unit == "seconds")
maximum_unit_int = 1;
else if (maximum_unit == "minutes")
maximum_unit_int = 2;
else if (maximum_unit == "hours")
maximum_unit_int = 3;
else if (maximum_unit == "days")
maximum_unit_int = 4;
else if (maximum_unit == "months")
maximum_unit_int = 5;
else if (maximum_unit == "years")
maximum_unit_int = 6;
value *= sig; value *= sig;
long long int date = value / 86400; double aux = 0;
double time = value - date * 86400;
long long int hours = time / 3600; long long int years = maximum_unit_int < 6 ? 0 : value / 31536000;
long long int minutes = (time - hours * 3600) / 60; aux += years * 31536000;
double seconds = time - hours * 3600 - minutes * 60; long long int months = maximum_unit_int < 5 ? 0 : (value - aux) / 2635200;
aux += months * 2635200;
long long int years = date / 365; long long int days = maximum_unit_int < 4 ? 0 : (value - aux) / 86400;
long long int months = (date - years * 365) / 30.5; aux += days * 86400;
long long int days = date - years * 365 - months * 30.5; long long int hours = maximum_unit_int < 3 ? 0 : (value - aux) / 3600;
aux += hours * 3600;
long long int minutes = maximum_unit_int < 2 ? 0 : (value - aux) / 60;
aux += minutes * 60;
double seconds = maximum_unit_int < 1 ? 0 : value - aux;
std::vector<String> parts; std::vector<String> parts;
if (years) /* If value is bigger than 2**64 (292471208677 years) overflow happens
To prevent wrong results the function shows only the year
and maximum_unit is ignored
*/
if (value > 9223372036854775808.0)
{ {
parts.push_back(std::to_string(years) + (years==1?" year":" years")); std::string years_str = std::to_string(value/31536000.0);
years_str.erase(years_str.find('.'), std::string::npos);
parts.push_back(years_str + " years");
} }
if (months) else
{
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 (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"));
}
if (hours) if (hours)
{ {
parts.push_back(std::to_string(hours) + (hours==1?" hour":" hours")); parts.push_back(std::to_string(hours) + (hours==1?" hour":" hours"));
@ -379,7 +428,6 @@ private:
} }
} }
String str_value; String str_value;
for(size_t i=0; i<parts.size(); i++) for(size_t i=0; i<parts.size(); i++)
{ {
@ -400,6 +448,17 @@ private:
template <typename T> template <typename T>
bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) const bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) const
{ {
String maximum_unit = "";
if (arguments.size() == 2)
{
const ColumnPtr & maximum_unit_column = block.getByPosition(arguments[1]).column;
if (const ColumnConst * maximum_unit_const_col = checkAndGetColumnConstStringOrFixedString(maximum_unit_column.get()))
maximum_unit = maximum_unit_const_col->getValue<String>();
else
throw Exception(
"Illegal column " + maximum_unit_const_col->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN);
}
if (const ColumnVector<T> * col_from = checkAndGetColumn<ColumnVector<T>>(block.getByPosition(arguments[0]).column.get())) if (const ColumnVector<T> * col_from = checkAndGetColumn<ColumnVector<T>>(block.getByPosition(arguments[0]).column.get()))
{ {
auto col_to = ColumnString::create(); auto col_to = ColumnString::create();
@ -415,7 +474,7 @@ private:
for (size_t i = 0; i < size; ++i) for (size_t i = 0; i < size; ++i)
{ {
formatReadableTimeDelta(static_cast<double>(vec_from[i]), buf_to); formatReadableTimeDelta(static_cast<double>(vec_from[i]), maximum_unit, buf_to);
writeChar(0, buf_to); writeChar(0, buf_to);
offsets_to[i] = buf_to.count(); offsets_to[i] = buf_to.count();
} }

View File

@ -1,30 +1,30 @@
1 second 1 second 1 5.5 seconds
2 seconds 2 seconds 60 5 minutes and 30 seconds
7 seconds 7 seconds 3600 5 hours and 30 minutes
20 seconds 20 seconds 86400 5 days and 12 hours
54 seconds 54 seconds 2592000 5 months, 12 days and 12 hours
2 minutes and 28 seconds 2 minutes and 28 seconds 31536000 5 years, 5 months and 30 days
6 minutes and 43 seconds 6 minutes and 43 seconds minutes 1 5.5 seconds
18 minutes and 16 seconds 18 minutes and 16 seconds minutes 60 5 minutes and 30 seconds
49 minutes and 40 seconds 49 minutes and 40 seconds minutes 3600 330 minutes
2 hours, 15 minutes and 3 seconds 2 hours, 15 minutes and 3 seconds minutes 86400 7920 minutes
6 hours, 7 minutes and 6 seconds 6 hours, 7 minutes and 6 seconds minutes 2592000 237600 minutes
16 hours, 37 minutes and 54 seconds 16 hours, 37 minutes and 54 seconds minutes 31536000 2890800 minutes
1 day, 21 hours, 12 minutes and 34 seconds 1 day, 21 hours, 12 minutes and 34 seconds hours 1 5.5 seconds
5 days, 2 hours, 53 minutes and 33 seconds 5 days, 2 hours, 53 minutes and 33 seconds hours 60 5 minutes and 30 seconds
13 days, 22 hours, 3 minutes and 24 seconds 13 days, 22 hours, 3 minutes and 24 seconds hours 3600 5 hours and 30 minutes
1month, 6 days, 20 hours, 3 minutes and 37 seconds 1month, 6 days, 20 hours, 3 minutes and 37 seconds hours 86400 132 hours
3 months, 10 days, 20 hours, 21 minutes and 50 seconds 3 months, 10 days, 20 hours, 21 minutes and 50 seconds hours 2592000 3960 hours
9 months, 4 days, 13 hours, 42 minutes and 32 seconds 9 months, 4 days, 13 hours, 42 minutes and 32 seconds hours 31536000 48180 hours
2 years, 29 days, 22 hours, 52 minutes and 49 seconds 2 years, 29 days, 22 hours, 52 minutes and 49 seconds days 1 5.5 seconds
5 years, 7 months, 26 days, 18 hours and 25 minutes 5 years, 7 months, 26 days, 18 hours and 25 minutes days 60 5 minutes and 30 seconds
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 days 3600 5 hours and 30 minutes
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 days 86400 5 days and 12 hours
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 days 2592000 165 days
309 years, 2 days, 1 hour, 50 minutes and 46 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds days 31536000 2007 days and 12 hours
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 months 1 5.5 seconds
2283 years, 3 months, 3 days, 55 minutes and 37 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds months 60 5 minutes and 30 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 months 3600 5 hours and 30 minutes
16871 years, 1month, 19 days, 17 hours, 56 minutes and 42 seconds - 68 years, 1month, 4 days, 3 hours, 14 minutes and 8 seconds months 86400 5 days and 12 hours
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 months 2592000 5 months, 12 days and 12 hours
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 months 31536000 65 months and 25 days

View File

@ -1,6 +1,19 @@
WITH round(exp(number), 6) AS x, toUInt64(x) AS y, toInt32(x) AS z SELECT
SELECT arrayJoin([1, 60, 60*60, 60*60*24, 60*60*24*30, 60*60*24*365]) AS elapsed,
formatReadableTimeDelta(y), formatReadableTimeDelta(elapsed*5.5) AS time_delta;
formatReadableTimeDelta(z) SELECT
FROM system.numbers 'minutes' AS maximum_unit,
LIMIT 30; arrayJoin([1, 60, 60*60, 60*60*24, 60*60*24*30, 60*60*24*365]) AS elapsed,
formatReadableTimeDelta(elapsed*5.5, maximum_unit) AS time_delta;
SELECT
'hours' AS maximum_unit,
arrayJoin([1, 60, 60*60, 60*60*24, 60*60*24*30, 60*60*24*365]) AS elapsed,
formatReadableTimeDelta(elapsed*5.5, maximum_unit) AS time_delta;
SELECT
'days' AS maximum_unit,
arrayJoin([1, 60, 60*60, 60*60*24, 60*60*24*30, 60*60*24*365]) AS elapsed,
formatReadableTimeDelta(elapsed*5.5, maximum_unit) AS time_delta;
SELECT
'months' AS maximum_unit,
arrayJoin([1, 60, 60*60, 60*60*24, 60*60*24*30, 60*60*24*365]) AS elapsed,
formatReadableTimeDelta(elapsed*5.5, maximum_unit) AS time_delta;