SQL: non ISO 8601 versions of DAY_OF_WEEK and WEEK_OF_YEAR functions (#36358)

* Renamed DAY_OF_WEEK and WEEK_OF_YEAR functions to their ISO version and
added the same functions with different functionality.
* Rewritten the datetime functions documentation to follow the format of the other
functions documentation pages.
This commit is contained in:
Andrei Stefan 2018-12-12 02:29:02 +02:00 committed by GitHub
parent 51800de2a8
commit de373060fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1036 additions and 165 deletions

View File

@ -5,92 +5,397 @@
beta[] beta[]
* Extract the year from a date (`YEAR`) [[sql-functions-datetime-day]]
==== `DAY_OF_MONTH`/`DOM`/`DAY`
.Synopsis:
[source, sql]
--------------------------------------------------
DAY_OF_MONTH(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the day of the month from a date.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[year] include-tagged::{sql-specs}/docs.csv-spec[dayOfMonth]
-------------------------------------------------- --------------------------------------------------
* Extract the month of the year from a date (`MONTH_OF_YEAR` or `MONTH`) [[sql-functions-datetime-dow]]
==== `DAY_OF_WEEK`/`DAYOFWEEK`/`DOW`
.Synopsis:
[source, sql]
--------------------------------------------------
DAY_OF_WEEK(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the day of the week from a date. Sunday is `1`, Monday is `2`, etc.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[monthOfYear] include-tagged::{sql-specs}/docs.csv-spec[dayOfWeek]
-------------------------------------------------- --------------------------------------------------
* Extract the week of the year from a date (`WEEK_OF_YEAR` or `WEEK`) [[sql-functions-datetime-doy]]
==== `DAY_OF_YEAR`/`DOY`
.Synopsis:
[source, sql]
--------------------------------------------------
DAY_OF_YEAR(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the day of the year from a date.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[weekOfYear] include-tagged::{sql-specs}/docs.csv-spec[dayOfYear]
-------------------------------------------------- --------------------------------------------------
* Extract the day of the year from a date (`DAY_OF_YEAR` or `DOY`) [[sql-functions-datetime-dayname]]
==== `DAY_NAME`/`DAYNAME`
.Synopsis:
[source, sql]
--------------------------------------------------
DAY_NAME(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: string
.Description:
Extract the day of the week from a datetime in text format (`Monday`, `Tuesday`...).
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[dayOfYear] include-tagged::{sql-specs}/docs.csv-spec[dayName]
-------------------------------------------------- --------------------------------------------------
* Extract the day of the month from a date (`DAY_OF_MONTH`, `DOM`, or `DAY`) [[sql-functions-datetime-hour]]
==== `HOUR_OF_DAY`/`HOUR`
.Synopsis:
[source, sql]
--------------------------------------------------
HOUR_OF_DAY(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the hour of the day from a date.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[dayOfMonth] include-tagged::{sql-specs}/docs.csv-spec[hourOfDay]
-------------------------------------------------- --------------------------------------------------
* Extract the day of the week from a date (`DAY_OF_WEEK` or `DOW`). [[sql-functions-datetime-isodow]]
==== `ISO_DAY_OF_WEEK`/`ISODAYOFWEEK`/`ISODOW`/`IDOW`
.Synopsis:
[source, sql]
--------------------------------------------------
ISO_DAY_OF_WEEK(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the day of the week from a date, following the https://en.wikipedia.org/wiki/ISO_week_date[ISO 8601 standard].
Monday is `1`, Tuesday is `2`, etc. Monday is `1`, Tuesday is `2`, etc.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[dayOfWeek] include-tagged::{sql-specs}/docs.csv-spec[isoDayOfWeek]
-------------------------------------------------- --------------------------------------------------
* Extract the hour of the day from a date (`HOUR_OF_DAY` or `HOUR`). [[sql-functions-datetime-isoweek]]
Monday is `1`, Tuesday is `2`, etc. ==== `ISO_WEEK_OF_YEAR`/`ISOWEEKOFYEAR`/`ISOWEEK`/`IWOY`/`IW`
.Synopsis:
[source, sql]
--------------------------------------------------
ISO_WEEK_OF_YEAR(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the week of the year from a date, following https://en.wikipedia.org/wiki/ISO_week_date[ISO 8601 standard]. The first week
of a year is the first week with a majority (4 or more) of its days in January.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[hourOfDay] include-tagged::{sql-specs}/docs.csv-spec[isoWeekOfYear]
-------------------------------------------------- --------------------------------------------------
* Extract the minute of the day from a date (`MINUTE_OF_DAY`). [[sql-functions-datetime-minuteofday]]
==== `MINUTE_OF_DAY`
.Synopsis:
[source, sql]
--------------------------------------------------
MINUTE_OF_DAY(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the minute of the day from a date.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[minuteOfDay] include-tagged::{sql-specs}/docs.csv-spec[minuteOfDay]
-------------------------------------------------- --------------------------------------------------
* Extract the minute of the hour from a date (`MINUTE_OF_HOUR`, `MINUTE`). [[sql-functions-datetime-minute]]
==== `MINUTE_OF_HOUR`/`MINUTE`
.Synopsis:
[source, sql]
--------------------------------------------------
MINUTE_OF_HOUR(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the minute of the hour from a date.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[minuteOfHour] include-tagged::{sql-specs}/docs.csv-spec[minuteOfHour]
-------------------------------------------------- --------------------------------------------------
* Extract the second of the minute from a date (`SECOND_OF_MINUTE`, `SECOND`). [[sql-functions-datetime-month]]
==== `MONTH_OF_YEAR`/`MONTH`
.Synopsis:
[source, sql]
--------------------------------------------------
MONTH(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the month of the year from a date.
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[secondOfMinute] include-tagged::{sql-specs}/docs.csv-spec[monthOfYear]
-------------------------------------------------- --------------------------------------------------
* Extract [[sql-functions-datetime-monthname]]
==== `MONTH_NAME`/`MONTHNAME`
As an alternative, one can support `EXTRACT` to extract fields from datetimes. .Synopsis:
You can run any <<sql-functions-datetime,datetime function>> [source, sql]
with `EXTRACT(<datetime_function> FROM <expression>)`. So --------------------------------------------------
MONTH_NAME(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: string
.Description:
Extract the month from a datetime in text format (`January`, `February`...).
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[extractDayOfYear] include-tagged::{sql-specs}/docs.csv-spec[monthName]
--------------------------------------------------
[[sql-functions-datetime-second]]
==== `SECOND_OF_MINUTE`/`SECOND`
.Synopsis:
[source, sql]
--------------------------------------------------
SECOND_OF_MINUTE(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the second of the minute from a date.
["source","sql",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{sql-specs}/docs.csv-spec[secondOfMinute]
--------------------------------------------------
[[sql-functions-datetime-quarter]]
==== `QUARTER`
.Synopsis:
[source, sql]
--------------------------------------------------
QUARTER(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the year quarter the date falls in.
["source","sql",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{sql-specs}/docs.csv-spec[quarter]
--------------------------------------------------
[[sql-functions-datetime-week]]
==== `WEEK_OF_YEAR`/`WEEK`
.Synopsis:
[source, sql]
--------------------------------------------------
WEEK_OF_YEAR(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the week of the year from a date.
["source","sql",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{sql-specs}/docs.csv-spec[weekOfYear]
--------------------------------------------------
[[sql-functions-datetime-year]]
==== `YEAR`
.Synopsis:
[source, sql]
--------------------------------------------------
YEAR(date_exp<1>)
--------------------------------------------------
*Input*:
<1> date expression
*Output*: integer
.Description:
Extract the year from a date.
["source","sql",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{sql-specs}/docs.csv-spec[year]
--------------------------------------------------
[[sql-functions-datetime-extract]]
==== `EXTRACT`
.Synopsis:
[source, sql]
--------------------------------------------------
EXTRACT(datetime_function<1> FROM date_exp<2>)
--------------------------------------------------
*Input*:
<1> datetime function name
<2> date expression
*Output*: integer
.Description:
Extract fields from a datetime by specifying the name of a <<sql-functions-datetime,datetime function>>.
The following
["source","sql",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{sql-specs}/docs.csv-spec[extractDayOfYear]
-------------------------------------------------- --------------------------------------------------
is the equivalent to is the equivalent to
["source","sql",subs="attributes,callouts,macros"] ["source","sql",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[dayOfYear] include-tagged::{sql-specs}/docs.csv-spec[dayOfYear]
-------------------------------------------------- --------------------------------------------------

View File

@ -78,6 +78,8 @@ public abstract class ShowTestCase extends CliIntegrationTestCase {
assertThat(readLine(), RegexMatcher.matches("\\s*DAY_OF_WEEK\\s*\\|\\s*SCALAR\\s*")); assertThat(readLine(), RegexMatcher.matches("\\s*DAY_OF_WEEK\\s*\\|\\s*SCALAR\\s*"));
assertThat(readLine(), RegexMatcher.matches("\\s*DAY_OF_YEAR\\s*\\|\\s*SCALAR\\s*")); assertThat(readLine(), RegexMatcher.matches("\\s*DAY_OF_YEAR\\s*\\|\\s*SCALAR\\s*"));
assertThat(readLine(), RegexMatcher.matches("\\s*HOUR_OF_DAY\\s*\\|\\s*SCALAR\\s*")); assertThat(readLine(), RegexMatcher.matches("\\s*HOUR_OF_DAY\\s*\\|\\s*SCALAR\\s*"));
assertThat(readLine(), RegexMatcher.matches("\\s*ISODAYOFWEEK\\s*\\|\\s*SCALAR\\s*"));
assertThat(readLine(), RegexMatcher.matches("\\s*ISO_DAY_OF_WEEK\\s*\\|\\s*SCALAR\\s*"));
assertThat(readLine(), RegexMatcher.matches("\\s*MINUTE_OF_DAY\\s*\\|\\s*SCALAR\\s*")); assertThat(readLine(), RegexMatcher.matches("\\s*MINUTE_OF_DAY\\s*\\|\\s*SCALAR\\s*"));
assertEquals("", readLine()); assertEquals("", readLine());
} }

View File

@ -40,6 +40,15 @@ DOW |SCALAR
DOY |SCALAR DOY |SCALAR
HOUR |SCALAR HOUR |SCALAR
HOUR_OF_DAY |SCALAR HOUR_OF_DAY |SCALAR
IDOW |SCALAR
ISODAYOFWEEK |SCALAR
ISODOW |SCALAR
ISOWEEK |SCALAR
ISOWEEKOFYEAR |SCALAR
ISO_DAY_OF_WEEK |SCALAR
ISO_WEEK_OF_YEAR|SCALAR
IW |SCALAR
IWOY |SCALAR
MINUTE |SCALAR MINUTE |SCALAR
MINUTE_OF_DAY |SCALAR MINUTE_OF_DAY |SCALAR
MINUTE_OF_HOUR |SCALAR MINUTE_OF_HOUR |SCALAR
@ -156,6 +165,8 @@ DAY_OF_MONTH |SCALAR
DAY_OF_WEEK |SCALAR DAY_OF_WEEK |SCALAR
DAY_OF_YEAR |SCALAR DAY_OF_YEAR |SCALAR
HOUR_OF_DAY |SCALAR HOUR_OF_DAY |SCALAR
ISODAYOFWEEK |SCALAR
ISO_DAY_OF_WEEK|SCALAR
MINUTE_OF_DAY |SCALAR MINUTE_OF_DAY |SCALAR
; ;

View File

@ -54,10 +54,10 @@ d:i | l:s
; ;
// //
// Date (in H2 these start at 0 instead of 1...) // Date
// //
dateTimeDayOfWeek dateTimeIsoDayOfWeek
SELECT DAY_OF_WEEK(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY DAY_OF_WEEK(birth_date); SELECT ISO_DAY_OF_WEEK(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY ISO_DAY_OF_WEEK(birth_date);
d:i | l:s d:i | l:s
1 | Preusig 1 | Preusig
@ -86,10 +86,96 @@ d:i | l:s
110 | Peac 110 | Peac
; ;
weekOfYear
SELECT WEEK(birth_date) week, birth_date FROM test_emp ORDER BY WEEK(birth_date) DESC, birth_date DESC LIMIT 15;
week:i | birth_date:ts
---------------+------------------------
52 |1962-12-29T00:00:00.000Z
52 |1959-12-25T00:00:00.000Z
52 |1952-12-24T00:00:00.000Z
51 |1960-12-17T00:00:00.000Z
50 |1956-12-13T00:00:00.000Z
49 |1959-12-03T00:00:00.000Z
49 |1957-12-03T00:00:00.000Z
48 |1963-11-26T00:00:00.000Z
48 |1962-11-26T00:00:00.000Z
47 |1962-11-19T00:00:00.000Z
46 |1956-11-14T00:00:00.000Z
46 |1952-11-13T00:00:00.000Z
45 |1962-11-07T00:00:00.000Z
45 |1953-11-07T00:00:00.000Z
44 |1961-11-02T00:00:00.000Z
;
weekOfYearWithFilter
SELECT WEEK(birth_date) week, birth_date FROM test_emp WHERE WEEK(birth_date) > 50 OR WEEK(birth_date) < 4 ORDER BY WEEK(birth_date) DESC, birth_date DESC;
week:i | birth_date:ts
---------------+------------------------
52 |1962-12-29T00:00:00.000Z
52 |1959-12-25T00:00:00.000Z
52 |1952-12-24T00:00:00.000Z
51 |1960-12-17T00:00:00.000Z
2 |1965-01-03T00:00:00.000Z
2 |1953-01-07T00:00:00.000Z
;
// //
// Aggregate // Aggregate
// //
dateTimeAggByIsoDayOfWeekWithFilter
SELECT IDOW(birth_date) day, DAY_NAME(birth_date) name, COUNT(*) c FROM test_emp WHERE IDOW(birth_date) < 6 GROUP BY day, name ORDER BY day desc;
day:i | name:s | c:l
---------------+---------------+---------------
5 |Friday |12
4 |Thursday |15
3 |Wednesday |14
2 |Tuesday |18
1 |Monday |8
;
dateTimeAggByIsoDayOfWeek
SELECT IDOW(birth_date) day, DAY_NAME(birth_date) name, COUNT(*) c FROM test_emp GROUP BY day, name ORDER BY day desc;
day:i | name:s | c:l
---------------+---------------+---------------
7 |Sunday |10
6 |Saturday |13
5 |Friday |12
4 |Thursday |15
3 |Wednesday |14
2 |Tuesday |18
1 |Monday |8
null |null |10
;
dateTimeAggByIsoWeekOfYear
SELECT IW(birth_date) iso_week, WEEK(birth_date) week FROM test_emp WHERE IW(birth_date) < 20 GROUP BY iso_week, week ORDER BY iso_week;
iso_week:i | week:i
---------------+---------------
1 |2
3 |4
4 |4
4 |5
6 |7
7 |7
8 |8
8 |9
9 |9
10 |11
12 |12
14 |14
14 |15
15 |16
16 |16
16 |17
18 |18
;
dateTimeAggByYear dateTimeAggByYear
SELECT YEAR(birth_date) AS d, CAST(SUM(emp_no) AS INT) s FROM "test_emp" GROUP BY YEAR(birth_date) ORDER BY YEAR(birth_date) LIMIT 13; SELECT YEAR(birth_date) AS d, CAST(SUM(emp_no) AS INT) s FROM "test_emp" GROUP BY YEAR(birth_date) ORDER BY YEAR(birth_date) LIMIT 13;
@ -166,112 +252,22 @@ d:i | c:l | s:i
null |10 |100445 null |10 |100445
; ;
constantYear weekOfYearGroupBy
// tag::year SELECT WEEK(birth_date) week, COUNT(*) c FROM test_emp WHERE MOD(WEEK(birth_date), 4) = 0 GROUP BY week ORDER BY WEEK(birth_date);
SELECT YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS year;
year week:i | c:l
--------------- ---------------+---------------
2018 4 |3
// end::year 8 |2
; 12 |1
16 |3
constantMonthOfYear 20 |1
// tag::monthOfYear 24 |2
SELECT MONTH_OF_YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS month; 28 |3
32 |1
month 36 |3
--------------- 40 |4
2 44 |2
// end::monthOfYear 48 |2
; 52 |3
constantWeekOfYear
// tag::weekOfYear
SELECT WEEK_OF_YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS week;
week
---------------
8
// end::weekOfYear
;
constantDayOfYear
// tag::dayOfYear
SELECT DAY_OF_YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
50
// end::dayOfYear
;
extractDayOfYear
// tag::extractDayOfYear
SELECT EXTRACT(DAY_OF_YEAR FROM CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
50
// end::extractDayOfYear
;
constantDayOfMonth
// tag::dayOfMonth
SELECT DAY_OF_MONTH(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
19
// end::dayOfMonth
;
constantDayOfWeek
// tag::dayOfWeek
SELECT DAY_OF_WEEK(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
1
// end::dayOfWeek
;
constantHourOfDay
// tag::hourOfDay
SELECT HOUR_OF_DAY(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS hour;
hour
---------------
10
// end::hourOfDay
;
constantMinuteOfDay
// tag::minuteOfDay
SELECT MINUTE_OF_DAY(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS minute;
minute
---------------
623
// end::minuteOfDay
;
constantMinuteOfHour
// tag::minuteOfHour
SELECT MINUTE_OF_HOUR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS minute;
minute
---------------
23
// end::minuteOfHour
;
constantSecondOfMinute
// tag::secondOfMinute
SELECT SECOND_OF_MINUTE(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS second;
second
---------------
27
// end::secondOfMinute
; ;

View File

@ -6,6 +6,9 @@
// Time NOT IMPLEMENTED in H2 on TIMESTAMP WITH TIME ZONE - hence why these are moved to CSV // Time NOT IMPLEMENTED in H2 on TIMESTAMP WITH TIME ZONE - hence why these are moved to CSV
// //
// WEEK_OF_YEAR moved to CSV tests, because H2 builds its Calendar with the local Locale, we consider ROOT as the default Locale
// This has implications on the results, which could change given specific locales where the rules for determining the start of a year are different.
// //
// Date // Date
// //
@ -31,6 +34,9 @@ SELECT DAYNAME(CAST('2018-09-03' AS TIMESTAMP)) day FROM "test_emp" limit 1;
quarterSelect quarterSelect
SELECT QUARTER(hire_date) q, hire_date FROM test_emp ORDER BY hire_date LIMIT 15; SELECT QUARTER(hire_date) q, hire_date FROM test_emp ORDER BY hire_date LIMIT 15;
dayOfWeek
SELECT DAY_OF_WEEK(birth_date) day, birth_date FROM test_emp ORDER BY DAY_OF_WEEK(birth_date);
// //
// Filter // Filter
// //
@ -59,6 +65,9 @@ SELECT first_name, last_name FROM "test_emp" WHERE DAYNAME(hire_date) = 'Sunday'
quarterWithFilter quarterWithFilter
SELECT QUARTER(hire_date) quarter, hire_date FROM test_emp WHERE QUARTER(hire_date) > 2 ORDER BY hire_date LIMIT 15; SELECT QUARTER(hire_date) quarter, hire_date FROM test_emp WHERE QUARTER(hire_date) > 2 ORDER BY hire_date LIMIT 15;
dayOfWeekWithFilter
SELECT DAY_OF_WEEK(birth_date) day, birth_date FROM test_emp WHERE DAY_OF_WEEK(birth_date) IN (6,7) ORDER BY DAY_OF_WEEK(birth_date);
// //
// Aggregate // Aggregate
// //
@ -101,4 +110,7 @@ dayNameWithGroupByOrderByAndHaving
SELECT CAST(MAX(salary) AS DOUBLE) max_salary, DAYNAME(hire_date) day_name FROM test_emp GROUP BY DAYNAME(hire_date) HAVING MAX(salary) > 50000 ORDER BY DAYNAME("hire_date"); SELECT CAST(MAX(salary) AS DOUBLE) max_salary, DAYNAME(hire_date) day_name FROM test_emp GROUP BY DAYNAME(hire_date) HAVING MAX(salary) > 50000 ORDER BY DAYNAME("hire_date");
quarterWithGroupByAndOrderBy quarterWithGroupByAndOrderBy
SELECT QUARTER(hire_date) quarter, COUNT(*) hires FROM test_emp GROUP BY QUARTER(hire_date) ORDER BY QUARTER(hire_date); SELECT QUARTER(hire_date) quarter, COUNT(*) hires FROM test_emp GROUP BY QUARTER(hire_date) ORDER BY QUARTER(hire_date);
dayOfWeekGroupBy
SELECT DAY_OF_WEEK(birth_date) day, COUNT(*) c FROM test_emp WHERE DAY_OF_WEEK(birth_date) < 6 GROUP BY day ORDER BY DAY_OF_WEEK(birth_date);

View File

@ -217,6 +217,15 @@ DOW |SCALAR
DOY |SCALAR DOY |SCALAR
HOUR |SCALAR HOUR |SCALAR
HOUR_OF_DAY |SCALAR HOUR_OF_DAY |SCALAR
IDOW |SCALAR
ISODAYOFWEEK |SCALAR
ISODOW |SCALAR
ISOWEEK |SCALAR
ISOWEEKOFYEAR |SCALAR
ISO_DAY_OF_WEEK |SCALAR
ISO_WEEK_OF_YEAR|SCALAR
IW |SCALAR
IWOY |SCALAR
MINUTE |SCALAR MINUTE |SCALAR
MINUTE_OF_DAY |SCALAR MINUTE_OF_DAY |SCALAR
MINUTE_OF_HOUR |SCALAR MINUTE_OF_HOUR |SCALAR
@ -345,6 +354,8 @@ DAY_OF_MONTH |SCALAR
DAY_OF_WEEK |SCALAR DAY_OF_WEEK |SCALAR
DAY_OF_YEAR |SCALAR DAY_OF_YEAR |SCALAR
HOUR_OF_DAY |SCALAR HOUR_OF_DAY |SCALAR
ISODAYOFWEEK |SCALAR
ISO_DAY_OF_WEEK|SCALAR
MINUTE_OF_DAY |SCALAR MINUTE_OF_DAY |SCALAR
// end::showFunctionsWithPattern // end::showFunctionsWithPattern
@ -1518,6 +1529,11 @@ SELECT TRUNCATE(-345.153, 1) AS trimmed;
// end::mathTruncateWithPositiveParameter // end::mathTruncateWithPositiveParameter
; ;
///////////////////////////////
//
// Null handling
//
///////////////////////////////
coalesceReturnNonNull coalesceReturnNonNull
// tag::coalesceReturnNonNull // tag::coalesceReturnNonNull
@ -1686,6 +1702,12 @@ true
// end::nullEqualsCompareTwoNulls // end::nullEqualsCompareTwoNulls
; ;
///////////////////////////////
//
// System functions
//
///////////////////////////////
// ignored because tests run with a docs-not-worthy cluster name // ignored because tests run with a docs-not-worthy cluster name
// at the time of this test being ignored, the cluster name was x-pack_plugin_sql_qa_single-node_integTestCluster // at the time of this test being ignored, the cluster name was x-pack_plugin_sql_qa_single-node_integTestCluster
database-Ignore database-Ignore
@ -1709,3 +1731,172 @@ SELECT USER();
elastic elastic
// end::user // end::user
; ;
///////////////////////////////
//
// Date-Time functions
//
///////////////////////////////
constantYear
// tag::year
SELECT YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS year;
year
---------------
2018
// end::year
;
constantMonthOfYear
// tag::monthOfYear
SELECT MONTH_OF_YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS month;
month
---------------
2
// end::monthOfYear
;
constantIsoWeekOfYear
// tag::isoWeekOfYear
SELECT ISO_WEEK_OF_YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS week;
week
---------------
8
// end::isoWeekOfYear
;
// Ignored because of https://github.com/elastic/elasticsearch/issues/33796
constantDayName-Ignore
// tag::dayName
SELECT DAY_NAME(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
Monday
// end::dayName
;
// Ignored because of https://github.com/elastic/elasticsearch/issues/33796
constantMonthName-Ignore
// tag::monthName
SELECT MONTH_NAME(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS month;
month
---------------
February
// end::monthName
;
constantDayOfYear
// tag::dayOfYear
SELECT DAY_OF_YEAR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
50
// end::dayOfYear
;
extractDayOfYear
// tag::extractDayOfYear
SELECT EXTRACT(DAY_OF_YEAR FROM CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
50
// end::extractDayOfYear
;
constantDayOfMonth
// tag::dayOfMonth
SELECT DAY_OF_MONTH(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
19
// end::dayOfMonth
;
constantDayOfWeek
// tag::dayOfWeek
SELECT DAY_OF_WEEK(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
2
// end::dayOfWeek
;
constantIsoDayOfWeek
// tag::isoDayOfWeek
SELECT ISO_DAY_OF_WEEK(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
day
---------------
1
// end::isoDayOfWeek
;
constantHourOfDay
// tag::hourOfDay
SELECT HOUR_OF_DAY(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS hour;
hour
---------------
10
// end::hourOfDay
;
constantMinuteOfDay
// tag::minuteOfDay
SELECT MINUTE_OF_DAY(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS minute;
minute
---------------
623
// end::minuteOfDay
;
constantMinuteOfHour
// tag::minuteOfHour
SELECT MINUTE_OF_HOUR(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS minute;
minute
---------------
23
// end::minuteOfHour
;
constantSecondOfMinute
// tag::secondOfMinute
SELECT SECOND_OF_MINUTE(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS second;
second
---------------
27
// end::secondOfMinute
;
constantQuarter
// tag::quarter
SELECT QUARTER(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS quarter;
quarter
---------------
1
// end::quarter
;
constantWeekOfYear
// tag::weekOfYear
SELECT WEEK(CAST('1988-01-05T09:22:10Z' AS TIMESTAMP)) AS week, ISOWEEK(CAST('1988-01-05T09:22:10Z' AS TIMESTAMP)) AS isoweek;
week | isoweek
---------------+---------------
2 |1
// end::weekOfYear
;

View File

@ -28,6 +28,8 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfMont
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfWeek; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfWeek;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfYear; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.HourOfDay; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.HourOfDay;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.IsoDayOfWeek;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.IsoWeekOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MinuteOfDay; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MinuteOfDay;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MinuteOfHour; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MinuteOfHour;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MonthName; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MonthName;
@ -151,7 +153,7 @@ public class FunctionRegistry {
def(Skewness.class, Skewness::new, "SKEWNESS"), def(Skewness.class, Skewness::new, "SKEWNESS"),
def(Kurtosis.class, Kurtosis::new, "KURTOSIS")); def(Kurtosis.class, Kurtosis::new, "KURTOSIS"));
// Scalar functions // Scalar functions
// conditional // Conditional
addToMap(def(Coalesce.class, Coalesce::new, "COALESCE"), addToMap(def(Coalesce.class, Coalesce::new, "COALESCE"),
def(IfNull.class, IfNull::new, "IFNULL", "ISNULL", "NVL"), def(IfNull.class, IfNull::new, "IFNULL", "ISNULL", "NVL"),
def(NullIf.class, NullIf::new, "NULLIF"), def(NullIf.class, NullIf::new, "NULLIF"),
@ -163,6 +165,8 @@ public class FunctionRegistry {
def(DayOfWeek.class, DayOfWeek::new, "DAY_OF_WEEK", "DAYOFWEEK", "DOW"), def(DayOfWeek.class, DayOfWeek::new, "DAY_OF_WEEK", "DAYOFWEEK", "DOW"),
def(DayOfYear.class, DayOfYear::new, "DAY_OF_YEAR", "DAYOFYEAR", "DOY"), def(DayOfYear.class, DayOfYear::new, "DAY_OF_YEAR", "DAYOFYEAR", "DOY"),
def(HourOfDay.class, HourOfDay::new, "HOUR_OF_DAY", "HOUR"), def(HourOfDay.class, HourOfDay::new, "HOUR_OF_DAY", "HOUR"),
def(IsoDayOfWeek.class, IsoDayOfWeek::new, "ISO_DAY_OF_WEEK", "ISODAYOFWEEK", "ISODOW", "IDOW"),
def(IsoWeekOfYear.class, IsoWeekOfYear::new, "ISO_WEEK_OF_YEAR", "ISOWEEKOFYEAR", "ISOWEEK", "IWOY", "IW"),
def(MinuteOfDay.class, MinuteOfDay::new, "MINUTE_OF_DAY"), def(MinuteOfDay.class, MinuteOfDay::new, "MINUTE_OF_DAY"),
def(MinuteOfHour.class, MinuteOfHour::new, "MINUTE_OF_HOUR", "MINUTE"), def(MinuteOfHour.class, MinuteOfHour::new, "MINUTE_OF_HOUR", "MINUTE"),
def(MonthName.class, MonthName::new, "MONTH_NAME", "MONTHNAME"), def(MonthName.class, MonthName::new, "MONTH_NAME", "MONTHNAME"),

View File

@ -9,6 +9,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NonIsoDateTimeProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor;
@ -78,6 +79,7 @@ public final class Processors {
// datetime // datetime
entries.add(new Entry(Processor.class, DateTimeProcessor.NAME, DateTimeProcessor::new)); entries.add(new Entry(Processor.class, DateTimeProcessor.NAME, DateTimeProcessor::new));
entries.add(new Entry(Processor.class, NamedDateTimeProcessor.NAME, NamedDateTimeProcessor::new)); entries.add(new Entry(Processor.class, NamedDateTimeProcessor.NAME, NamedDateTimeProcessor::new));
entries.add(new Entry(Processor.class, NonIsoDateTimeProcessor.NAME, NonIsoDateTimeProcessor::new));
entries.add(new Entry(Processor.class, QuarterProcessor.NAME, QuarterProcessor::new)); entries.add(new Entry(Processor.class, QuarterProcessor.NAME, QuarterProcessor::new));
// math // math
entries.add(new Entry(Processor.class, MathProcessor.NAME, MathProcessor::new)); entries.add(new Entry(Processor.class, MathProcessor.NAME, MathProcessor::new));

View File

@ -18,14 +18,14 @@ public class DateTimeProcessor extends BaseDateTimeProcessor {
public enum DateTimeExtractor { public enum DateTimeExtractor {
DAY_OF_MONTH(ChronoField.DAY_OF_MONTH), DAY_OF_MONTH(ChronoField.DAY_OF_MONTH),
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK), ISO_DAY_OF_WEEK(ChronoField.DAY_OF_WEEK),
DAY_OF_YEAR(ChronoField.DAY_OF_YEAR), DAY_OF_YEAR(ChronoField.DAY_OF_YEAR),
HOUR_OF_DAY(ChronoField.HOUR_OF_DAY), HOUR_OF_DAY(ChronoField.HOUR_OF_DAY),
MINUTE_OF_DAY(ChronoField.MINUTE_OF_DAY), MINUTE_OF_DAY(ChronoField.MINUTE_OF_DAY),
MINUTE_OF_HOUR(ChronoField.MINUTE_OF_HOUR), MINUTE_OF_HOUR(ChronoField.MINUTE_OF_HOUR),
MONTH_OF_YEAR(ChronoField.MONTH_OF_YEAR), MONTH_OF_YEAR(ChronoField.MONTH_OF_YEAR),
SECOND_OF_MINUTE(ChronoField.SECOND_OF_MINUTE), SECOND_OF_MINUTE(ChronoField.SECOND_OF_MINUTE),
WEEK_OF_YEAR(ChronoField.ALIGNED_WEEK_OF_YEAR), ISO_WEEK_OF_YEAR(ChronoField.ALIGNED_WEEK_OF_YEAR),
YEAR(ChronoField.YEAR); YEAR(ChronoField.YEAR);
private final ChronoField field; private final ChronoField field;

View File

@ -6,18 +6,19 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NonIsoDateTimeProcessor.NonIsoDateTimeExtractor;
import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
* Extract the day of the week from a datetime. 1 is Monday, 2 is Tuesday, etc. * Extract the day of the week from a datetime in non-ISO format. 1 is Sunday, 2 is Monday, etc.
*/ */
public class DayOfWeek extends DateTimeFunction { public class DayOfWeek extends NonIsoDateTimeFunction {
public DayOfWeek(Location location, Expression field, TimeZone timeZone) { public DayOfWeek(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone, DateTimeExtractor.DAY_OF_WEEK); super(location, field, timeZone, NonIsoDateTimeExtractor.DAY_OF_WEEK);
} }
@Override @Override
@ -29,9 +30,4 @@ public class DayOfWeek extends DateTimeFunction {
protected DayOfWeek replaceChild(Expression newChild) { protected DayOfWeek replaceChild(Expression newChild) {
return new DayOfWeek(location(), newChild, timeZone()); return new DayOfWeek(location(), newChild, timeZone());
} }
}
@Override
public String dateTimeFormat() {
return "e";
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
import java.util.TimeZone;
/**
* Extract the day of the week (following the ISO standard) from a datetime. 1 is Monday, 2 is Tuesday, etc.
*/
public class IsoDayOfWeek extends DateTimeFunction {
public IsoDayOfWeek(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone, DateTimeExtractor.ISO_DAY_OF_WEEK);
}
@Override
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
return IsoDayOfWeek::new;
}
@Override
protected IsoDayOfWeek replaceChild(Expression newChild) {
return new IsoDayOfWeek(location(), newChild, timeZone());
}
@Override
public String dateTimeFormat() {
return "e";
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
import java.util.TimeZone;
/**
* Extract the week of the year from a datetime following the ISO standard.
*/
public class IsoWeekOfYear extends DateTimeFunction {
public IsoWeekOfYear(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone, DateTimeExtractor.ISO_WEEK_OF_YEAR);
}
@Override
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
return IsoWeekOfYear::new;
}
@Override
protected IsoWeekOfYear replaceChild(Expression newChild) {
return new IsoWeekOfYear(location(), newChild, timeZone());
}
@Override
public String dateTimeFormat() {
return "w";
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NonIsoDateTimeProcessor.NonIsoDateTimeExtractor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.time.ZonedDateTime;
import java.util.Locale;
import java.util.TimeZone;
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
/*
* Base class for date/time functions that behave differently in a non-ISO format
*/
abstract class NonIsoDateTimeFunction extends BaseDateTimeFunction {
private final NonIsoDateTimeExtractor extractor;
NonIsoDateTimeFunction(Location location, Expression field, TimeZone timeZone, NonIsoDateTimeExtractor extractor) {
super(location, field, timeZone);
this.extractor = extractor;
}
@Override
protected Object doFold(ZonedDateTime dateTime) {
return extractor.extract(dateTime);
}
@Override
public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate(
formatTemplate(format(Locale.ROOT, "{sql}.%s(doc[{}].value, {})",
StringUtils.underscoreToLowerCamelCase(extractor.name()))),
paramsBuilder()
.variable(field.name())
.variable(timeZone().getID()).build(),
dataType());
}
@Override
protected Processor makeProcessor() {
return new NonIsoDateTimeProcessor(extractor, timeZone());
}
@Override
public DataType dataType() {
return DataType.INTEGER;
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.util.Calendar;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import java.util.function.Function;
public class NonIsoDateTimeProcessor extends BaseDateTimeProcessor {
public enum NonIsoDateTimeExtractor {
DAY_OF_WEEK(zdt -> {
// by ISO 8601 standard, Monday is the first day of the week and has the value 1
// non-ISO 8601 standard considers Sunday as the first day of the week and value 1
int dayOfWeek = zdt.get(ChronoField.DAY_OF_WEEK) + 1;
return dayOfWeek == 8 ? 1 : dayOfWeek;
}),
WEEK_OF_YEAR(zdt -> {
// by ISO 8601 standard, the first week of a year is the first week with a majority (4 or more) of its days in January.
// Other Locales may have their own standards (see Arabic or Japanese calendars).
LocalDateTime ld = zdt.toLocalDateTime();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(zdt.getZone()), Locale.ROOT);
cal.clear();
cal.set(ld.get(ChronoField.YEAR), ld.get(ChronoField.MONTH_OF_YEAR) - 1, ld.get(ChronoField.DAY_OF_MONTH),
ld.get(ChronoField.HOUR_OF_DAY), ld.get(ChronoField.MINUTE_OF_HOUR), ld.get(ChronoField.SECOND_OF_MINUTE));
return cal.get(Calendar.WEEK_OF_YEAR);
});
private final Function<ZonedDateTime, Integer> apply;
NonIsoDateTimeExtractor(Function<ZonedDateTime, Integer> apply) {
this.apply = apply;
}
public final Integer extract(ZonedDateTime dateTime) {
return apply.apply(dateTime);
}
public final Integer extract(ZonedDateTime millis, String tzId) {
return apply.apply(millis.withZoneSameInstant(ZoneId.of(tzId)));
}
}
public static final String NAME = "nidt";
private final NonIsoDateTimeExtractor extractor;
public NonIsoDateTimeProcessor(NonIsoDateTimeExtractor extractor, TimeZone timeZone) {
super(timeZone);
this.extractor = extractor;
}
public NonIsoDateTimeProcessor(StreamInput in) throws IOException {
super(in);
extractor = in.readEnum(NonIsoDateTimeExtractor.class);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeEnum(extractor);
}
@Override
public String getWriteableName() {
return NAME;
}
NonIsoDateTimeExtractor extractor() {
return extractor;
}
@Override
public Object doProcess(ZonedDateTime dateTime) {
return extractor.extract(dateTime);
}
@Override
public int hashCode() {
return Objects.hash(extractor, timeZone());
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
NonIsoDateTimeProcessor other = (NonIsoDateTimeProcessor) obj;
return Objects.equals(extractor, other.extractor)
&& Objects.equals(timeZone(), other.timeZone());
}
@Override
public String toString() {
return extractor.toString();
}
}

View File

@ -6,18 +6,19 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NonIsoDateTimeProcessor.NonIsoDateTimeExtractor;
import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
* Extract the week of the year from a datetime. * Extract the week of the year from a datetime following the non-ISO standard.
*/ */
public class WeekOfYear extends DateTimeFunction { public class WeekOfYear extends NonIsoDateTimeFunction {
public WeekOfYear(Location location, Expression field, TimeZone timeZone) { public WeekOfYear(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone, DateTimeExtractor.WEEK_OF_YEAR); super(location, field, timeZone, NonIsoDateTimeExtractor.WEEK_OF_YEAR);
} }
@Override @Override
@ -29,9 +30,4 @@ public class WeekOfYear extends DateTimeFunction {
protected WeekOfYear replaceChild(Expression newChild) { protected WeekOfYear replaceChild(Expression newChild) {
return new WeekOfYear(location(), newChild, timeZone()); return new WeekOfYear(location(), newChild, timeZone());
} }
}
@Override
public String dateTimeFormat() {
return "w";
}
}

View File

@ -10,6 +10,7 @@ import org.elasticsearch.script.JodaCompatibleZonedDateTime;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NonIsoDateTimeProcessor.NonIsoDateTimeExtractor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation; import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
@ -314,6 +315,13 @@ public final class InternalSqlScriptUtils {
return NameExtractor.DAY_NAME.extract(asDateTime(dateTime), tzId); return NameExtractor.DAY_NAME.extract(asDateTime(dateTime), tzId);
} }
public static Integer dayOfWeek(Object dateTime, String tzId) {
if (dateTime == null || tzId == null) {
return null;
}
return NonIsoDateTimeExtractor.DAY_OF_WEEK.extract(asDateTime(dateTime), tzId);
}
public static String monthName(Object dateTime, String tzId) { public static String monthName(Object dateTime, String tzId) {
if (dateTime == null || tzId == null) { if (dateTime == null || tzId == null) {
return null; return null;
@ -327,6 +335,13 @@ public final class InternalSqlScriptUtils {
} }
return QuarterProcessor.quarter(asDateTime(dateTime), tzId); return QuarterProcessor.quarter(asDateTime(dateTime), tzId);
} }
public static Integer weekOfYear(Object dateTime, String tzId) {
if (dateTime == null || tzId == null) {
return null;
}
return NonIsoDateTimeExtractor.WEEK_OF_YEAR.extract(asDateTime(dateTime), tzId);
}
public static ZonedDateTime asDateTime(Object dateTime) { public static ZonedDateTime asDateTime(Object dateTime) {
if (dateTime instanceof JodaCompatibleZonedDateTime) { if (dateTime instanceof JodaCompatibleZonedDateTime) {

View File

@ -98,8 +98,10 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS
# #
Integer dateTimeChrono(Object, String, String) Integer dateTimeChrono(Object, String, String)
String dayName(Object, String) String dayName(Object, String)
Integer dayOfWeek(Object, String)
String monthName(Object, String) String monthName(Object, String)
Integer quarter(Object, String) Integer quarter(Object, String)
Integer weekOfYear(Object, String)
IntervalDayTime intervalDayTime(String, String) IntervalDayTime intervalDayTime(String, String)
IntervalYearMonth intervalYearMonth(String, String) IntervalYearMonth intervalYearMonth(String, String)
ZonedDateTime asDateTime(Object) ZonedDateTime asDateTime(Object)

View File

@ -163,7 +163,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
} }
public void testMissingExtractSimilarMany() { public void testMissingExtractSimilarMany() {
assertEquals("1:8: Unknown datetime field [DOP], did you mean any of [DOM, DOW, DOY]?", assertEquals("1:8: Unknown datetime field [DOP], did you mean any of [DOM, DOW, DOY, IDOW]?",
error("SELECT EXTRACT(DOP FROM date) FROM test")); error("SELECT EXTRACT(DOP FROM date) FROM test"));
} }

View File

@ -0,0 +1,92 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NonIsoDateTimeProcessor.NonIsoDateTimeExtractor;
import java.io.IOException;
import java.util.TimeZone;
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime;
public class NonIsoDateTimeProcessorTests extends AbstractWireSerializingTestCase<NonIsoDateTimeProcessor> {
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
public static NonIsoDateTimeProcessor randomNonISODateTimeProcessor() {
return new NonIsoDateTimeProcessor(randomFrom(NonIsoDateTimeExtractor.values()), UTC);
}
@Override
protected NonIsoDateTimeProcessor createTestInstance() {
return randomNonISODateTimeProcessor();
}
@Override
protected Reader<NonIsoDateTimeProcessor> instanceReader() {
return NonIsoDateTimeProcessor::new;
}
@Override
protected NonIsoDateTimeProcessor mutateInstance(NonIsoDateTimeProcessor instance) throws IOException {
NonIsoDateTimeExtractor replaced = randomValueOtherThan(instance.extractor(), () -> randomFrom(NonIsoDateTimeExtractor.values()));
return new NonIsoDateTimeProcessor(replaced, UTC);
}
public void testNonISOWeekOfYearInUTC() {
NonIsoDateTimeProcessor proc = new NonIsoDateTimeProcessor(NonIsoDateTimeExtractor.WEEK_OF_YEAR, UTC);
assertEquals(2, proc.process(dateTime(568372930000L))); //1988-01-05T09:22:10Z[UTC]
assertEquals(6, proc.process(dateTime(981278530000L))); //2001-02-04T09:22:10Z[UTC]
assertEquals(7, proc.process(dateTime(224241730000L))); //1977-02-08T09:22:10Z[UTC]
assertEquals(12, proc.process(dateTime(132744130000L))); //1974-03-17T09:22:10Z[UTC]
assertEquals(17, proc.process(dateTime(230376130000L))); //1977-04-20T09:22:10Z[UTC]
assertEquals(17, proc.process(dateTime(766833730000L))); //1994-04-20T09:22:10Z[UTC]
assertEquals(29, proc.process(dateTime(79780930000L))); //1972-07-12T09:22:10Z[UTC]
assertEquals(33, proc.process(dateTime(902913730000L))); //1998-08-12T09:22:10Z[UTC]
}
public void testNonISOWeekOfYearInNonUTCTimeZone() {
NonIsoDateTimeProcessor proc = new NonIsoDateTimeProcessor(NonIsoDateTimeExtractor.WEEK_OF_YEAR, TimeZone.getTimeZone("GMT-10:00"));
assertEquals(2, proc.process(dateTime(568372930000L)));
assertEquals(5, proc.process(dateTime(981278530000L)));
assertEquals(7, proc.process(dateTime(224241730000L)));
assertEquals(11, proc.process(dateTime(132744130000L)));
assertEquals(17, proc.process(dateTime(230376130000L)));
assertEquals(17, proc.process(dateTime(766833730000L)));
assertEquals(29, proc.process(dateTime(79780930000L)));
assertEquals(33, proc.process(dateTime(902913730000L)));
}
public void testNonISODayOfWeekInUTC() {
NonIsoDateTimeProcessor proc = new NonIsoDateTimeProcessor(NonIsoDateTimeExtractor.DAY_OF_WEEK, UTC);
assertEquals(3, proc.process(dateTime(568372930000L))); //1988-01-05T09:22:10Z[UTC]
assertEquals(1, proc.process(dateTime(981278530000L))); //2001-02-04T09:22:10Z[UTC]
assertEquals(3, proc.process(dateTime(224241730000L))); //1977-02-08T09:22:10Z[UTC]
assertEquals(1, proc.process(dateTime(132744130000L))); //1974-03-17T09:22:10Z[UTC]
assertEquals(4, proc.process(dateTime(230376130000L))); //1977-04-20T09:22:10Z[UTC]
assertEquals(4, proc.process(dateTime(766833730000L))); //1994-04-20T09:22:10Z[UTC]
assertEquals(7, proc.process(dateTime(333451330000L))); //1980-07-26T09:22:10Z[UTC]
assertEquals(6, proc.process(dateTime(874660930000L))); //1997-09-19T09:22:10Z[UTC]
}
public void testNonISODayOfWeekInNonUTCTimeZone() {
NonIsoDateTimeProcessor proc = new NonIsoDateTimeProcessor(NonIsoDateTimeExtractor.DAY_OF_WEEK, TimeZone.getTimeZone("GMT-10:00"));
assertEquals(2, proc.process(dateTime(568372930000L)));
assertEquals(7, proc.process(dateTime(981278530000L)));
assertEquals(2, proc.process(dateTime(224241730000L)));
assertEquals(7, proc.process(dateTime(132744130000L)));
assertEquals(3, proc.process(dateTime(230376130000L)));
assertEquals(3, proc.process(dateTime(766833730000L)));
assertEquals(6, proc.process(dateTime(333451330000L)));
assertEquals(5, proc.process(dateTime(874660930000L)));
}
}

View File

@ -23,7 +23,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayName;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfMonth; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfMonth;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfYear; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MonthOfYear; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MonthOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.WeekOfYear; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.IsoWeekOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.Year; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.Year;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.ACos; import org.elasticsearch.xpack.sql.expression.function.scalar.math.ACos;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.ASin; import org.elasticsearch.xpack.sql.expression.function.scalar.math.ASin;
@ -333,9 +333,9 @@ public class OptimizerTests extends ESTestCase {
assertEquals(1, foldFunction(new MonthOfYear(EMPTY, cast, UTC))); assertEquals(1, foldFunction(new MonthOfYear(EMPTY, cast, UTC)));
assertEquals(19, foldFunction(new DayOfMonth(EMPTY, cast, UTC))); assertEquals(19, foldFunction(new DayOfMonth(EMPTY, cast, UTC)));
assertEquals(19, foldFunction(new DayOfYear(EMPTY, cast, UTC))); assertEquals(19, foldFunction(new DayOfYear(EMPTY, cast, UTC)));
assertEquals(3, foldFunction(new WeekOfYear(EMPTY, cast, UTC))); assertEquals(3, foldFunction(new IsoWeekOfYear(EMPTY, cast, UTC)));
assertNull(foldFunction( assertNull(foldFunction(
new WeekOfYear(EMPTY, new Literal(EMPTY, null, DataType.NULL), UTC))); new IsoWeekOfYear(EMPTY, new Literal(EMPTY, null, DataType.NULL), UTC)));
} }
public void testConstantFoldingIn() { public void testConstantFoldingIn() {