Skip to content

Commit 89dba98

Browse files
Add Minute_Of_Hour Function As An Alias Of Minute Function (#196)
Added Testing And Implementation For Minute_Of_Hour Function Signed-off-by: GabeFernandez310 <[email protected]>
1 parent 1b58f7d commit 89dba98

File tree

8 files changed

+160
-11
lines changed

8 files changed

+160
-11
lines changed

core/src/main/java/org/opensearch/sql/expression/DSL.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ public static FunctionExpression minute_of_day(Expression... expressions) {
358358
return compile(FunctionProperties.None, BuiltinFunctionName.MINUTE_OF_DAY, expressions);
359359
}
360360

361+
public static FunctionExpression minute_of_hour(Expression... expressions) {
362+
return compile(FunctionProperties.None, BuiltinFunctionName.MINUTE_OF_HOUR, expressions);
363+
}
364+
361365
public static FunctionExpression month(Expression... expressions) {
362366
return compile(FunctionProperties.None, BuiltinFunctionName.MONTH, expressions);
363367
}

core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,9 @@ public void register(BuiltinFunctionRepository repository) {
114114
repository.register(makedate());
115115
repository.register(maketime());
116116
repository.register(microsecond());
117-
repository.register(minute());
117+
repository.register(minute(BuiltinFunctionName.MINUTE));
118118
repository.register(minute_of_day());
119+
repository.register(minute(BuiltinFunctionName.MINUTE_OF_HOUR));
119120
repository.register(month(BuiltinFunctionName.MONTH));
120121
repository.register(month(BuiltinFunctionName.MONTH_OF_YEAR));
121122
repository.register(monthName());
@@ -429,11 +430,12 @@ private DefaultFunctionResolver microsecond() {
429430
/**
430431
* MINUTE(STRING/TIME/DATETIME/TIMESTAMP). return the minute value for time.
431432
*/
432-
private DefaultFunctionResolver minute() {
433-
return define(BuiltinFunctionName.MINUTE.getName(),
433+
private DefaultFunctionResolver minute(BuiltinFunctionName name) {
434+
return define(name.getName(),
434435
impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, STRING),
435436
impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, TIME),
436437
impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, DATETIME),
438+
impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, DATE),
437439
impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, TIMESTAMP)
438440
);
439441
}
@@ -945,7 +947,8 @@ private ExprValue exprMicrosecond(ExprValue time) {
945947
* @return ExprValue.
946948
*/
947949
private ExprValue exprMinute(ExprValue time) {
948-
return new ExprIntegerValue(time.timeValue().getMinute());
950+
return new ExprIntegerValue(
951+
(MINUTES.between(LocalTime.MIN, time.timeValue()) % 60));
949952
}
950953

951954
/**

core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public enum BuiltinFunctionName {
7878
MICROSECOND(FunctionName.of("microsecond")),
7979
MINUTE(FunctionName.of("minute")),
8080
MINUTE_OF_DAY(FunctionName.of("minute_of_day")),
81+
MINUTE_OF_HOUR(FunctionName.of("minute_of_hour")),
8182
MONTH(FunctionName.of("month")),
8283
MONTH_OF_YEAR(FunctionName.of("month_of_year")),
8384
MONTHNAME(FunctionName.of("monthname")),

core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeFunctionTest.java

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@
2828
import com.google.common.collect.ImmutableList;
2929
import java.time.LocalDate;
3030
import java.util.List;
31+
import java.util.stream.Stream;
3132
import lombok.AllArgsConstructor;
3233
import org.junit.jupiter.api.BeforeEach;
3334
import org.junit.jupiter.api.Test;
3435
import org.junit.jupiter.api.extension.ExtendWith;
36+
import org.junit.jupiter.params.ParameterizedTest;
37+
import org.junit.jupiter.params.provider.Arguments;
38+
import org.junit.jupiter.params.provider.MethodSource;
3539
import org.mockito.Mock;
3640
import org.mockito.junit.jupiter.MockitoExtension;
3741
import org.opensearch.sql.data.model.ExprDateValue;
@@ -46,6 +50,7 @@
4650
import org.opensearch.sql.expression.Expression;
4751
import org.opensearch.sql.expression.ExpressionTestBase;
4852
import org.opensearch.sql.expression.FunctionExpression;
53+
import org.opensearch.sql.expression.LiteralExpression;
4954
import org.opensearch.sql.expression.env.Environment;
5055

5156
@ExtendWith(MockitoExtension.class)
@@ -727,7 +732,7 @@ private void testMinuteOfDay(String date, int value) {
727732
assertEquals(INTEGER, expression.type());
728733
assertEquals(integerValue(value), eval(expression));
729734
}
730-
735+
731736
@Test
732737
public void minuteOfDay() {
733738
when(nullRef.type()).thenReturn(TIME);
@@ -764,6 +769,85 @@ public void minuteOfDay() {
764769
testMinuteOfDay("2020-08-17 00:00:01", 0);
765770
}
766771

772+
private void minuteOfHourQuery(FunctionExpression dateExpression, int minute, String testExpr) {
773+
assertAll(
774+
() -> assertEquals(INTEGER, dateExpression.type()),
775+
() -> assertEquals(integerValue(minute), eval(dateExpression)),
776+
() -> assertEquals(testExpr, dateExpression.toString())
777+
);
778+
}
779+
780+
private static Stream<Arguments> getTestDataForMinuteOfHour() {
781+
return Stream.of(
782+
Arguments.of(
783+
DSL.literal(new ExprTimeValue("01:02:03")),
784+
2,
785+
"minute_of_hour(TIME '01:02:03')"),
786+
Arguments.of(
787+
DSL.literal("01:02:03"),
788+
2,
789+
"minute_of_hour(\"01:02:03\")"),
790+
Arguments.of(
791+
DSL.literal(new ExprTimestampValue("2020-08-17 01:02:03")),
792+
2,
793+
"minute_of_hour(TIMESTAMP '2020-08-17 01:02:03')"),
794+
Arguments.of(
795+
DSL.literal(new ExprDatetimeValue("2020-08-17 01:02:03")),
796+
2,
797+
"minute_of_hour(DATETIME '2020-08-17 01:02:03')"),
798+
Arguments.of(
799+
DSL.literal("2020-08-17 01:02:03"),
800+
2,
801+
"minute_of_hour(\"2020-08-17 01:02:03\")")
802+
);
803+
}
804+
805+
@ParameterizedTest(name = "{2}")
806+
@MethodSource("getTestDataForMinuteOfHour")
807+
public void minuteOfHour(LiteralExpression arg, int expectedResult, String expectedString) {
808+
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
809+
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());
810+
811+
minuteOfHourQuery(DSL.minute_of_hour(arg), expectedResult, expectedString);
812+
}
813+
814+
private void invalidMinuteOfHourQuery(String time) {
815+
FunctionExpression expression = DSL.minute_of_hour(DSL.literal(new ExprTimeValue(time)));
816+
eval(expression);
817+
}
818+
819+
@Test
820+
public void minuteOfHourInvalidArguments() {
821+
when(nullRef.type()).thenReturn(TIME);
822+
when(missingRef.type()).thenReturn(TIME);
823+
824+
assertAll(
825+
() -> assertEquals(nullValue(), eval(DSL.minute_of_hour(nullRef))),
826+
() -> assertEquals(missingValue(), eval(DSL.minute_of_hour(missingRef))),
827+
828+
//Invalid Seconds
829+
() -> assertThrows(
830+
SemanticCheckException.class,
831+
() -> invalidMinuteOfHourQuery("12:23:61")),
832+
833+
//Invalid Minutes
834+
() -> assertThrows(
835+
SemanticCheckException.class,
836+
() -> invalidMinuteOfHourQuery("12:61:34")),
837+
838+
//Invalid Hours
839+
() -> assertThrows(
840+
SemanticCheckException.class,
841+
() -> invalidMinuteOfHourQuery("25:23:34")),
842+
843+
//incorrect format
844+
() -> assertThrows(
845+
SemanticCheckException.class,
846+
() -> invalidMinuteOfHourQuery("asdfasdf"))
847+
);
848+
}
849+
850+
767851
@Test
768852
public void month() {
769853
when(nullRef.type()).thenReturn(DATE);

docs/user/dql/functions.rst

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,20 +1747,21 @@ Description
17471747
>>>>>>>>>>>
17481748

17491749
Usage: minute(time) returns the minute for time, in the range 0 to 59.
1750+
The `minute_of_hour` function is provided as an alias.
17501751

17511752
Argument type: STRING/TIME/DATETIME/TIMESTAMP
17521753

17531754
Return type: INTEGER
17541755

17551756
Example::
17561757

1757-
os> SELECT MINUTE((TIME '01:02:03'))
1758+
os> SELECT MINUTE(time('01:02:03')), MINUTE_OF_HOUR(time('01:02:03'))
17581759
fetched rows / total rows = 1/1
1759-
+-----------------------------+
1760-
| MINUTE((TIME '01:02:03')) |
1761-
|-----------------------------|
1762-
| 2 |
1763-
+-----------------------------+
1760+
+----------------------------+------------------------------------+
1761+
| MINUTE(time('01:02:03')) | MINUTE_OF_HOUR(time('01:02:03')) |
1762+
|----------------------------+------------------------------------|
1763+
| 2 | 2 |
1764+
+----------------------------+------------------------------------+
17641765

17651766
MINUTE_OF_DAY
17661767
------

integ-test/src/test/java/org/opensearch/sql/sql/DateTimeFunctionIT.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,52 @@ public void testMinuteOfDay() throws IOException {
390390
verifyDataRows(result, rows(1050));
391391
}
392392

393+
@Test
394+
public void testMinuteOfHour() throws IOException {
395+
JSONObject result = executeQuery("select minute_of_hour(timestamp('2020-09-16 17:30:00'))");
396+
verifySchema(result, schema(
397+
"minute_of_hour(timestamp('2020-09-16 17:30:00'))", null, "integer"));
398+
verifyDataRows(result, rows(30));
399+
400+
result = executeQuery("select minute_of_hour(time('17:30:00'))");
401+
verifySchema(result, schema("minute_of_hour(time('17:30:00'))", null, "integer"));
402+
verifyDataRows(result, rows(30));
403+
404+
result = executeQuery("select minute_of_hour('2020-09-16 17:30:00')");
405+
verifySchema(result, schema("minute_of_hour('2020-09-16 17:30:00')", null, "integer"));
406+
verifyDataRows(result, rows(30));
407+
408+
result = executeQuery("select minute_of_hour('17:30:00')");
409+
verifySchema(result, schema("minute_of_hour('17:30:00')", null, "integer"));
410+
verifyDataRows(result, rows(30));
411+
}
412+
413+
@Test
414+
public void testMinuteFunctionAliasesReturnTheSameResults() throws IOException {
415+
JSONObject result1 = executeQuery("SELECT minute('11:30:00')");
416+
JSONObject result2 = executeQuery("SELECT minute_of_hour('11:30:00')");
417+
verifyDataRows(result1, rows(30));
418+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
419+
420+
result1 = executeQuery(String.format(
421+
"SELECT minute(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
422+
result2 = executeQuery(String.format(
423+
"SELECT minute_of_hour(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
424+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
425+
426+
result1 = executeQuery(String.format(
427+
"SELECT minute(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
428+
result2 = executeQuery(String.format(
429+
"SELECT minute_of_hour(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
430+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
431+
432+
result1 = executeQuery(String.format(
433+
"SELECT minute(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
434+
result2 = executeQuery(String.format(
435+
"SELECT minute_of_hour(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
436+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
437+
}
438+
393439
@Test
394440
public void testMonth() throws IOException {
395441
JSONObject result = executeQuery("select month(date('2020-09-16'))");

sql/src/main/antlr/OpenSearchSQLParser.g4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ dateTimeFunctionName
429429
| MICROSECOND
430430
| MINUTE
431431
| MINUTE_OF_DAY
432+
| MINUTE_OF_HOUR
432433
| MONTH
433434
| MONTHNAME
434435
| NOW

sql/src/test/java/org/opensearch/sql/sql/antlr/SQLSyntaxParserTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,15 @@ public void can_parse_dayofyear_functions() {
207207
assertNotNull(parser.parse("SELECT day_of_year('2022-11-18')"));
208208
}
209209

210+
@Test
211+
public void can_parse_minute_functions() {
212+
assertNotNull(parser.parse("SELECT minute('12:23:34')"));
213+
assertNotNull(parser.parse("SELECT minute_of_hour('12:23:34')"));
214+
215+
assertNotNull(parser.parse("SELECT minute('2022-12-20 12:23:34')"));
216+
assertNotNull(parser.parse("SELECT minute_of_hour('2022-12-20 12:23:34')"));
217+
}
218+
210219
@Test
211220
public void can_parse_month_of_year_function() {
212221
assertNotNull(parser.parse("SELECT month('2022-11-18')"));

0 commit comments

Comments
 (0)