Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions api/expression-parser.api
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ public abstract interface class org/hisp/dhis/lib/expression/ast/Typed {
public final class org/hisp/dhis/lib/expression/ast/Typed$Companion {
public final fun toBooleanTypeCoercion (Ljava/lang/Object;)Ljava/lang/Boolean;
public final fun toDateTypeCoercion (Ljava/lang/Object;)Lkotlinx/datetime/LocalDate;
public final fun toInstantTypeCoercion (Ljava/lang/Object;)Lkotlin/time/Instant;
public final fun toMixedTypeTypeCoercion (Ljava/lang/Object;)Ljava/lang/Object;
public final fun toNumberTypeCoercion (Ljava/lang/Object;)Ljava/lang/Double;
public final fun toStringTypeCoercion (Ljava/lang/Object;)Ljava/lang/String;
Expand Down Expand Up @@ -1408,7 +1409,7 @@ public abstract interface class org/hisp/dhis/lib/expression/spi/ExpressionFunct
public fun d2_length (Ljava/lang/String;)I
public fun d2_maxValue (Lorg/hisp/dhis/lib/expression/spi/VariableValue;)D
public fun d2_minValue (Lorg/hisp/dhis/lib/expression/spi/VariableValue;)D
public fun d2_minutesBetween (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I
public fun d2_minutesBetween (Lkotlin/time/Instant;Lkotlin/time/Instant;)I
public fun d2_modulus (Ljava/lang/Number;Ljava/lang/Number;)D
public fun d2_monthsBetween (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I
public fun d2_oizp (Ljava/lang/Number;)D
Expand Down Expand Up @@ -1470,7 +1471,7 @@ public final class org/hisp/dhis/lib/expression/spi/ExpressionFunctions$DefaultI
public static fun d2_length (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Ljava/lang/String;)I
public static fun d2_maxValue (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lorg/hisp/dhis/lib/expression/spi/VariableValue;)D
public static fun d2_minValue (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lorg/hisp/dhis/lib/expression/spi/VariableValue;)D
public static fun d2_minutesBetween (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I
public static fun d2_minutesBetween (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lkotlin/time/Instant;Lkotlin/time/Instant;)I
public static fun d2_modulus (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Ljava/lang/Number;Ljava/lang/Number;)D
public static fun d2_monthsBetween (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I
public static fun d2_oizp (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Ljava/lang/Number;)D
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repositories {
mavenCentral()
}

version = "1.3.1-SNAPSHOT"
version = "1.3.2-SNAPSHOT"
group = "org.hisp.dhis.lib.expression"

if (project.hasProperty("removeSnapshotSuffix")) {
Expand Down
17 changes: 17 additions & 0 deletions src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Typed.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.hisp.dhis.lib.expression.ast
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import kotlinx.datetime.toInstant
import kotlinx.datetime.atTime
import org.hisp.dhis.lib.expression.spi.ValueType
import org.hisp.dhis.lib.expression.spi.VariableValue
import kotlin.time.Instant
Expand Down Expand Up @@ -40,6 +42,21 @@ fun interface Typed {
throw IllegalArgumentException("Count not coerce to date: '$value'")
}

fun toInstantTypeCoercion(value: Any?): Instant? {
if (value == null) return null
if (value is VariableValue) return toInstantTypeCoercion(toMixedTypeTypeCoercion(value))
if (value is LocalDate) return value.atTime(0, 0).toInstant(TimeZone.UTC)
if (value is String) {
return try {
Instant.parse(value)
} catch (e: IllegalArgumentException) {
toInstantTypeCoercion(LocalDate.parse(value))
}
}
if (value is Instant) return value
throw IllegalArgumentException("Count not coerce to instant: '$value'")
}

fun toStringTypeCoercion(value: Any?): String? {
if (value == null) return null
if (value is VariableValue) return toStringTypeCoercion(toMixedTypeTypeCoercion(value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import kotlinx.datetime.LocalDate
import org.hisp.dhis.lib.expression.ast.*
import org.hisp.dhis.lib.expression.ast.UnaryOperator.Companion.negate
import org.hisp.dhis.lib.expression.spi.*
import kotlin.time.Instant

/**
* A [NodeInterpreter] that calculates the expression result value using a [ExpressionFunctions] to
Expand Down Expand Up @@ -113,8 +114,8 @@ internal class Calculator(
NamedFunction.d2_length -> functions.d2_length(evalToString(fn.child(0)))
NamedFunction.d2_maxValue -> functions.d2_maxValue(evalToVar(fn.child(0)))
NamedFunction.d2_minutesBetween -> functions.d2_minutesBetween(
evalToDate(fn.child(0)),
evalToDate(fn.child(1)))
evalToInstant(fn.child(0)),
evalToInstant(fn.child(1)))
NamedFunction.d2_minValue -> functions.d2_minValue(evalToVar(fn.child(0)))
NamedFunction.d2_modulus -> functions.d2_modulus(
evalToNumber(fn.child(0)),
Expand Down Expand Up @@ -313,6 +314,10 @@ internal class Calculator(
return eval(node, "Date", Typed::toDateTypeCoercion)
}

fun evalToInstant(node: Node<*>): Instant? {
return eval(node, "Instant", Typed::toInstantTypeCoercion)
}

private fun evalToInteger(node: Node<*>): Int? {
val num = evalToNumber(node) ?: return null
require(num % 1.0 == 0.0) { "Expected an integer but got a floating point for: $node" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hisp.dhis.lib.expression.spi

import com.ionspin.kotlin.bignum.decimal.BigDecimal
import kotlin.time.Instant
import kotlinx.datetime.*
import org.hisp.dhis.lib.expression.ast.BinaryOperator.Companion.modulo
import org.hisp.dhis.lib.expression.math.GS1Elements.Companion.fromKey
Expand Down Expand Up @@ -257,10 +258,10 @@ fun interface ExpressionFunctions {
else value.candidates.maxOfOrNull(String::toDouble) ?: Double.NaN
}

fun d2_minutesBetween(start: LocalDate?, end: LocalDate?): Int {
fun d2_minutesBetween(start: Instant?, end: Instant?): Int {
require(start != null) { "start parameter of d2:minutesBetween must not be null" }
require(end != null) { "end parameter of d2:minutesBetween must not be null" }
return d2_daysBetween(start, end).times(24 * 60)
return (end - start).inWholeMinutes.toInt()
}

fun d2_minValue(value: VariableValue?): Double {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ internal class MinutesBetweenTest {
assertEquals(-6 * minPerDay, evaluate("d2:minutesBetween(\"2020-01-07\", \"2020-01-01\")"))
}

@Test
fun testMinutesBetween_ISO8601() {
assertEquals(8, evaluate("d2:minutesBetween(\"2020-01-01T18:01:00Z\", \"2020-01-01T18:09:00Z\")"))
assertEquals(60, evaluate("d2:minutesBetween(\"2020-01-01\", \"2020-01-01T01:00:00Z\")"))
}

@Test
fun testMinutesBetween_Null() {
val ex = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:minutesBetween(null, \"2021-01-01\")") }
Expand Down