From 7922232df3e0cf423d19f15745f745e1feaf9110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ossian=20Edstr=C3=B6m?= Date: Thu, 12 Mar 2026 12:01:08 +0100 Subject: [PATCH 1/4] Functional but hard to follow fix --- .../Excel/Functions/DateAndTime/Weekday.cs | 24 +++++ .../Excel/Functions/DateTimeFunctionsTests.cs | 91 +++++++++++++++++-- 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs b/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs index 1733dcf54a..e1416e9b13 100644 --- a/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs +++ b/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs @@ -72,9 +72,33 @@ private int CalculateDayOfWeek(DateTime dateTime, int returnType) return _oneBasedStartOnMonday[dayIx]; case 3: return _zeroBasedStartOnSunday[dayIx]; + case 12: + return _oneBasedStartOnMonday[GetIndex(dayIx - 1)]; + case 13: + return _oneBasedStartOnMonday[GetIndex(dayIx - 2)]; + case 14: + return _oneBasedStartOnMonday[GetIndex(dayIx - 3)]; + case 15: + return _oneBasedStartOnMonday[GetIndex(dayIx - 4)]; + case 16: + return _oneBasedStartOnMonday[GetIndex(dayIx - 5)]; + case 17: + return _oneBasedStartOnMonday[GetIndex(dayIx - 6)]; default: throw new ExcelErrorValueException(eErrorType.Num); } } + + int GetIndex(int indexPotentiallyNegative) + { + if(indexPotentiallyNegative >= 0) + { + return indexPotentiallyNegative; + } + else + { + return _oneBasedStartOnMonday.Count + indexPotentiallyNegative; + } + } } } diff --git a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs index de977b6f25..ed989f4ade 100644 --- a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs +++ b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs @@ -26,20 +26,21 @@ Date Author Change ******************************************************************************* 01/27/2020 EPPlus Software AB Initial release EPPlus 5 *******************************************************************************/ -using System; -using System.Text; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using EPPlusTest.FormulaParsing.TestHelpers; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Threading; +using OfficeOpenXml; using OfficeOpenXml.FormulaParsing; +using OfficeOpenXml.FormulaParsing.Excel.Functions; using OfficeOpenXml.FormulaParsing.Excel.Functions.DateAndTime; -using EPPlusTest.FormulaParsing.TestHelpers; -using OfficeOpenXml; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance; using OfficeOpenXml.FormulaParsing.FormulaExpressions; -using OfficeOpenXml.FormulaParsing.Excel.Functions; using OfficeOpenXml.FormulaParsing.Ranges; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; namespace EPPlusTest.Excel.Functions { @@ -302,6 +303,78 @@ public void WeekdayShouldReturnCorrectResultForASundayWhenReturnTypeIs3() Assert.AreEqual(6, result.Result); } + [TestMethod] + public void WeekdayShouldReturnCorrectResultForAThursdayWhenTypeIs12() + { + var func = new Weekday(); + var date = new DateTime(2026, 3, 12).ToOADate(); + + var result = func.Execute(FunctionsHelper.CreateArgs(date, 12), _parsingContext); + var result2 = func.Execute(FunctionsHelper.CreateArgs(date, 13), _parsingContext); + var result3 = func.Execute(FunctionsHelper.CreateArgs(date, 14), _parsingContext); + var result4 = func.Execute(FunctionsHelper.CreateArgs(date, 15), _parsingContext); + var result5 = func.Execute(FunctionsHelper.CreateArgs(date, 16), _parsingContext); + var result6 = func.Execute(FunctionsHelper.CreateArgs(date, 17), _parsingContext); + + Assert.AreEqual(3, result.Result); + Assert.AreEqual(2, result2.Result); + Assert.AreEqual(1, result3.Result); + Assert.AreEqual(7, result4.Result); + Assert.AreEqual(6, result5.Result); + Assert.AreEqual(5, result6.Result); + } + + [TestMethod] + public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs12() + { + var func = new Weekday(); + var date = new DateTime(2026, 3, 11).ToOADate(); + + var result = func.Execute(FunctionsHelper.CreateArgs(date, 12), _parsingContext); + var result2 = func.Execute(FunctionsHelper.CreateArgs(date, 13), _parsingContext); + var result3 = func.Execute(FunctionsHelper.CreateArgs(date, 14), _parsingContext); + var result4 = func.Execute(FunctionsHelper.CreateArgs(date, 15), _parsingContext); + var result5 = func.Execute(FunctionsHelper.CreateArgs(date, 16), _parsingContext); + var result6 = func.Execute(FunctionsHelper.CreateArgs(date, 17), _parsingContext); + + Assert.AreEqual(2, result.Result); + Assert.AreEqual(1, result2.Result); + Assert.AreEqual(7, result3.Result); + Assert.AreEqual(6, result4.Result); + Assert.AreEqual(5, result5.Result); + Assert.AreEqual(4, result6.Result); + } + + [TestMethod] + public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs17ForAllDays() + { + var func = new Weekday(); + + List weekOfOADates = new List(); + + for(int i = 0; i< 7; i++) + { + weekOfOADates.Add(new DateTime(2026, 3, 9 + i).ToOADate()); + } + + for(int i = 0; i< 7; i++) + { + var result = func.Execute(FunctionsHelper.CreateArgs(weekOfOADates[i], 17), _parsingContext); + + if(2 + i <= 7) + { + Assert.AreEqual(2 + i, result.Result); + } + else + { + //only happens for 2+6=8 go back to 1 + Assert.AreEqual(1, result.Result); + //Equivalent to + Assert.AreEqual(2+i-7, result.Result); + } + } + } + [TestMethod] public void WeekNumShouldReturnCorrectResult() { From 2b0955a5a97352416313b09e898f881c6e123266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ossian=20Edstr=C3=B6m?= Date: Thu, 12 Mar 2026 16:17:35 +0100 Subject: [PATCH 2/4] Simpler solution --- .../Excel/Functions/DateAndTime/Weekday.cs | 30 ++++----- .../Excel/Functions/DateTimeFunctionsTests.cs | 64 +++++++++++++------ 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs b/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs index e1416e9b13..bc7d59c4d1 100644 --- a/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs +++ b/src/EPPlus/FormulaParsing/Excel/Functions/DateAndTime/Weekday.cs @@ -73,32 +73,26 @@ private int CalculateDayOfWeek(DateTime dateTime, int returnType) case 3: return _zeroBasedStartOnSunday[dayIx]; case 12: - return _oneBasedStartOnMonday[GetIndex(dayIx - 1)]; case 13: - return _oneBasedStartOnMonday[GetIndex(dayIx - 2)]; case 14: - return _oneBasedStartOnMonday[GetIndex(dayIx - 3)]; case 15: - return _oneBasedStartOnMonday[GetIndex(dayIx - 4)]; case 16: - return _oneBasedStartOnMonday[GetIndex(dayIx - 5)]; case 17: - return _oneBasedStartOnMonday[GetIndex(dayIx - 6)]; + var dayIxForArr0 = returnType - 11; + + //Calculate distance/index from expected 0 + var idx = dayIx - dayIxForArr0; + + //If index is negative count backwards from end of array + if (idx < 0) + { + idx = _oneBasedStartOnMonday.Count + idx; + } + + return _oneBasedStartOnMonday[idx]; default: throw new ExcelErrorValueException(eErrorType.Num); } } - - int GetIndex(int indexPotentiallyNegative) - { - if(indexPotentiallyNegative >= 0) - { - return indexPotentiallyNegative; - } - else - { - return _oneBasedStartOnMonday.Count + indexPotentiallyNegative; - } - } } } diff --git a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs index ed989f4ade..cf7bf97e20 100644 --- a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs +++ b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs @@ -345,34 +345,62 @@ public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs12() Assert.AreEqual(4, result6.Result); } - [TestMethod] - public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs17ForAllDays() + List CreateListOfOaDates() { - var func = new Weekday(); - List weekOfOADates = new List(); - for(int i = 0; i< 7; i++) + for (int i = 0; i < 7; i++) { weekOfOADates.Add(new DateTime(2026, 3, 9 + i).ToOADate()); } - for(int i = 0; i< 7; i++) + return weekOfOADates; + } + + [TestMethod] + public void WeekdayShouldReturnCorrectResultyWhenTypeIs12ForAllDays() + { + var allWeekdays = CreateListOfOaDates(); + var func = new Weekday(); + + List results = new List(); + + for (int i = 0; i < 7; i++) { - var result = func.Execute(FunctionsHelper.CreateArgs(weekOfOADates[i], 17), _parsingContext); + var result = func.Execute(FunctionsHelper.CreateArgs(allWeekdays[i], 12), _parsingContext); + results.Add(result); + } - if(2 + i <= 7) - { - Assert.AreEqual(2 + i, result.Result); - } - else - { - //only happens for 2+6=8 go back to 1 - Assert.AreEqual(1, result.Result); - //Equivalent to - Assert.AreEqual(2+i-7, result.Result); - } + Assert.AreEqual(7, results[0].Result); + Assert.AreEqual(1, results[1].Result); + Assert.AreEqual(2, results[2].Result); + Assert.AreEqual(3, results[3].Result); + Assert.AreEqual(4, results[4].Result); + Assert.AreEqual(5, results[5].Result); + Assert.AreEqual(6, results[6].Result); + } + + [TestMethod] + public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs17ForAllDays() + { + var allWeekdays = CreateListOfOaDates(); + var func = new Weekday(); + + List results = new List(); + + for (int i = 0; i < 7; i++) + { + var result = func.Execute(FunctionsHelper.CreateArgs(allWeekdays[i], 17), _parsingContext); + results.Add(result); } + + Assert.AreEqual(2, results[0].Result); + Assert.AreEqual(3, results[1].Result); + Assert.AreEqual(4, results[2].Result); + Assert.AreEqual(5, results[3].Result); + Assert.AreEqual(6, results[4].Result); + Assert.AreEqual(7, results[5].Result); + Assert.AreEqual(1, results[6].Result); } [TestMethod] From b121e5f0f49c56a34476216576b18f3cd19bf8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ossian=20Edstr=C3=B6m?= Date: Thu, 12 Mar 2026 16:43:33 +0100 Subject: [PATCH 3/4] Added better testing to cover every possible option --- .../Excel/Functions/DateTimeFunctionsTests.cs | 171 +++++++++++------- 1 file changed, 105 insertions(+), 66 deletions(-) diff --git a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs index cf7bf97e20..c7a292396a 100644 --- a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs +++ b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs @@ -189,7 +189,7 @@ public void TimeShouldReturnACorrectSerialNumber() var expectedResult = GetTime(10, 11, 12); var func = new Time(); var result = func.Execute(FunctionsHelper.CreateArgs(10, 11, 12), _parsingContext); - Assert.AreEqual(expectedResult, result.Result); + Assert.AreEqual(expectedResult, result.Result); } [TestMethod] @@ -303,104 +303,143 @@ public void WeekdayShouldReturnCorrectResultForASundayWhenReturnTypeIs3() Assert.AreEqual(6, result.Result); } + static readonly Dictionary MondayToSundayOADict = new Dictionary() + { + {"Monday", new DateTime(2026, 3, 9).ToOADate()}, + {"Tuesday", new DateTime(2026, 3, 10).ToOADate()}, + {"Wednesday", new DateTime(2026, 3, 11).ToOADate()}, + {"Thursday", new DateTime(2026, 3, 12).ToOADate()}, + {"Friday", new DateTime(2026, 3, 13).ToOADate()}, + {"Saturday", new DateTime(2026, 3, 14).ToOADate()}, + {"Sunday", new DateTime(2026, 3, 15).ToOADate()} + + }; + [TestMethod] - public void WeekdayShouldReturnCorrectResultForAThursdayWhenTypeIs12() + [DataRow("Monday", 1)] + [DataRow("Tuesday", 2)] + [DataRow("Wednesday", 3)] + [DataRow("Thursday", 4)] + [DataRow("Friday", 5)] + [DataRow("Saturday", 6)] + [DataRow("Sunday", 7)] + public void WeekdayWhenTypeIs11ForAllDays(string day, int expectedResult) { var func = new Weekday(); - var date = new DateTime(2026, 3, 12).ToOADate(); - var result = func.Execute(FunctionsHelper.CreateArgs(date, 12), _parsingContext); - var result2 = func.Execute(FunctionsHelper.CreateArgs(date, 13), _parsingContext); - var result3 = func.Execute(FunctionsHelper.CreateArgs(date, 14), _parsingContext); - var result4 = func.Execute(FunctionsHelper.CreateArgs(date, 15), _parsingContext); - var result5 = func.Execute(FunctionsHelper.CreateArgs(date, 16), _parsingContext); - var result6 = func.Execute(FunctionsHelper.CreateArgs(date, 17), _parsingContext); + var oaDate = MondayToSundayOADict[day]; + var result = func.Execute(FunctionsHelper.CreateArgs(oaDate, 11), _parsingContext); - Assert.AreEqual(3, result.Result); - Assert.AreEqual(2, result2.Result); - Assert.AreEqual(1, result3.Result); - Assert.AreEqual(7, result4.Result); - Assert.AreEqual(6, result5.Result); - Assert.AreEqual(5, result6.Result); + Assert.AreEqual(expectedResult, result.Result); } [TestMethod] - public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs12() + [DataRow("Monday", 7)] + [DataRow("Tuesday", 1)] + [DataRow("Wednesday", 2)] + [DataRow("Thursday", 3)] + [DataRow("Friday", 4)] + [DataRow("Saturday", 5)] + [DataRow("Sunday", 6)] + public void WeekdayWhenTypeIs12ForAllDays(string day, int expectedResult) { var func = new Weekday(); - var date = new DateTime(2026, 3, 11).ToOADate(); - var result = func.Execute(FunctionsHelper.CreateArgs(date, 12), _parsingContext); - var result2 = func.Execute(FunctionsHelper.CreateArgs(date, 13), _parsingContext); - var result3 = func.Execute(FunctionsHelper.CreateArgs(date, 14), _parsingContext); - var result4 = func.Execute(FunctionsHelper.CreateArgs(date, 15), _parsingContext); - var result5 = func.Execute(FunctionsHelper.CreateArgs(date, 16), _parsingContext); - var result6 = func.Execute(FunctionsHelper.CreateArgs(date, 17), _parsingContext); + var oaDate = MondayToSundayOADict[day]; + var result = func.Execute(FunctionsHelper.CreateArgs(oaDate, 12), _parsingContext); - Assert.AreEqual(2, result.Result); - Assert.AreEqual(1, result2.Result); - Assert.AreEqual(7, result3.Result); - Assert.AreEqual(6, result4.Result); - Assert.AreEqual(5, result5.Result); - Assert.AreEqual(4, result6.Result); + Assert.AreEqual(expectedResult, result.Result); } - List CreateListOfOaDates() + [TestMethod] + [DataRow("Monday", 6)] + [DataRow("Tuesday", 7)] + [DataRow("Wednesday", 1)] + [DataRow("Thursday", 2)] + [DataRow("Friday", 3)] + [DataRow("Saturday", 4)] + [DataRow("Sunday", 5)] + public void WeekdayWhenTypeIs13ForAllDays(string day, int expectedResult) { - List weekOfOADates = new List(); + var func = new Weekday(); - for (int i = 0; i < 7; i++) - { - weekOfOADates.Add(new DateTime(2026, 3, 9 + i).ToOADate()); - } + var oaDate = MondayToSundayOADict[day]; + var result = func.Execute(FunctionsHelper.CreateArgs(oaDate, 13), _parsingContext); - return weekOfOADates; + Assert.AreEqual(expectedResult, result.Result); } [TestMethod] - public void WeekdayShouldReturnCorrectResultyWhenTypeIs12ForAllDays() + [DataRow("Monday", 5)] + [DataRow("Tuesday", 6)] + [DataRow("Wednesday", 7)] + [DataRow("Thursday", 1)] + [DataRow("Friday", 2)] + [DataRow("Saturday", 3)] + [DataRow("Sunday", 4)] + public void WeekdayWhenTypeIs14ForAllDays(string day, int expectedResult) { - var allWeekdays = CreateListOfOaDates(); var func = new Weekday(); - List results = new List(); + var oaDate = MondayToSundayOADict[day]; + var result = func.Execute(FunctionsHelper.CreateArgs(oaDate, 14), _parsingContext); + + Assert.AreEqual(expectedResult, result.Result); + } - for (int i = 0; i < 7; i++) - { - var result = func.Execute(FunctionsHelper.CreateArgs(allWeekdays[i], 12), _parsingContext); - results.Add(result); - } - Assert.AreEqual(7, results[0].Result); - Assert.AreEqual(1, results[1].Result); - Assert.AreEqual(2, results[2].Result); - Assert.AreEqual(3, results[3].Result); - Assert.AreEqual(4, results[4].Result); - Assert.AreEqual(5, results[5].Result); - Assert.AreEqual(6, results[6].Result); + [TestMethod] + [DataRow("Monday", 4)] + [DataRow("Tuesday", 5)] + [DataRow("Wednesday", 6)] + [DataRow("Thursday", 7)] + [DataRow("Friday", 1)] + [DataRow("Saturday", 2)] + [DataRow("Sunday", 3)] + public void WeekdayWhenTypeIs15ForAllDays(string day, int expectedResult) + { + var func = new Weekday(); + + var oaDate = MondayToSundayOADict[day]; + var result = func.Execute(FunctionsHelper.CreateArgs(oaDate, 15), _parsingContext); + + Assert.AreEqual(expectedResult, result.Result); } [TestMethod] - public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs17ForAllDays() + [DataRow("Monday", 3)] + [DataRow("Tuesday", 4)] + [DataRow("Wednesday", 5)] + [DataRow("Thursday", 6)] + [DataRow("Friday", 7)] + [DataRow("Saturday", 1)] + [DataRow("Sunday", 2)] + public void WeekdayWhenTypeIs16ForAllDays(string day, int expectedResult) { - var allWeekdays = CreateListOfOaDates(); var func = new Weekday(); - List results = new List(); + var oaDate = MondayToSundayOADict[day]; + var result = func.Execute(FunctionsHelper.CreateArgs(oaDate, 16), _parsingContext); - for (int i = 0; i < 7; i++) - { - var result = func.Execute(FunctionsHelper.CreateArgs(allWeekdays[i], 17), _parsingContext); - results.Add(result); - } + Assert.AreEqual(expectedResult, result.Result); + } + + [TestMethod] + [DataRow("Monday", 2)] + [DataRow("Tuesday", 3)] + [DataRow("Wednesday", 4)] + [DataRow("Thursday", 5)] + [DataRow("Friday", 6)] + [DataRow("Saturday", 7)] + [DataRow("Sunday", 1)] + public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs17ForAllDays(string day, int expectedResult) + { + var func = new Weekday(); - Assert.AreEqual(2, results[0].Result); - Assert.AreEqual(3, results[1].Result); - Assert.AreEqual(4, results[2].Result); - Assert.AreEqual(5, results[3].Result); - Assert.AreEqual(6, results[4].Result); - Assert.AreEqual(7, results[5].Result); - Assert.AreEqual(1, results[6].Result); + var oaDate = MondayToSundayOADict[day]; + var result = func.Execute(FunctionsHelper.CreateArgs(oaDate, 17), _parsingContext); + + Assert.AreEqual(expectedResult, result.Result); } [TestMethod] From 3a78513b34955176a9e1638a816430c885968a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ossian=20Edstr=C3=B6m?= Date: Thu, 12 Mar 2026 16:53:12 +0100 Subject: [PATCH 4/4] fixed test name to match others --- .../FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs index c7a292396a..ed85feea02 100644 --- a/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs +++ b/src/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs @@ -432,7 +432,7 @@ public void WeekdayWhenTypeIs16ForAllDays(string day, int expectedResult) [DataRow("Friday", 6)] [DataRow("Saturday", 7)] [DataRow("Sunday", 1)] - public void WeekdayShouldReturnCorrectResultForAWednesdayWhenTypeIs17ForAllDays(string day, int expectedResult) + public void WeekdayWhenTypeIs17ForAllDays(string day, int expectedResult) { var func = new Weekday();