mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-07-08 18:16:08 +00:00
- DatedifFunction.DaysIgnoringYears: clamp day to DaysInMonth to avoid ArgumentOutOfRangeException when end date is Feb 29 and target year is not a leap year - XLookupFunction: change LinearSearch/BinarySearch to return (index, error) tuple so errors in the lookup array are propagated instead of being swallowed as "not found" - AggregationMethods.Median: return #NUM! for empty input instead of 0, matching Excel behavior - RangeRef.Intersection: return RangeRef.Invalid when ranges don't overlap instead of producing a bogus normalized range
322 lines
9.3 KiB
C#
322 lines
9.3 KiB
C#
using System;
|
|
using Xunit;
|
|
|
|
using Radzen.Documents.Spreadsheet;
|
|
namespace Radzen.Blazor.Spreadsheet.Tests;
|
|
|
|
public class DateFunctionTests
|
|
{
|
|
readonly Worksheet sheet = new(5, 5);
|
|
|
|
[Fact]
|
|
public void ShouldCreateDateFromYearMonthDay()
|
|
{
|
|
sheet.Cells["A1"].Formula = "=DATE(2024,3,15)";
|
|
var date = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 3, 15), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleMonthOverflow()
|
|
{
|
|
// Month 13 = January of next year
|
|
sheet.Cells["A1"].Formula = "=DATE(2024,13,1)";
|
|
var date = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2025, 1, 1), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleZeroMonth()
|
|
{
|
|
// Month 0 = December of previous year
|
|
sheet.Cells["A1"].Formula = "=DATE(2024,0,1)";
|
|
var date = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2023, 12, 1), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleNegativeMonth()
|
|
{
|
|
// Month -1 = November of previous year
|
|
sheet.Cells["A1"].Formula = "=DATE(2024,-1,1)";
|
|
var date = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2023, 11, 1), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleDayOverflow()
|
|
{
|
|
// Day 32 in January = Feb 1
|
|
sheet.Cells["A1"].Formula = "=DATE(2024,1,32)";
|
|
var date = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 2, 1), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleZeroDay()
|
|
{
|
|
// Day 0 = last day of previous month
|
|
sheet.Cells["A1"].Formula = "=DATE(2024,1,0)";
|
|
var date = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2023, 12, 31), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleNegativeDay()
|
|
{
|
|
sheet.Cells["A1"].Formula = "=DATE(2024,1,-1)";
|
|
var date = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2023, 12, 30), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldTreatYearsBetween0And1899AsOffsetFrom1900()
|
|
{
|
|
// Year 0 = 1900
|
|
sheet.Cells["A1"].Formula = "=DATE(0,1,1)";
|
|
var date1 = sheet.Cells["A1"].Data.GetValueOrDefault<DateTime>();
|
|
Assert.Equal(new DateTime(1900, 1, 1), date1);
|
|
|
|
// Year 24 = 1924
|
|
sheet.Cells["A2"].Formula = "=DATE(24,1,1)";
|
|
var date2 = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
Assert.Equal(new DateTime(1924, 1, 1), date2);
|
|
}
|
|
}
|
|
|
|
public class EdateFunctionTests
|
|
{
|
|
readonly Worksheet sheet = new(5, 5);
|
|
|
|
[Fact]
|
|
public void ShouldAddOneMonth()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 15);
|
|
sheet.Cells["A2"].Formula = "=EDATE(A1,1)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 2, 15), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldSubtractOneMonth()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 15);
|
|
sheet.Cells["A2"].Formula = "=EDATE(A1,-1)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2023, 12, 15), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldClampToEndOfShorterMonth()
|
|
{
|
|
// Jan 31 + 1 month = Feb 29 (2024 is leap year)
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 31);
|
|
sheet.Cells["A2"].Formula = "=EDATE(A1,1)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 2, 29), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldClampToEndOfApril()
|
|
{
|
|
// Mar 31 + 1 month = Apr 30
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 3, 31);
|
|
sheet.Cells["A2"].Formula = "=EDATE(A1,1)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 4, 30), date);
|
|
}
|
|
}
|
|
|
|
public class EomonthFunctionTests
|
|
{
|
|
readonly Worksheet sheet = new(5, 5);
|
|
|
|
[Fact]
|
|
public void ShouldReturnEndOfSameMonth()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 15);
|
|
sheet.Cells["A2"].Formula = "=EOMONTH(A1,0)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 1, 31), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnEndOfNextMonth()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 15);
|
|
sheet.Cells["A2"].Formula = "=EOMONTH(A1,1)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 2, 29), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleLeapYear()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 2, 15);
|
|
sheet.Cells["A2"].Formula = "=EOMONTH(A1,0)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2024, 2, 29), date);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleNonLeapYear()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2023, 2, 15);
|
|
sheet.Cells["A2"].Formula = "=EOMONTH(A1,0)";
|
|
var date = sheet.Cells["A2"].Data.GetValueOrDefault<DateTime>();
|
|
|
|
Assert.Equal(new DateTime(2023, 2, 28), date);
|
|
}
|
|
}
|
|
|
|
public class DatedifFunctionTests
|
|
{
|
|
readonly Worksheet sheet = new(5, 5);
|
|
|
|
[Fact]
|
|
public void ShouldReturnFullYears()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2024, 12, 31);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"Y\")";
|
|
|
|
Assert.Equal(0d, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnFullMonths()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2024, 12, 31);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"M\")";
|
|
|
|
Assert.Equal(11d, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnDays()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2024, 12, 31);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"D\")";
|
|
|
|
Assert.Equal(365d, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnMonthsIgnoringYears()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2025, 3, 15);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"YM\")";
|
|
|
|
Assert.Equal(2d, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnDaysIgnoringMonths()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2025, 3, 15);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"MD\")";
|
|
|
|
Assert.Equal(14d, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnDaysIgnoringYears()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2024, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2025, 3, 15);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"YD\")";
|
|
|
|
Assert.Equal(74d, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnErrorWhenStartDateIsAfterEndDate()
|
|
{
|
|
sheet.Cells["A1"].Value = new DateTime(2025, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2024, 1, 1);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"D\")";
|
|
|
|
Assert.Equal(CellError.Num, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleLeapDayInYDWhenStartYearIsNotLeapYear()
|
|
{
|
|
// end is Feb 29 (leap year), start year is not a leap year
|
|
sheet.Cells["A1"].Value = new DateTime(2023, 1, 1);
|
|
sheet.Cells["A2"].Value = new DateTime(2024, 2, 29);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"YD\")";
|
|
|
|
Assert.Equal(58d, sheet.Cells["A3"].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleLeapDayInYDWhenAdjustedEndBeforeStart()
|
|
{
|
|
// end is Feb 29, start is March 15 in a non-leap year — adjusted end < start, so add 1 year
|
|
sheet.Cells["A1"].Value = new DateTime(2023, 3, 15);
|
|
sheet.Cells["A2"].Value = new DateTime(2024, 2, 29);
|
|
sheet.Cells["A3"].Formula = "=DATEDIF(A1,A2,\"YD\")";
|
|
|
|
Assert.Equal(351d, sheet.Cells["A3"].Value);
|
|
}
|
|
}
|
|
|
|
public class TimeFunctionTests
|
|
{
|
|
readonly Worksheet sheet = new(5, 5);
|
|
|
|
[Fact]
|
|
public void ShouldReturnFractionOfDay()
|
|
{
|
|
sheet.Cells["A1"].Formula = "=TIME(12,0,0)";
|
|
|
|
Assert.Equal(0.5, sheet.Cells["A1"].Data.GetValueOrDefault<double>());
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldReturnZeroForMidnight()
|
|
{
|
|
sheet.Cells["A1"].Formula = "=TIME(0,0,0)";
|
|
|
|
Assert.Equal(0d, sheet.Cells["A1"].Data.GetValueOrDefault<double>());
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldHandleMinutesAndSeconds()
|
|
{
|
|
sheet.Cells["A1"].Formula = "=TIME(12,30,0)";
|
|
var result = sheet.Cells["A1"].Data.GetValueOrDefault<double>();
|
|
|
|
Assert.Equal(0.520833333333333, result, 10);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldWrapOverflowHours()
|
|
{
|
|
// 25 hours wraps to 1 hour
|
|
sheet.Cells["A1"].Formula = "=TIME(25,0,0)";
|
|
var result = sheet.Cells["A1"].Data.GetValueOrDefault<double>();
|
|
|
|
Assert.Equal(1.0 / 24.0, result, 10);
|
|
}
|
|
}
|