diff --git a/src/EPPlus/Utils/TypeConversion/ConvertUtil.cs b/src/EPPlus/Utils/TypeConversion/ConvertUtil.cs index a582adf09c..9eb5ccdf4e 100644 --- a/src/EPPlus/Utils/TypeConversion/ConvertUtil.cs +++ b/src/EPPlus/Utils/TypeConversion/ConvertUtil.cs @@ -543,6 +543,17 @@ private static bool Matches(char c, ref int ix) } } + private static TimeSpan GetTimespanTicks(DateTime dt) + { + var startDate = DateTime.FromOADate(0); + long ticks = dt.Ticks; + if(dt.Ticks > startDate.Date.Ticks) + { + ticks = dt.Ticks - startDate.Ticks; + } + return new TimeSpan(ticks); + } + /// /// Convert cell value to desired type, including nullable structs. /// When converting blank string to nullable struct (e.g. ' ' to int?) null is returned. @@ -621,7 +632,7 @@ internal static T GetTypedCellValueInner(object value, bool returnDefaultIfEx TimeSpan? ts = null; if (value is DateTime dt) { - ts = new TimeSpan((long)(dt.ToOADate() * TimeSpan.TicksPerDay)); + ts = GetTimespanTicks(dt); } #if (NET8_0_OR_GREATER) else if (value is TimeOnly timeOnly) @@ -630,7 +641,7 @@ internal static T GetTypedCellValueInner(object value, bool returnDefaultIfEx } else if (value is DateOnly dateOnly) { - ts=new TimeSpan((long)dateOnly.ToDateTime(TimeOnly.MinValue).ToOADate() * TimeSpan.TicksPerDay); + ts = GetTimespanTicks(dateOnly.ToDateTime(TimeOnly.MinValue)); } #endif else if (conversion.TryGetTimeSpan(out object tso)) diff --git a/src/EPPlusTest/Core/Range/RangeTest.cs b/src/EPPlusTest/Core/Range/RangeTest.cs index 681fb45652..60899e3a1c 100644 --- a/src/EPPlusTest/Core/Range/RangeTest.cs +++ b/src/EPPlusTest/Core/Range/RangeTest.cs @@ -31,6 +31,7 @@ Date Author Change using OfficeOpenXml; using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; using OfficeOpenXml.Style; +using OfficeOpenXml.Utils.TypeConversion; using System; using System.Collections.Generic; using System.Diagnostics; @@ -374,6 +375,42 @@ public void RangeIsEmptyStyleTests() Assert.IsTrue(isEmpty); } + [TestMethod] + public void i2558() + { + using(var p = OpenTemplatePackage("TestTime.xlsx")) + { + var worksheet = p.Workbook.Worksheets.First(); + var cell = worksheet.Cells[1, 1]; + + var rawValue = cell.Value; + var valueAsString = cell.GetValue(); + var valueAsTimeSpan = cell.GetValue(); + + Console.WriteLine($"Raw value : {cell.Value}"); + Console.WriteLine($"Raw type : {cell.Value?.GetType()}"); + Console.WriteLine($"Number format : {cell.Style.Numberformat.Format}"); + + var time = cell.GetValue(); + + Console.WriteLine($"TimeSpan : {time:c}"); + Console.WriteLine($"Hours : {time.Hours}"); + Console.WriteLine($"Minutes : {time.Minutes}"); + Console.WriteLine($"Seconds : {time.Seconds}"); + Console.WriteLine($"Ticks : {time.Ticks}"); + + var expected = new TimeSpan(6, 10, 0); + var matches = + valueAsTimeSpan.Hours == expected.Hours && + valueAsTimeSpan.Minutes == expected.Minutes && + valueAsTimeSpan.Seconds == expected.Seconds; + + Assert.AreEqual(expected, valueAsTimeSpan); + //Console.WriteLine($"Actual : {valueAsTimeSpan:hh\\:mm\\:ss}"); + //Console.WriteLine($"Expected : {expected:hh\\:mm\\:ss}"); + //Console.WriteLine(matches ? "PASS" : "FAIL"); + } + } #if NET6_0_OR_GREATER [TestMethod]