Add fraction part to the time argument, add docs, better test

This commit is contained in:
Nikolay Degterinsky 2023-09-27 20:12:49 +00:00
parent 2b4594e375
commit c1f59eccd5
6 changed files with 197 additions and 45 deletions

View File

@ -137,6 +137,51 @@ Like [makeDateTime](#makedatetime) but produces a [DateTime64](../../sql-referen
makeDateTime32(year, month, day, hour, minute, second[, fraction[, precision[, timezone]]])
```
## timestamp
Converts the first argument 'expr' to type DateTime64(6).
If the second argument 'expr_time' is provided, it adds the specified time to the converted value.
**Syntax**
``` sql
timestamp(expr[, expr_time])
```
**Arguments**
- `expr` - Date or date with time. Type: [String](../../sql-reference/data-types/string.md).
- `expr_time` - Time to add. [String](../../sql-reference/data-types/string.md).
**Examples**
``` sql
SELECT timestamp('2013-12-31') as ts;
```
Result:
``` text
┌─────────────────────────ts─┐
│ 2013-12-31 00:00:00.000000 │
└────────────────────────────┘
```
``` sql
SELECT timestamp('2013-12-31 12:00:00', '12:00:00.11') as ts;
```
Result:
``` text
┌─────────────────────────ts─┐
│ 2014-01-01 00:00:00.110000 │
└────────────────────────────┘
```
**Returned value**
- [DateTime64](../../sql-reference/data-types/datetime64.md)(6)
## timeZone
Returns the timezone of the current session, i.e. the value of setting [session_timezone](../../operations/settings/settings.md#session_timezone).

View File

@ -110,9 +110,6 @@ public:
readDateTime64Text(value, col_to->getScale(), read_buffer, *local_time_zone);
vec_to[i] = value;
// if (!isAllRead(read_buffer))
// throwExceptionForIncompletelyParsedValue(read_buffer, *res_type);
current_offset = next_offset;
}
}
@ -133,9 +130,6 @@ public:
readDateTime64Text(value, col_to->getScale(), read_buffer, *local_time_zone);
vec_to[i] = value;
// if (!isAllRead(read_buffer))
// throwExceptionForIncompletelyParsedValue(read_buffer, *res_type);
current_offset = next_offset;
}
}
@ -150,9 +144,6 @@ public:
if (arguments.size() == 1)
return col_to;
/// hh-mm-ss
static constexpr auto time_length = 8;
const IColumn * col_time = arguments[1].column.get();
if (const ColumnString * col_time_string = checkAndGetColumn<ColumnString>(col_time))
@ -167,20 +158,11 @@ public:
const size_t next_offset = (*offsets)[i];
const size_t string_size = next_offset - current_offset - 1;
if (string_size != time_length)
throw Exception(ErrorCodes::ILLEGAL_COLUMN,
"Illegal size of argument of argument of 2nd argument of function {}",
col_date->getName());
ReadBufferFromMemory read_buffer(&(*chars)[current_offset], string_size);
const UInt8 * s = chars->data() + current_offset;
UInt8 hour = (s[0] - '0') * 10 + (s[1] - '0');
UInt8 minute = (s[3] - '0') * 10 + (s[4] - '0');
UInt8 second = (s[6] - '0') * 10 + (s[7] - '0');
time_t time_offset = hour * 3600 + minute * 60 + second;
vec_to[i] += time_offset * common::exp10_i32(DATETIME_SCALE);
Decimal64 value = 0;
readTime64Text(value, col_to->getScale(), read_buffer);
vec_to[i] += value;
current_offset = next_offset;
}
@ -190,26 +172,17 @@ public:
const ColumnString::Chars * chars = &col_time_fixed_string->getChars();
const size_t fixed_string_size = col_time_fixed_string->getN();
if (fixed_string_size != time_length)
throw Exception(ErrorCodes::ILLEGAL_COLUMN,
"Illegal size of argument of argument of 2nd argument of function {}",
getName());
size_t current_offset = 0;
for (size_t i = 0; i < input_rows_count; ++i)
{
const size_t next_offset = current_offset + fixed_string_size;
const UInt8 * s = chars->data() + current_offset;
ReadBufferFromMemory read_buffer(&(*chars)[current_offset], fixed_string_size);
UInt8 hour = (s[0] - '0') * 10 + (s[1] - '0');
UInt8 minute = (s[3] - '0') * 10 + (s[4] - '0');
UInt8 second = (s[6] - '0') * 10 + (s[7] - '0');
time_t time_offset = hour * 3600 + minute * 60 + second;
vec_to[i] += time_offset * common::exp10_i32(DATETIME_SCALE);
Decimal64 value = 0;
readTime64Text(value, col_to->getScale(), read_buffer);
vec_to[i] += value;
current_offset = next_offset;
}
@ -230,7 +203,23 @@ public:
REGISTER_FUNCTION(Timestamp)
{
factory.registerFunction<FunctionTimestamp>({}, FunctionFactory::CaseInsensitive);
factory.registerFunction<FunctionTimestamp>(FunctionDocumentation{
.description = R"(
Converts the first argument 'expr' to type DateTime64(6).
If the second argument 'expr_time' is provided, it adds the specified time to the converted value.
:::)",
.syntax = "timestamp(expr[, expr_time])",
.arguments = {
{"expr", "Date or date with time. Type: String."},
{"expr_time", "Time to add. Type: String."}
},
.returned_value = "The result of conversion and, optionally, addition. Type: DateTime64(6).",
.examples = {
{"timestamp", "SELECT timestamp('2013-12-31')", "2013-12-31 00:00:00.000000"},
{"timestamp", "SELECT timestamp('2013-12-31 12:00:00')", "2013-12-31 12:00:00.000000"},
{"timestamp", "SELECT timestamp('2013-12-31 12:00:00', '12:00:00.11')", "2014-01-01 00:00:00.110000"},
},
.categories{"DateTime"}}, FunctionFactory::CaseInsensitive);
}
}

View File

@ -1080,6 +1080,98 @@ inline void readDateTimeText(LocalDateTime & datetime, ReadBuffer & buf)
datetime.second((s[6] - '0') * 10 + (s[7] - '0'));
}
/// In (hh)h:mm:ss format.
template <typename ReturnType = void>
inline ReturnType readTimeTextImpl(time_t & time, ReadBuffer & buf)
{
static constexpr bool throw_exception = std::is_same_v<ReturnType, void>;
int16_t hours;
int16_t minutes;
int16_t seconds;
readIntText(hours, buf);
int negative_multiplier = hours < 0 ? -1 : 1;
// :mm:ss
const size_t remaining_time_size = 6;
char s[remaining_time_size];
size_t size = buf.read(s, remaining_time_size);
if (size != remaining_time_size)
{
s[size] = 0;
if constexpr (throw_exception)
throw ParsingException(ErrorCodes::CANNOT_PARSE_DATETIME, "Cannot parse DateTime {}", s);
else
return false;
}
minutes = (s[1] - '0') * 10 + (s[2] - '0');
seconds = (s[4] - '0') * 10 + (s[5] - '0');
time = hours * 3600 + (minutes * 60 + seconds) * negative_multiplier;
return ReturnType(true);
}
template <typename ReturnType>
inline ReturnType readTimeTextImpl(Decimal64 & time64, UInt32 scale, ReadBuffer & buf)
{
time_t whole;
if (!readTimeTextImpl<bool>(whole, buf))
{
return ReturnType(false);
}
DB::DecimalUtils::DecimalComponents<Decimal64> components{static_cast<Decimal64::NativeType>(whole), 0};
if (!buf.eof() && *buf.position() == '.')
{
++buf.position();
/// Read digits, up to 'scale' positions.
for (size_t i = 0; i < scale; ++i)
{
if (!buf.eof() && isNumericASCII(*buf.position()))
{
components.fractional *= 10;
components.fractional += *buf.position() - '0';
++buf.position();
}
else
{
/// Adjust to scale.
components.fractional *= 10;
}
}
/// Ignore digits that are out of precision.
while (!buf.eof() && isNumericASCII(*buf.position()))
++buf.position();
}
bool is_ok = true;
if constexpr (std::is_same_v<ReturnType, void>)
{
time64 = DecimalUtils::decimalFromComponents<Decimal64>(components, scale);
}
else
{
is_ok = DecimalUtils::tryGetDecimalFromComponents<Decimal64>(components, scale, time64);
}
return ReturnType(is_ok);
}
inline void readTime64Text(Decimal64 & time64, UInt32 scale, ReadBuffer & buf)
{
readTimeTextImpl<void>(time64, scale, buf);
}
/// Generic methods to read value in native binary format.
template <typename T>

View File

@ -690,7 +690,6 @@ throwIf
tid
timeSlot
timeSlots
timestamp
timezoneOf
timezoneOffset
toBool

View File

@ -1,4 +1,15 @@
2003-12-31 00:00:00.000000
2004-01-01 00:00:00.000000
2003-12-31 00:00:00.000000
2004-01-01 00:00:00.000000
2013-12-31 00:00:00.000000
2013-12-31 12:00:00.000000
2013-12-31 12:00:00.111111
2014-01-01 00:01:02.000000
2014-01-01 00:01:02.100000
2014-01-01 00:01:02.110000
2014-01-01 00:01:02.111000
2014-01-01 00:01:02.111100
2014-01-01 00:01:02.111110
2014-01-01 00:01:02.111111
2013-12-30 23:58:57.888889
2013-12-31 10:58:57.888889
2013-12-27 07:58:57.888889
2013-12-31 00:00:00.000000
2014-01-01 00:00:00.000000

View File

@ -1,5 +1,21 @@
SELECT TIMESTAMP('2003-12-31');
SELECT TIMESTAMP('2003-12-31 12:00:00', '12:00:00');
SELECT TIMESTAMP('2013-12-31');
SELECT TIMESTAMP('2013-12-31 12:00:00');
SELECT TIMESTAMP('2013-12-31 12:00:00.111111');
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:01:02');
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:01:02.1');
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:01:02.11');
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:01:02.111');
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:01:02.1111');
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:01:02.11111');
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:01:02.111111');
SELECT TIMESTAMP('2013-12-31 12:00:00', '-12:01:02.111111');
SELECT TIMESTAMP('2013-12-31 12:00:00', '-1:01:02.111111');
SELECT TIMESTAMP('2013-12-31 12:00:00', '-100:01:02.111111');
SELECT TIMESTAMP(materialize('2003-12-31'));
SELECT TIMESTAMP(materialize('2003-12-31 12:00:00'), materialize('12:00:00'));
SELECT TIMESTAMP(materialize('2013-12-31'));
SELECT TIMESTAMP(materialize('2013-12-31 12:00:00'), materialize('12:00:00'));
SELECT TIMESTAMP(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT TIMESTAMP('2013-12-31 12:00:00', '12:00:00', ''); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT TIMESTAMP(1); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
SELECT TIMESTAMP(1, 2); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }