From ef17dc31e511ad980d8aee8e609e64259d6008c4 Mon Sep 17 00:00:00 2001 From: Chandana Date: Thu, 21 May 2026 15:27:28 +0530 Subject: [PATCH 1/2] Add utc now function to support utc datetime evaluation in expressions. --- ExpressionEvaluator/ExpressionKeywords.cs | 1 + .../Procedures/Functions/UtcNow.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 ExpressionEvaluator/Procedures/Functions/UtcNow.cs diff --git a/ExpressionEvaluator/ExpressionKeywords.cs b/ExpressionEvaluator/ExpressionKeywords.cs index 01f3788..1184ccd 100644 --- a/ExpressionEvaluator/ExpressionKeywords.cs +++ b/ExpressionEvaluator/ExpressionKeywords.cs @@ -51,6 +51,7 @@ static ExpressionKeywords() new NaturalLog(60), new Sign(60), new Now(60), + new UtcNow(60), new Days(60), new Hours(60), new Minutes(60), diff --git a/ExpressionEvaluator/Procedures/Functions/UtcNow.cs b/ExpressionEvaluator/Procedures/Functions/UtcNow.cs new file mode 100644 index 0000000..8d77759 --- /dev/null +++ b/ExpressionEvaluator/Procedures/Functions/UtcNow.cs @@ -0,0 +1,15 @@ +using System; +using Vanderbilt.Biostatistics.Wfccm2; + +namespace ExpressionEvaluator.Procedures.Functions +{ + internal class UtcNow : Function + { + public UtcNow(int precedance) + : base("utcnow", precedance, 0, false) + { + _name2 = "UtcNow"; + Datetime = () => DateTime.UtcNow; + } + } +} From 2990e3958adeb7bf997baa19c8fa5e31bdfb0ece Mon Sep 17 00:00:00 2001 From: Chandana Date: Wed, 27 May 2026 17:52:13 +0530 Subject: [PATCH 2/2] Adding unit tests for utcnow function --- .../UtcNowFunctionTests.cs | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 ExpressionEvaluatorTests/UtcNowFunctionTests.cs diff --git a/ExpressionEvaluatorTests/UtcNowFunctionTests.cs b/ExpressionEvaluatorTests/UtcNowFunctionTests.cs new file mode 100644 index 0000000..64860d7 --- /dev/null +++ b/ExpressionEvaluatorTests/UtcNowFunctionTests.cs @@ -0,0 +1,160 @@ +// ReSharper disable InconsistentNaming + +using System; +using NUnit.Framework; +using Vanderbilt.Biostatistics.Wfccm2; + +namespace ExpressionEvaluatorTests +{ + [TestFixture] + public class UtcNowFunctionTests + { + private Expression func; + + [SetUp] + public void init() { func = new Expression(""); } + + [TearDown] + public void clear() { func.Clear(); } + + [Test] + public void UtcNow_AllLower_NoException() + { + func.Function = "utcnow()"; + } + + [Test] + public void UtcNow_HasCaps_NoException() + { + func.Function = "UtcNow()"; + } + + [Test] + public void UtcNow_AllUpper_NoException() + { + func.Function = "UTCNOW()"; + } + + [Test] + public void UtcNow_Evaluate_ReturnsDateTime() + { + func.Function = "utcnow()"; + var result = func.Evaluate(); + Assert.IsInstanceOf(result); + } + + [Test] + public void UtcNow_Evaluate_ReturnsUtcKind() + { + func.Function = "utcnow()"; + var result = func.Evaluate(); + Assert.AreEqual(DateTimeKind.Utc, result.Kind); + } + + [Test] + public void UtcNow_Evaluate_ReturnsCurrentUtcTime() + { + func.Function = "utcnow()"; + var before = DateTime.UtcNow; + var result = func.Evaluate(); + var after = DateTime.UtcNow; + + Assert.GreaterOrEqual(result, before); + Assert.LessOrEqual(result, after); + } + + [Test] + public void UtcNow_AddDays_IsCorrect() + { + func.Function = "utcnow() + days(1)"; + var before = DateTime.UtcNow; + var result = func.Evaluate(); + var after = DateTime.UtcNow; + + Assert.GreaterOrEqual(result, before.AddDays(1)); + Assert.LessOrEqual(result, after.AddDays(1)); + } + + [Test] + public void UtcNow_SubtractDays_IsCorrect() + { + func.Function = "utcnow() - days(1)"; + var before = DateTime.UtcNow; + var result = func.Evaluate(); + var after = DateTime.UtcNow; + + Assert.GreaterOrEqual(result, before.AddDays(-1)); + Assert.LessOrEqual(result, after.AddDays(-1)); + } + + [Test] + public void UtcNow_GreaterThanPastDate_IsTrue() + { + func.Function = "utcnow() > a"; + func.AddSetVariable("a", new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)); + Assert.IsTrue(func.EvaluateBoolean()); + } + + [Test] + public void UtcNow_LessThanFutureDate_IsTrue() + { + func.Function = "utcnow() < a"; + func.AddSetVariable("a", new DateTime(2100, 1, 1, 0, 0, 0, DateTimeKind.Utc)); + Assert.IsTrue(func.EvaluateBoolean()); + } + + [Test] + public void UtcNow_GreaterOrEqualPastDate_IsTrue() + { + var testDate = DateTime.UtcNow.AddDays(-1); + func.Function = "utcnow() >= a"; + func.AddSetVariable("a", testDate); + Assert.IsTrue(func.EvaluateBoolean()); + } + + [Test] + public void UtcNow_LessOrEqualFutureDate_IsTrue() + { + var testDate = DateTime.UtcNow.AddDays(1); + func.Function = "utcnow() <= a"; + func.AddSetVariable("a", testDate); + Assert.IsTrue(func.EvaluateBoolean()); + } + + [Test] + public void UtcNow_SubtractVariable_ReturnsTimeSpan() + { + var pastDate = DateTime.UtcNow.AddDays(-1); + func.Function = "totaldays(utcnow() - a)"; + func.AddSetVariable("a", pastDate); + var result = func.EvaluateNumeric(); + + Assert.GreaterOrEqual(result, 0.99); + Assert.LessOrEqual(result, 1.01); + } + + [Test] + public void UtcNow_AddHours_IsCorrect() + { + func.Function = "utcnow() + hours(2)"; + var before = DateTime.UtcNow; + var result = func.Evaluate(); + var after = DateTime.UtcNow; + + Assert.GreaterOrEqual(result, before.AddHours(2)); + Assert.LessOrEqual(result, after.AddHours(2)); + } + + [Test] + public void UtcNow_AddMinutes_IsCorrect() + { + func.Function = "utcnow() + minutes(30)"; + var before = DateTime.UtcNow; + var result = func.Evaluate(); + var after = DateTime.UtcNow; + + Assert.GreaterOrEqual(result, before.AddMinutes(30)); + Assert.LessOrEqual(result, after.AddMinutes(30)); + } + } +}