-
Notifications
You must be signed in to change notification settings - Fork 25.2k
SQL: Implement DATETIME_FORMAT function for date/time formatting #54832
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Implement TO_CHAR(<date/datetime/time>, <pattern>) function with aliases: TOCHAR, DATE_FORMAT, FORMAT which allows for formatting a timestamp to the specified format. The patterns allowed as those of `java.time.format.DateTimeFormatter`. Related to elastic#53714
Pinging @elastic/es-ql (:Query Languages/SQL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments - the only remark I have is about the function name.
Looks good otherwise.
*Description*: Returns the date/datetime/time as a string using the format specified in the 2nd argument. The formatting | ||
pattern used is the one from | ||
https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/time/format/DateTimeFormatter.html[`java.time.format.DateTimeFormatter`]. | ||
If any of the two arguments is `null` or the pattern is an empty string a `null` is returned. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop a : is string null
is returned.
If the pattern is empty, null be returned - is that the behavior of the other DBs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MySQL and PostgreSQL return null
for empty pattern, SQL Server returns string in the default format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's go with the PostgreSQL.
@@ -60,7 +61,8 @@ DAY_OF_WEEK |SCALAR | |||
DAY_OF_YEAR |SCALAR | |||
DOM |SCALAR | |||
DOW |SCALAR | |||
DOY |SCALAR | |||
DOY |SCALAR | |||
FORMAT |SCALAR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FORMAT
seems a bit too generic - is there any DB that uses it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SQL Server
import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isString; | ||
import static org.elasticsearch.xpack.sql.expression.SqlTypeResolutions.isDateOrTime; | ||
|
||
public class ToChar extends BinaryDateTimeFunction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the function called ToChar
and not DateFormat
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No real reason, I just started with the to_char
from PostgreSQL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will rename it according to the name of the function we decide to use finally.
@@ -404,6 +404,48 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateTimeMinutes] | |||
include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateMinutes] | |||
-------------------------------------------------- | |||
|
|||
[[sql-functions-datetime-dateformat]] | |||
==== `DATE_FORMAT/FORMAT/TO_CHAR/TOCHAR` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do all the function names exist in other DBs? I'd like one which is unique to us due to the Java time format - maybe datetime_format
(and datetime_parse
) - or vice-versa - format_datetime
and parse_datetime
.
My point is, I think going forward it would be good to have our own function which uses Java time and then support the rest of the DB functions as translating implementation for the given arguments to our class.
For example DATE_FORMAT
which is MySQL specific would exist as such but the MySQL arguments would then be converted into the java.time
pattern.
Otherwise looking like MySQL but not supporting its pattern is confusing.
Since the pattern support is tedious, I'm fine with doing that in a separate support for the future. However I'd like to clarify the approach in the docs.
MySQL DATE_FORMAT
: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format
SQL Server FORMAT
: https://docs.microsoft.com/en-us/sql/t-sql/functions/format-transact-sql?view=sql-server-ver15#ExampleD
Postgres TO_CHAR
: https://www.postgresql.org/docs/9.1/functions-formatting.html
Oracle TOCHAR
: https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34510
Note that outside MySQL, the rest of the functions accept multiple formats and data types so to avoid confusion, I would not create aliases for them yet.
But rather treat them separately since if we are to introduce such functions, we would essentially have to obey their semantics rather then ours.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to DATETIME_FORMAT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed now, but it seems sane to me, 👍.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nicely done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Left just couple of insignificant comments.
@@ -177,6 +178,7 @@ public SqlFunctionRegistry() { | |||
def(MonthName.class, MonthName::new, "MONTH_NAME", "MONTHNAME"), | |||
def(MonthOfYear.class, MonthOfYear::new, "MONTH_OF_YEAR", "MONTH"), | |||
def(SecondOfMinute.class, SecondOfMinute::new, "SECOND_OF_MINUTE", "SECOND"), | |||
def(DateTimeFormat.class, DateTimeFormat::new, "DATETIME_FORMAT"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason for not using the alphabetical order (as it is now in the registry)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, will fix, It was because of the original name ToChar
.
format( | ||
null, | ||
"first argument of [{}] must be one of {} or their aliases; found value [{}]", | ||
sourceText(), | ||
validDateTimeFieldValues(), | ||
Expressions.name(left()) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:-(
if (resolution.unresolved()) { | ||
return resolution; | ||
} | ||
resolution = isString(right(), sourceText(), Expressions.ParamOrdinal.SECOND); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isString
is enough here? (vs. isStringAndExact
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's enough, tried for example:
SELECT * FROM test WHERE DATETIME_FORMAT(field1, 'uuuu-MM-dd') = '2020-04-08';
SELECT DATETIME_FORMAT(field1, 'uuuu-MM-dd') FROM test;
on an index:
{
"mappings": {
"properties": {
"field1": {
"type": "date"
},
"field2": {
"type": "text"
}
}
}
}
and data:
{
"field1": "05/04/2020 10:20:30",
"field2": "uuuu-MM-dd"
}
and it works.
Implement to_char according to the PostgreSQL spec: https://www.postgresql.org/docs/9.1/functions-formatting.html by translating to the java.time patterns used in DATETIME_FORMAT. Follows: elastic#54832
Implement to_char according to the PostgreSQL spec: https://www.postgresql.org/docs/9.1/functions-formatting.html by translating to the java.time patterns used in DATETIME_FORMAT. Follows: elastic#54832
Implement DATETIME_FORMAT(<date/datetime/time>, ) function
which allows for formatting a timestamp to the specified format. The patterns
allowed as those of
java.time.format.DateTimeFormatter
.Related to #53714