diff --git a/checker/src/main/java/dev/cel/checker/CelStandardDeclarations.java b/checker/src/main/java/dev/cel/checker/CelStandardDeclarations.java index 12ad47c62..159ed9e0c 100644 --- a/checker/src/main/java/dev/cel/checker/CelStandardDeclarations.java +++ b/checker/src/main/java/dev/cel/checker/CelStandardDeclarations.java @@ -213,6 +213,7 @@ public enum StandardFunction { "getDayOfWeek", Overload.DateTime.TIMESTAMP_TO_DAY_OF_WEEK, Overload.DateTime.TIMESTAMP_TO_DAY_OF_WEEK_WITH_TZ), + GET_DAYS("getDays", Overload.DateTime.DURATION_TO_DAYS), GET_HOURS( "getHours", Overload.DateTime.TIMESTAMP_TO_HOURS, @@ -907,6 +908,10 @@ public enum DateTime implements StandardOverload { SimpleType.TIMESTAMP, SimpleType.STRING)), + DURATION_TO_DAYS( + CelOverloadDecl.newMemberOverload( + "duration_to_days", "get days from duration", SimpleType.INT, SimpleType.DURATION)), + TIMESTAMP_TO_HOURS( CelOverloadDecl.newMemberOverload( "timestamp_to_hours", diff --git a/checker/src/test/resources/standardEnvDump.baseline b/checker/src/test/resources/standardEnvDump.baseline index 864bfd340..d8559a7fd 100644 --- a/checker/src/test/resources/standardEnvDump.baseline +++ b/checker/src/test/resources/standardEnvDump.baseline @@ -200,6 +200,9 @@ declare getDayOfYear { function timestamp_to_day_of_year google.protobuf.Timestamp.() -> int function timestamp_to_day_of_year_with_tz google.protobuf.Timestamp.(string) -> int } +declare getDays { + function duration_to_days google.protobuf.Duration.() -> int +} declare getFullYear { function timestamp_to_year google.protobuf.Timestamp.() -> int function timestamp_to_year_with_tz google.protobuf.Timestamp.(string) -> int diff --git a/common/src/main/java/dev/cel/common/internal/ProtoTimeUtils.java b/common/src/main/java/dev/cel/common/internal/ProtoTimeUtils.java index 36671842d..017206d76 100644 --- a/common/src/main/java/dev/cel/common/internal/ProtoTimeUtils.java +++ b/common/src/main/java/dev/cel/common/internal/ProtoTimeUtils.java @@ -68,6 +68,7 @@ public final class ProtoTimeUtils { private static final long SECONDS_PER_MINUTE = 60L; private static final long SECONDS_PER_HOUR = SECONDS_PER_MINUTE * 60; + private static final long SECONDS_PER_DAY = SECONDS_PER_HOUR * 24; private static final ThreadLocal TIMESTAMP_FORMAT = new ThreadLocal() { @@ -164,6 +165,14 @@ public static long toHours(Duration duration) { return checkValid(duration).getSeconds() / SECONDS_PER_HOUR; } + /** + * Convert a Duration to the number of days. The result will be rounded towards 0 to the nearest + * day. + */ + public static long toDays(Duration duration) { + return checkValid(duration).getSeconds() / SECONDS_PER_DAY; + } + /** * Convert a Duration to the number of minutes. The result will be rounded towards 0 to the * nearest minute. diff --git a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel index d253edba3..04036ec1b 100644 --- a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel @@ -628,6 +628,7 @@ java_library( "//runtime/standard:get_day_of_month", "//runtime/standard:get_day_of_week", "//runtime/standard:get_day_of_year", + "//runtime/standard:get_days", "//runtime/standard:get_full_year", "//runtime/standard:get_hours", "//runtime/standard:get_milliseconds", @@ -686,6 +687,7 @@ cel_android_library( "//runtime/standard:get_day_of_month_android", "//runtime/standard:get_day_of_week_android", "//runtime/standard:get_day_of_year_android", + "//runtime/standard:get_days_android", "//runtime/standard:get_full_year_android", "//runtime/standard:get_hours_android", "//runtime/standard:get_milliseconds_android", diff --git a/runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java b/runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java index 39797e086..ab7f81a67 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java +++ b/runtime/src/main/java/dev/cel/runtime/CelStandardFunctions.java @@ -55,6 +55,8 @@ import dev.cel.runtime.standard.GetDayOfWeekFunction.GetDayOfWeekOverload; import dev.cel.runtime.standard.GetDayOfYearFunction; import dev.cel.runtime.standard.GetDayOfYearFunction.GetDayOfYearOverload; +import dev.cel.runtime.standard.GetDaysFunction; +import dev.cel.runtime.standard.GetDaysFunction.GetDaysOverload; import dev.cel.runtime.standard.GetFullYearFunction; import dev.cel.runtime.standard.GetFullYearFunction.GetFullYearOverload; import dev.cel.runtime.standard.GetHoursFunction; @@ -158,6 +160,7 @@ public final class CelStandardFunctions { GetDayOfMonthFunction.create(), GetDayOfWeekFunction.create(), GetDayOfYearFunction.create(), + GetDaysFunction.create(), GetFullYearFunction.create(), GetHoursFunction.create(), GetMillisecondsFunction.create(), @@ -311,6 +314,8 @@ public enum StandardFunction { GetDayOfWeekOverload.TIMESTAMP_TO_DAY_OF_WEEK, GetDayOfWeekOverload.TIMESTAMP_TO_DAY_OF_WEEK_WITH_TZ), + GET_DAYS("getDays", GetDaysOverload.DURATION_TO_DAYS), + GET_HOURS( "getHours", GetHoursOverload.TIMESTAMP_TO_HOURS, diff --git a/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel index 5853220ae..524189697 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel @@ -519,6 +519,41 @@ cel_android_library( ], ) +java_library( + name = "get_days", + srcs = ["GetDaysFunction.java"], + tags = [ + ], + deps = [ + ":standard_function", + ":standard_overload", + "//common:options", + "//common/internal:proto_time_utils", + "//runtime:function_binding", + "//runtime:runtime_equality", + "//runtime/standard:standard_function", + "@maven//:com_google_guava_guava", + "@maven//:com_google_protobuf_protobuf_java", + ], +) + +cel_android_library( + name = "get_days_android", + srcs = ["GetDaysFunction.java"], + tags = [ + ], + deps = [ + ":standard_function_android", + ":standard_overload_android", + "//common:options", + "//common/internal:proto_time_utils_android", + "//runtime:function_binding_android", + "//runtime:runtime_equality_android", + "@maven_android//:com_google_guava_guava", + "@maven_android//:com_google_protobuf_protobuf_javalite", + ], +) + java_library( name = "get_full_year", srcs = ["GetFullYearFunction.java"], diff --git a/runtime/src/main/java/dev/cel/runtime/standard/GetDaysFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/GetDaysFunction.java new file mode 100644 index 000000000..79f700019 --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/standard/GetDaysFunction.java @@ -0,0 +1,71 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev.cel.runtime.standard; + +import com.google.common.collect.ImmutableSet; +import com.google.protobuf.Duration; +import dev.cel.common.CelOptions; +import dev.cel.common.internal.ProtoTimeUtils; +import dev.cel.runtime.CelFunctionBinding; +import dev.cel.runtime.RuntimeEquality; +import java.util.Arrays; + +/** Standard function for {@code getDays}. */ +public final class GetDaysFunction extends CelStandardFunction { + private static final GetDaysFunction ALL_OVERLOADS = create(GetDaysOverload.values()); + + public static GetDaysFunction create() { + return ALL_OVERLOADS; + } + + public static GetDaysFunction create(GetDaysFunction.GetDaysOverload... overloads) { + return create(Arrays.asList(overloads)); + } + + public static GetDaysFunction create(Iterable overloads) { + return new GetDaysFunction(ImmutableSet.copyOf(overloads)); + } + + /** Overloads for the standard function. */ + public enum GetDaysOverload implements CelStandardOverload { + DURATION_TO_DAYS( + (celOptions, runtimeEquality) -> { + if (celOptions.evaluateCanonicalTypesToNativeValues()) { + return CelFunctionBinding.from( + "duration_to_days", java.time.Duration.class, java.time.Duration::toDays); + } else { + return CelFunctionBinding.from( + "duration_to_days", Duration.class, ProtoTimeUtils::toDays); + } + }), + ; + + private final CelStandardOverload standardOverload; + + @Override + public CelFunctionBinding newFunctionBinding( + CelOptions celOptions, RuntimeEquality runtimeEquality) { + return standardOverload.newFunctionBinding(celOptions, runtimeEquality); + } + + GetDaysOverload(CelStandardOverload standardOverload) { + this.standardOverload = standardOverload; + } + } + + private GetDaysFunction(ImmutableSet overloads) { + super("getDays", overloads); + } +} diff --git a/runtime/standard/BUILD.bazel b/runtime/standard/BUILD.bazel index bc6ab9ed3..8af6d0025 100644 --- a/runtime/standard/BUILD.bazel +++ b/runtime/standard/BUILD.bazel @@ -156,6 +156,16 @@ cel_android_library( exports = ["//runtime/src/main/java/dev/cel/runtime/standard:get_date_android"], ) +java_library( + name = "get_days", + exports = ["//runtime/src/main/java/dev/cel/runtime/standard:get_days"], +) + +cel_android_library( + name = "get_days_android", + exports = ["//runtime/src/main/java/dev/cel/runtime/standard:get_days_android"], +) + java_library( name = "get_full_year", exports = ["//runtime/src/main/java/dev/cel/runtime/standard:get_full_year"],