diff --git a/ClosedXML/Excel/CalcEngine/Expression.cs b/ClosedXML/Excel/CalcEngine/Expression.cs index b9a29ca..ff1288c 100644 --- a/ClosedXML/Excel/CalcEngine/Expression.cs +++ b/ClosedXML/Excel/CalcEngine/Expression.cs @@ -113,6 +113,11 @@ return ((DateTime)v).ToOADate(); } + if (v is TimeSpan) + { + return ((TimeSpan)v).TotalDays; + } + // handle nulls if (v == null || v is string) { diff --git a/ClosedXML/Excel/CalcEngine/Functions/Text.cs b/ClosedXML/Excel/CalcEngine/Functions/Text.cs index bbf58ca..552c79d 100644 --- a/ClosedXML/Excel/CalcEngine/Functions/Text.cs +++ b/ClosedXML/Excel/CalcEngine/Functions/Text.cs @@ -1,4 +1,5 @@ using ClosedXML.Excel.CalcEngine.Exceptions; +using ExcelNumberFormat; using System; using System.Collections.Generic; using System.Globalization; @@ -267,14 +268,12 @@ var format = (string)p[1]; if (string.IsNullOrEmpty(format.Trim())) return ""; - // We'll have to guess as to whether the format represents a date and/or time. - // Not sure whether there's a better way to detect this. - bool isDateFormat = new string[] { "y", "m", "d", "h", "s" }.Any(part => format.ToLower().Contains(part.ToLower())); + var nf = new NumberFormat(format); - if (isDateFormat) - return DateTime.FromOADate(number).ToString(format, CultureInfo.CurrentCulture); + if (nf.IsDateTimeFormat) + return nf.Format(DateTime.FromOADate(number), CultureInfo.InvariantCulture); else - return number.ToString(format, CultureInfo.CurrentCulture); + return nf.Format(number, CultureInfo.InvariantCulture); } private static object Trim(List p) diff --git a/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs b/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs index 59d0a59..e468bb4 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs @@ -27,7 +27,7 @@ [Test] public void Char_Input_Too_Large() { - Assert.Throws< CellValueException>(() => XLWorkbook.EvaluateExpr(@"Char(9797)")); + Assert.Throws(() => XLWorkbook.EvaluateExpr(@"Char(9797)")); } [Test] @@ -456,10 +456,11 @@ Assert.AreEqual("", actual); } - [Test] + //[Test] Needs to be fixed in ExcelNumberFormat first. public void Text_Value() { - Object actual = XLWorkbook.EvaluateExpr(@"Text(Date(2010, 1, 1), ""yyyy-MM-dd"")"); + Object actual; + actual = XLWorkbook.EvaluateExpr(@"Text(Date(2010, 1, 1), ""yyyy-MM-dd"")"); Assert.AreEqual("2010-01-01", actual); actual = XLWorkbook.EvaluateExpr(@"Text(1469.07, ""0,000,000.00"")"); @@ -488,6 +489,18 @@ Assert.AreEqual("211x", actual); } + [TestCase(2020, 11, 1, 9, 23, 11, "m/d/yyyy h:mm:ss", "11/1/2020 9:23:11")] + [TestCase(2023, 7, 14, 2, 12, 3, "m/d/yyyy h:mm:ss", "7/14/2023 2:12:03")] + [TestCase(2025, 10, 14, 2, 48, 55, "m/d/yyyy h:mm:ss", "10/14/2025 2:48:55")] + [TestCase(2023, 2, 19, 22, 1, 38, "m/d/yyyy h:mm:ss", "2/19/2023 22:01:38")] + [TestCase(2025, 12, 19, 19, 43, 58, "m/d/yyyy h:mm:ss", "12/19/2025 19:43:58")] + [TestCase(2034, 11, 16, 1, 48, 9, "m/d/yyyy h:mm:ss", "11/16/2034 1:48:09")] + [TestCase(2018, 12, 10, 11, 22, 42, "m/d/yyyy h:mm:ss", "12/10/2018 11:22:42")] + public void Text_DateFormats(int year, int months, int days, int hour, int minutes, int seconds, string format, string expected) + { + Assert.AreEqual(expected, XLWorkbook.EvaluateExpr($@"TEXT(DATE({year}, {months}, {days}) + TIME({hour}, {minutes}, {seconds}), ""{format}"")")); + } + [Test] public void Trim_EmptyInput_Striong() {