Skip to content

Commit 1aa4341

Browse files
authored
Merge pull request #82 from ergon/feature/dope-197-date-functions
DOPE-197: date functions
2 parents a03f867 + 7461e17 commit 1aa4341

85 files changed

Lines changed: 5982 additions & 115 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package ch.ergon.dope.functions
2+
3+
import ch.ergon.dope.QueryBuilder
4+
import ch.ergon.dope.integrationTest.BaseIntegrationTest
5+
import ch.ergon.dope.integrationTest.toMapValues
6+
import ch.ergon.dope.resolvable.expression.type.alias
7+
import ch.ergon.dope.resolvable.expression.type.arithmetic.add
8+
import ch.ergon.dope.resolvable.expression.type.collection.inArray
9+
import ch.ergon.dope.resolvable.expression.type.function.date.DateUnitType.DAY
10+
import ch.ergon.dope.resolvable.expression.type.function.date.DateUnitType.MONTH
11+
import ch.ergon.dope.resolvable.expression.type.function.date.dateRangeBy
12+
import ch.ergon.dope.resolvable.expression.type.function.date.differenceIn
13+
import ch.ergon.dope.resolvable.expression.type.function.date.localClockString
14+
import java.time.OffsetDateTime
15+
import java.time.format.DateTimeFormatter
16+
import kotlin.test.Test
17+
import kotlin.test.assertEquals
18+
19+
class DateFunctionsIntegrationTest : BaseIntegrationTest() {
20+
@Test
21+
fun `use string functions to create a new string`() {
22+
val dopeQuery = QueryBuilder()
23+
.select(
24+
localClockString("YYYY-MM-DD").alias("localClock"),
25+
"2023-10-05".differenceIn("2023-08-05", MONTH).add(3).alias("differenceInDays"),
26+
"2025-09-09".inArray("2025-08-08".dateRangeBy("2025-10-10", DAY)).alias("inArray"),
27+
).build()
28+
29+
val queryResult = queryWithoutParameters(dopeQuery)
30+
val result = queryResult.toMapValues()
31+
32+
assertEquals(OffsetDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), result["localClock"])
33+
assertEquals(2 + 3, result["differenceInDays"])
34+
assertEquals(true, result["inArray"])
35+
}
36+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.StringType
7+
8+
class ClockLocalExpression(format: TypeExpression<StringType>? = null) : FunctionExpression<StringType>("CLOCK_LOCAL", format)
9+
10+
fun localClockString(format: TypeExpression<StringType>? = null) = ClockLocalExpression(format)
11+
12+
fun localClockString(format: String) = localClockString(format.toDopeType())
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
4+
import ch.ergon.dope.validtype.NumberType
5+
6+
class ClockMillisExpression() : FunctionExpression<NumberType>("CLOCK_MILLIS")
7+
8+
fun clockMillis() = ClockMillisExpression()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.StringType
7+
8+
class ClockStringExpression(format: TypeExpression<StringType>? = null) : FunctionExpression<StringType>("CLOCK_STR", format)
9+
10+
fun clockString(format: TypeExpression<StringType>? = null) = ClockStringExpression(format)
11+
12+
fun clockString(format: String) = clockString(format.toDopeType())
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.StringType
7+
8+
class ClockTimezoneExpression(timeZone: TypeExpression<StringType>, format: TypeExpression<StringType>? = null) :
9+
FunctionExpression<StringType>("CLOCK_TZ", timeZone, format)
10+
11+
fun formattedClockIn(timeZone: TypeExpression<StringType>, format: TypeExpression<StringType>? = null) =
12+
ClockTimezoneExpression(timeZone, format)
13+
14+
fun formattedClockIn(timeZone: String, format: TypeExpression<StringType>? = null) = formattedClockIn(timeZone.toDopeType(), format)
15+
16+
fun formattedClockIn(timeZone: TypeExpression<StringType>, format: String) = formattedClockIn(timeZone, format.toDopeType())
17+
18+
fun formattedClockIn(timeZone: String, format: String) = formattedClockIn(timeZone.toDopeType(), format.toDopeType())
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.StringType
7+
8+
class ClockUtcExpression(format: TypeExpression<StringType>? = null) : FunctionExpression<StringType>("CLOCK_UTC", format)
9+
10+
fun utcClockString(format: TypeExpression<StringType>? = null) = ClockUtcExpression(format)
11+
12+
fun utcClockString(format: String) = utcClockString(format.toDopeType())
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.NumberType
7+
import ch.ergon.dope.validtype.StringType
8+
9+
class DateAddMillisExpression(date: TypeExpression<NumberType>, increment: TypeExpression<NumberType>, dateUnit: DateUnit) :
10+
FunctionExpression<NumberType>("DATE_ADD_MILLIS", date, increment, dateUnit)
11+
12+
class DateAddStrExpression(date: TypeExpression<StringType>, increment: TypeExpression<NumberType>, dateUnit: DateUnit) :
13+
FunctionExpression<StringType>("DATE_ADD_STR", date, increment, dateUnit)
14+
15+
@JvmName("millisPlusTypeDateComponent")
16+
fun TypeExpression<NumberType>.addDateUnit(
17+
increment: TypeExpression<NumberType>,
18+
dateUnit: DateUnit,
19+
) = DateAddMillisExpression(this, increment, dateUnit)
20+
21+
@JvmName("millisPlusNumberDateComponent")
22+
fun TypeExpression<NumberType>.addDateUnit(
23+
increment: Number,
24+
dateUnit: DateUnit,
25+
) = addDateUnit(increment.toDopeType(), dateUnit)
26+
27+
fun Number.addDateUnit(
28+
increment: TypeExpression<NumberType>,
29+
dateUnit: DateUnit,
30+
) = toDopeType().addDateUnit(increment, dateUnit)
31+
32+
fun Number.addDateUnit(
33+
increment: Number,
34+
dateUnit: DateUnit,
35+
) = toDopeType().addDateUnit(increment.toDopeType(), dateUnit)
36+
37+
@JvmName("strPlusTypeDateComponent")
38+
fun TypeExpression<StringType>.addDateUnit(
39+
increment: TypeExpression<NumberType>,
40+
dateUnit: DateUnit,
41+
) = DateAddStrExpression(this, increment, dateUnit)
42+
43+
@JvmName("strPlusNumberDateComponent")
44+
fun TypeExpression<StringType>.addDateUnit(
45+
increment: Number,
46+
dateUnit: DateUnit,
47+
) = addDateUnit(increment.toDopeType(), dateUnit)
48+
49+
fun String.addDateUnit(
50+
increment: TypeExpression<NumberType>,
51+
dateUnit: DateUnit,
52+
) = toDopeType().addDateUnit(increment, dateUnit)
53+
54+
fun String.addDateUnit(
55+
increment: Number,
56+
dateUnit: DateUnit,
57+
) = toDopeType().addDateUnit(increment.toDopeType(), dateUnit)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.DopeQueryManager
4+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.StringType
7+
8+
sealed interface DateComponent : TypeExpression<StringType>
9+
sealed interface DateUnit : DateComponent
10+
11+
enum class DateUnitType(private val queryString: String) : DateUnit {
12+
MILLENNIUM("MILLENNIUM"),
13+
CENTURY("CENTURY"),
14+
DECADE("DECADE"),
15+
YEAR("YEAR"),
16+
QUARTER("QUARTER"),
17+
MONTH("MONTH"),
18+
WEEK("WEEK"),
19+
DAY("DAY"),
20+
HOUR("HOUR"),
21+
MINUTE("MINUTE"),
22+
SECOND("SECOND"),
23+
MILLISECOND("MILLISECOND"),
24+
;
25+
26+
override fun toDopeQuery(manager: DopeQueryManager) =
27+
queryString.toDopeType().toDopeQuery(manager)
28+
}
29+
30+
enum class DateComponentType(private val queryString: String) : DateComponent {
31+
ISO_YEAR("ISO_YEAR"),
32+
ISO_WEEK("ISO_WEEK"),
33+
DAY_OF_YEAR("DAY_OF_YEAR"),
34+
DAY_OF_WEEK("DAY_OF_WEEK"),
35+
TIMEZONE("TIMEZONE"),
36+
TIMEZONE_HOUR("TIMEZONE_HOUR"),
37+
TIMEZONE_MINUTE("TIMEZONE_MINUTE"),
38+
;
39+
40+
override fun toDopeQuery(manager: DopeQueryManager) =
41+
queryString.toDopeType().toDopeQuery(manager)
42+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.NumberType
7+
import ch.ergon.dope.validtype.StringType
8+
9+
class DateDiffMillisExpression(date: TypeExpression<NumberType>, other: TypeExpression<NumberType>, dateUnit: DateUnit) :
10+
FunctionExpression<NumberType>("DATE_DIFF_MILLIS", date, other, dateUnit)
11+
12+
class DateDiffStrExpression(date: TypeExpression<StringType>, other: TypeExpression<StringType>, dateUnit: DateUnit) :
13+
FunctionExpression<NumberType>("DATE_DIFF_STR", date, other, dateUnit)
14+
15+
@JvmName("millisDiffTypeDateComponent")
16+
fun TypeExpression<NumberType>.differenceIn(
17+
other: TypeExpression<NumberType>,
18+
dateUnit: DateUnit,
19+
) = DateDiffMillisExpression(this, other, dateUnit)
20+
21+
@JvmName("millisDiffNumberDateComponent")
22+
fun TypeExpression<NumberType>.differenceIn(
23+
other: Number,
24+
dateUnit: DateUnit,
25+
) = differenceIn(other.toDopeType(), dateUnit)
26+
27+
fun Number.differenceIn(
28+
other: TypeExpression<NumberType>,
29+
dateUnit: DateUnit,
30+
) = toDopeType().differenceIn(other, dateUnit)
31+
32+
fun Number.differenceIn(
33+
other: Number,
34+
dateUnit: DateUnit,
35+
) = toDopeType().differenceIn(other.toDopeType(), dateUnit)
36+
37+
@JvmName("strDiffTypeDateComponent")
38+
fun TypeExpression<StringType>.differenceIn(
39+
other: TypeExpression<StringType>,
40+
dateUnit: DateUnit,
41+
) = DateDiffStrExpression(this, other, dateUnit)
42+
43+
@JvmName("strDiffNumberDateComponent")
44+
fun TypeExpression<StringType>.differenceIn(
45+
other: String,
46+
dateUnit: DateUnit,
47+
) = differenceIn(other.toDopeType(), dateUnit)
48+
49+
fun String.differenceIn(
50+
other: TypeExpression<StringType>,
51+
dateUnit: DateUnit,
52+
) = toDopeType().differenceIn(other, dateUnit)
53+
54+
fun String.differenceIn(
55+
other: String,
56+
dateUnit: DateUnit,
57+
) = toDopeType().differenceIn(other.toDopeType(), dateUnit)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.date
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.StringType
7+
8+
class DateFormatStrExpression(date: TypeExpression<StringType>, format: TypeExpression<StringType>) :
9+
FunctionExpression<StringType>("DATE_FORMAT_STR", date, format)
10+
11+
fun TypeExpression<StringType>.formatDate(format: TypeExpression<StringType>) = DateFormatStrExpression(this, format)
12+
13+
fun TypeExpression<StringType>.formatDate(format: String) = formatDate(format.toDopeType())
14+
15+
fun String.formatDate(format: TypeExpression<StringType>) = toDopeType().formatDate(format)
16+
17+
fun String.formatDate(format: String) = toDopeType().formatDate(format.toDopeType())

0 commit comments

Comments
 (0)