diff --git a/src/MiniExcel/Attributes/ExcelColumnAttribute.cs b/src/MiniExcel/Attributes/ExcelColumnAttribute.cs index d7337418..965f0579 100644 --- a/src/MiniExcel/Attributes/ExcelColumnAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelColumnAttribute.cs @@ -9,6 +9,8 @@ public class ExcelColumnAttribute : Attribute private int _index = -1; private string _xName; + internal int FormatId { get; set; } = -1; + public string Name { get; set; } public string[] Aliases { get; set; } @@ -52,6 +54,7 @@ private void Init(int index, string columnName = null) public class DynamicExcelColumn : ExcelColumnAttribute { public string Key { get; set; } + public DynamicExcelColumn(string key) { Key = key; diff --git a/src/MiniExcel/OpenXml/Constants/ExcelXml.cs b/src/MiniExcel/OpenXml/Constants/ExcelXml.cs index a4efba29..b1a0d7a5 100644 --- a/src/MiniExcel/OpenXml/Constants/ExcelXml.cs +++ b/src/MiniExcel/OpenXml/Constants/ExcelXml.cs @@ -1,4 +1,9 @@ -using MiniExcelLibs.OpenXml.Models; +using MiniExcelLibs.Attributes; +using MiniExcelLibs.OpenXml.Models; +using MiniExcelLibs.Utils; +using System.Collections.Generic; +using System.Linq; +using System.Text; namespace MiniExcelLibs.OpenXml.Constants { @@ -52,10 +57,21 @@ static ExcelXml() "; - internal static readonly string DefaultStylesXml = @" + #region StyleSheet + + private const int startUpNumFmts = 1; + private const string NumFmtsToken = "{{numFmts}}"; + private const string NumFmtsCountToken = "{{numFmtCount}}"; + + private const int startUpCellXfs = 5; + private const string cellXfsToken = "{{cellXfs}}"; + private const string cellXfsCountToken = "{{cellXfsCount}}"; + + internal static readonly string DefaultStylesXml = $@" - + + {NumFmtsToken} @@ -133,7 +149,7 @@ static ExcelXml() "; + #endregion + internal static readonly string DefaultWorkbookXml = @" @@ -231,5 +250,52 @@ internal static string DrawingXml(FileDto file, int fileIndex) internal static string Sheet(SheetDto sheetDto, int sheetId) => $@""; + + internal static string SetupStyleXml(string styleXml, ICollection columns) + { + const int numFmtIndex = 166; + + var sb = new StringBuilder(styleXml); + var columnsToApply = GenerateStyleIds(columns); + + var numFmts = columnsToApply.Select((x, i) => + { + return new + { + numFmt = +$@"", + + cellXfs = +$@" + + " + }; + }).ToArray(); + + sb.Replace(NumFmtsToken, string.Join(string.Empty, numFmts.Select(x => x.numFmt))); + sb.Replace(NumFmtsCountToken, (startUpNumFmts + numFmts.Length).ToString()); + + sb.Replace(cellXfsToken, string.Join(string.Empty, numFmts.Select(x => x.cellXfs))); + sb.Replace(cellXfsCountToken, (5 + numFmts.Length).ToString()); + return sb.ToString(); + } + + private static IEnumerable GenerateStyleIds(ICollection dynamicColumns) + { + if (dynamicColumns == null) + yield break; + + int index = 0; + foreach (var g in dynamicColumns?.Where(x => !string.IsNullOrWhiteSpace(x.Format) && new ExcelNumberFormat(x.Format).IsValid).GroupBy(x => x.Format)) + { + foreach (var col in g) + col.FormatId = startUpCellXfs + index; + + yield return g.First(); + index++; + } + } + } } diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs index edd0117e..80e85e38 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs @@ -36,6 +36,7 @@ internal async Task GenerateDefaultOpenXmlAsync(CancellationToken cancellationTo { await CreateZipEntryAsync(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels, cancellationToken); await CreateZipEntryAsync(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString, cancellationToken); + await GenerateStylesXmlAsync(cancellationToken); } private async Task CreateSheetXmlAsync(object value, string sheetPath, CancellationToken cancellationToken) @@ -47,27 +48,27 @@ private async Task CreateSheetXmlAsync(object value, string sheetPath, Cancellat if (value == null) { await WriteEmptySheetAsync(writer); - goto End; //for re-using code } - - //DapperRow - - switch (value) + else { - case IDataReader dataReader: - await GenerateSheetByIDataReaderAsync(writer, dataReader); - break; - case IEnumerable enumerable: - await GenerateSheetByEnumerableAsync(writer, enumerable); - break; - case DataTable dataTable: - await GenerateSheetByDataTableAsync(writer, dataTable); - break; - default: - throw new NotImplementedException($"Type {value.GetType().FullName} is not implemented. Please open an issue."); + //DapperRow + + switch (value) + { + case IDataReader dataReader: + await GenerateSheetByIDataReaderAsync(writer, dataReader); + break; + case IEnumerable enumerable: + await GenerateSheetByEnumerableAsync(writer, enumerable); + break; + case DataTable dataTable: + await GenerateSheetByDataTableAsync(writer, dataTable); + break; + default: + throw new NotImplementedException($"Type {value.GetType().FullName} is not implemented. Please open an issue."); + } } } - End: //for re-using code _zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, ExcelContentTypes.Worksheet)); } @@ -455,8 +456,6 @@ private async Task GenerateEndXmlAsync(CancellationToken cancellationToken) { await AddFilesToZipAsync(cancellationToken); - await GenerateStylesXmlAsync(cancellationToken); - await GenerateDrawinRelXmlAsync(cancellationToken); await GenerateDrawingXmlAsync(cancellationToken); @@ -479,7 +478,7 @@ private async Task AddFilesToZipAsync(CancellationToken cancellationToken) /// private async Task GenerateStylesXmlAsync(CancellationToken cancellationToken) { - var styleXml = GetStylesXml(); + var styleXml = GetStylesXml(_configuration.DynamicColumns); await CreateZipEntryAsync( ExcelFileNames.Styles, diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs index 16ead3ae..3ec3283f 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs @@ -1,4 +1,5 @@ -using MiniExcelLibs.OpenXml.Constants; +using MiniExcelLibs.Attributes; +using MiniExcelLibs.OpenXml.Constants; using MiniExcelLibs.OpenXml.Models; using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; @@ -108,6 +109,7 @@ private ExcelColumnInfo GetColumnInfosFromDynamicConfiguration(string columnName if (dynamicColumn.Format != null) { prop.ExcelFormat = dynamicColumn.Format; + prop.ExcelFormatId = dynamicColumn.FormatId; } if (dynamicColumn.Aliases != null) @@ -158,14 +160,26 @@ private Tuple GetCellValue(int rowIndex, int cellIndex, return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(str)); } - if (columnInfo?.ExcelFormat != null && value is IFormattable formattableValue) + var type = GetValueType(value, columnInfo); + + + if (columnInfo?.ExcelFormat != null && columnInfo?.ExcelFormatId == -1 && value is IFormattable formattableValue) { var formattedStr = formattableValue.ToString(columnInfo.ExcelFormat, _configuration.Culture); return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(formattedStr)); } - var type = GetValueType(value, columnInfo); + if (type == typeof(DateTime)) + { + return GetDateTimeValue((DateTime)value, columnInfo); + } +#if NET6_0_OR_GREATER + if (type == typeof(DateOnly)) + { + return GetDateTimeValue(((DateOnly)value).ToDateTime(new TimeOnly()), columnInfo); + } +#endif if (type.IsEnum) { var description = CustomPropertyHelper.DescriptionAttr(type, value); @@ -193,33 +207,6 @@ private Tuple GetCellValue(int rowIndex, int cellIndex, return Tuple.Create("4", "str", ExcelOpenXmlUtils.EncodeXML(base64)); } - if (type == typeof(DateTime)) - { - return GetDateTimeValue(value, columnInfo); - } - -#if NET6_0_OR_GREATER - if (type == typeof(DateOnly)) - { - if (_configuration.Culture != CultureInfo.InvariantCulture) - { - var cellValue = ((DateOnly)value).ToString(_configuration.Culture); - return Tuple.Create("2", "str", cellValue); - } - - if (columnInfo == null || columnInfo.ExcelFormat == null) - { - var oaDate = CorrectDateTimeValue((DateTime)value); - var cellValue = oaDate.ToString(CultureInfo.InvariantCulture); - return Tuple.Create("3", null, cellValue); - } - - // TODO: now it'll lose date type information - var formattedCellValue = ((DateOnly)value).ToString(columnInfo.ExcelFormat, _configuration.Culture); - return Tuple.Create("2", "str", formattedCellValue); - } -#endif - return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(value.ToString())); } @@ -325,24 +312,21 @@ private string GetFileValue(int rowIndex, int cellIndex, object value) return base64; } - private Tuple GetDateTimeValue(object value, ExcelColumnInfo columnInfo) + private Tuple GetDateTimeValue(DateTime value, ExcelColumnInfo columnInfo) { + string cellValue = null; if (_configuration.Culture != CultureInfo.InvariantCulture) { - var cellValue = ((DateTime)value).ToString(_configuration.Culture); + cellValue = (value).ToString(_configuration.Culture); return Tuple.Create("2", "str", cellValue); } + var oaDate = CorrectDateTimeValue(value); + cellValue = oaDate.ToString(CultureInfo.InvariantCulture); if (columnInfo == null || columnInfo.ExcelFormat == null) - { - var oaDate = CorrectDateTimeValue((DateTime)value); - var cellValue = oaDate.ToString(CultureInfo.InvariantCulture); return Tuple.Create("3", null, cellValue); - } - - // TODO: now it'll lose date type information - var formattedCellValue = ((DateTime)value).ToString(columnInfo.ExcelFormat, _configuration.Culture); - return Tuple.Create("2", "str", formattedCellValue); + else + return Tuple.Create(columnInfo.ExcelFormatId.ToString(), (string)null, cellValue); } private static double CorrectDateTimeValue(DateTime value) @@ -375,14 +359,14 @@ private string GetDimensionRef(int maxRowIndex, int maxColumnIndex) return dimensionRef; } - private string GetStylesXml() + private string GetStylesXml(ICollection columns) { switch (_configuration.TableStyles) { case TableStyles.None: - return ExcelXml.NoneStylesXml; + return ExcelXml.SetupStyleXml(ExcelXml.NoneStylesXml, columns); case TableStyles.Default: - return ExcelXml.DefaultStylesXml; + return ExcelXml.SetupStyleXml(ExcelXml.DefaultStylesXml, columns); default: return string.Empty; } diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs index 07a7e267..79e211d6 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs @@ -1,4 +1,5 @@ -using MiniExcelLibs.OpenXml.Constants; +using MiniExcelLibs.Attributes; +using MiniExcelLibs.OpenXml.Constants; using MiniExcelLibs.OpenXml.Models; using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; @@ -68,6 +69,7 @@ internal void GenerateDefaultOpenXml() { CreateZipEntry(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels); CreateZipEntry(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString); + GenerateStylesXml(); } private void CreateSheetXml(object value, string sheetPath) @@ -79,29 +81,29 @@ private void CreateSheetXml(object value, string sheetPath) if (value == null) { WriteEmptySheet(writer); - goto End; //for re-using code - } - - //DapperRow - - if (value is IDataReader) - { - GenerateSheetByIDataReader(writer, value as IDataReader); - } - else if (value is IEnumerable) - { - GenerateSheetByEnumerable(writer, value as IEnumerable); - } - else if (value is DataTable) - { - GenerateSheetByDataTable(writer, value as DataTable); } else { - throw new NotImplementedException($"Type {value.GetType().FullName} is not implemented. Please open an issue."); + //DapperRow + + if (value is IDataReader) + { + GenerateSheetByIDataReader(writer, value as IDataReader); + } + else if (value is IEnumerable) + { + GenerateSheetByEnumerable(writer, value as IEnumerable); + } + else if (value is DataTable) + { + GenerateSheetByDataTable(writer, value as DataTable); + } + else + { + throw new NotImplementedException($"Type {value.GetType().FullName} is not implemented. Please open an issue."); + } } } - End: //for re-using code _zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, ExcelContentTypes.Worksheet)); } @@ -151,7 +153,7 @@ private void GenerateSheetByIDataReader(MiniExcelStreamWriter writer, IDataReade for (int i = 0; i < fieldCount; i++) { var cellValue = reader.GetValue(i); - WriteCell(writer, yIndex, xIndex, cellValue, columnInfo: null); + WriteCell(writer, yIndex, xIndex, cellValue, columnInfo: props?.FirstOrDefault(x => x?.ExcelColumnIndex == xIndex - 1)); xIndex++; } writer.Write(WorksheetXml.EndRow); @@ -356,7 +358,7 @@ private void GenerateSheetByDataTable(MiniExcelStreamWriter writer, DataTable va for (int j = 0; j < value.Columns.Count; j++) { var cellValue = value.Rows[i][j]; - WriteCell(writer, yIndex, xIndex, cellValue, columnInfo: null); + WriteCell(writer, yIndex, xIndex, cellValue, columnInfo: props?.FirstOrDefault(x => x?.ExcelColumnIndex == xIndex - 1)); xIndex++; } writer.Write(WorksheetXml.EndRow); @@ -454,7 +456,7 @@ private int GenerateSheetByColumnInfo(MiniExcelStreamWriter writer, IEnumerat return yIndex - 1; } - + private void WriteCell(MiniExcelStreamWriter writer, int rowIndex, int cellIndex, object value, ExcelColumnInfo columnInfo) { var columnReference = ExcelOpenXmlUtils.ConvertXyToCell(cellIndex, rowIndex); @@ -484,8 +486,6 @@ private void GenerateEndXml() { AddFilesToZip(); - GenerateStylesXml(); - GenerateDrawinRelXml(); GenerateDrawingXml(); @@ -508,7 +508,7 @@ private void AddFilesToZip() /// private void GenerateStylesXml() { - var styleXml = GetStylesXml(); + var styleXml = GetStylesXml(_configuration.DynamicColumns); CreateZipEntry(ExcelFileNames.Styles, ExcelContentTypes.Styles, styleXml); } @@ -600,4 +600,4 @@ private void CreateZipEntry(string path, byte[] content) } } } -} \ No newline at end of file +} diff --git a/src/MiniExcel/Utils/CustomPropertyHelper.cs b/src/MiniExcel/Utils/CustomPropertyHelper.cs index 7fa06114..fa7f164f 100644 --- a/src/MiniExcel/Utils/CustomPropertyHelper.cs +++ b/src/MiniExcel/Utils/CustomPropertyHelper.cs @@ -23,6 +23,7 @@ internal class ExcelColumnInfo public double? ExcelColumnWidth { get; internal set; } public string ExcelIndexName { get; internal set; } public bool ExcelIgnore { get; internal set; } + public int ExcelFormatId { get; internal set; } } internal class ExcellSheetInfo @@ -205,6 +206,7 @@ private static IEnumerable ConvertToExcelCustomPropertyInfo(Pro ExcelIndexName = p.GetAttribute()?.ExcelXName ?? excelColumn?.IndexName, ExcelColumnWidth = p.GetAttribute()?.ExcelColumnWidth ?? excelColumn?.Width, ExcelFormat = excelFormat ?? excelColumn?.Format, + ExcelFormatId = excelColumn?.FormatId ?? -1 }; }).Where(_ => _ != null); } @@ -292,7 +294,10 @@ internal static void SetDictionaryColumnInfo(List _props, objec p.Nullable = true; //p.ExcludeNullableType = item2[key]?.GetType(); if (dynamicColumn.Format != null) + { p.ExcelFormat = dynamicColumn.Format; + p.ExcelFormatId = dynamicColumn.FormatId; + } if (dynamicColumn.Aliases != null) p.ExcelColumnAliases = dynamicColumn.Aliases; if (dynamicColumn.IndexName != null) diff --git a/tests/MiniExcelTests/MiniExcelIssueTests.cs b/tests/MiniExcelTests/MiniExcelIssueTests.cs index 220d538f..498fa29f 100644 --- a/tests/MiniExcelTests/MiniExcelIssueTests.cs +++ b/tests/MiniExcelTests/MiniExcelIssueTests.cs @@ -309,7 +309,7 @@ public void TestIssue370() var rows = MiniExcel.Query(path, false).ToList(); Assert.Equal("createdate", rows[0].A); - Assert.Equal("2022-04-12", rows[1].A); + Assert.Equal(new DateTime(2022, 04, 12), rows[1].A); Assert.Equal("name", rows[0].B); Assert.Equal("Jack", rows[1].B); Assert.Equal("Account Point", rows[0].C); @@ -334,7 +334,7 @@ public void TestIssue369() var rows = MiniExcel.Query(path, false).ToList(); Assert.Equal("createdate", rows[0].A); - Assert.Equal("2022-04-12", rows[1].A); + Assert.Equal(new DateTime(2022, 04, 12), rows[1].A); Assert.Equal("name", rows[0].B); Assert.Equal("Jack", rows[1].B); Assert.Equal("Account Point", rows[0].C); diff --git a/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs b/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs index d2cae10e..ecf4fd06 100644 --- a/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs +++ b/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs @@ -1281,5 +1281,150 @@ await Assert.ThrowsAsync(async () => } }); } + + [Fact] + public async Task DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingIDataReader() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); + var dateTime = DateTime.Now; + var onlyDate = DateOnly.FromDateTime(dateTime); + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(int)); + table.Columns.Add("Column3", typeof(DateTime)); + table.Columns.Add("Column4", typeof(DateOnly)); + table.Rows.Add("MiniExcel", 1, dateTime, onlyDate); + table.Rows.Add("Github", 2, dateTime, onlyDate); + } + + var configuration = new OpenXmlConfiguration + { + DynamicColumns = new[] + { + new DynamicExcelColumn("Column1") + { + Name = "Name of something", + Index = 0, + Width = 150 + }, + new DynamicExcelColumn("Column2") + { + Name = "Its value", + Index = 1, + Width = 150 + }, + new DynamicExcelColumn("Column3") + { + Name = "Its Date", + Index = 2, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss", + } + + } + }; + var reader = table.CreateDataReader(); + + await MiniExcel.SaveAsAsync(path, reader, configuration: configuration); + + using (var stream = File.OpenRead(path)) + { + var rows = stream.Query(useHeaderRow: true) + .Select(x => (IDictionary)x) + .ToList(); + + Assert.Contains("Name of something", rows[0]); + Assert.Contains("Its value", rows[0]); + Assert.Contains("Its Date", rows[0]); + Assert.Contains("Column4", rows[0]); + Assert.Contains("Name of something", rows[1]); + Assert.Contains("Its value", rows[1]); + Assert.Contains("Its Date", rows[1]); + Assert.Contains("Column4", rows[1]); + + Assert.Equal("MiniExcel", rows[0]["Name of something"]); + Assert.Equal(1D, rows[0]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[0]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[0]["Column4"]); + Assert.Equal("Github", rows[1]["Name of something"]); + Assert.Equal(2D, rows[1]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[1]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[1]["Column4"]); + } + } + + [Fact] + public async Task DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingDataTable() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); + var dateTime = DateTime.Now; + var onlyDate = DateOnly.FromDateTime(dateTime); + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(int)); + table.Columns.Add("Column3", typeof(DateTime)); + table.Columns.Add("Column4", typeof(DateOnly)); + table.Rows.Add("MiniExcel", 1, dateTime, onlyDate); + table.Rows.Add("Github", 2, dateTime, onlyDate); + } + + var configuration = new OpenXmlConfiguration + { + DynamicColumns = new[] + { + new DynamicExcelColumn("Column1") + { + Name = "Name of something", + Index = 0, + Width = 150 + }, + new DynamicExcelColumn("Column2") + { + Name = "Its value", + Index = 1, + Width = 150 + }, + new DynamicExcelColumn("Column3") + { + Name = "Its Date", + Index = 2, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss" + } + } + }; + + await MiniExcel.SaveAsAsync(path, table, configuration: configuration); + + using (var stream = File.OpenRead(path)) + { + var rows = stream.Query(useHeaderRow: true) + .Select(x => (IDictionary)x) + .Select(x => (IDictionary)x) + .ToList(); + + Assert.Contains("Name of something", rows[0]); + Assert.Contains("Its value", rows[0]); + Assert.Contains("Its Date", rows[0]); + Assert.Contains("Column4", rows[0]); + Assert.Contains("Name of something", rows[1]); + Assert.Contains("Its value", rows[1]); + Assert.Contains("Its Date", rows[1]); + Assert.Contains("Column4", rows[1]); + + + Assert.Equal("MiniExcel", rows[0]["Name of something"]); + Assert.Equal(1D, rows[0]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[0]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[0]["Column4"]); + Assert.Equal("Github", rows[1]["Name of something"]); + Assert.Equal(2D, rows[1]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[1]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[1]["Column4"]); + } + } + } } \ No newline at end of file diff --git a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs index 1aa967e2..5af73bb3 100644 --- a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs +++ b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs @@ -1217,12 +1217,16 @@ public void SharedStringNoCacheTest() public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingIDataReader() { var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); + var dateTime = DateTime.Now; + var onlyDate = DateOnly.FromDateTime(dateTime); var table = new DataTable(); { table.Columns.Add("Column1", typeof(string)); table.Columns.Add("Column2", typeof(int)); - table.Rows.Add("MiniExcel", 1); - table.Rows.Add("Github", 2); + table.Columns.Add("Column3", typeof(DateTime)); + table.Columns.Add("Column4", typeof(DateOnly)); + table.Rows.Add("MiniExcel", 1, dateTime, onlyDate); + table.Rows.Add("Github", 2, dateTime, onlyDate); } var configuration = new OpenXmlConfiguration @@ -1240,7 +1244,15 @@ public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingIDataReader() Name = "Its value", Index = 1, Width = 150 + }, + new DynamicExcelColumn("Column3") + { + Name = "Its Date", + Index = 2, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss", } + } }; var reader = table.CreateDataReader(); @@ -1255,13 +1267,21 @@ public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingIDataReader() Assert.Contains("Name of something", rows[0]); Assert.Contains("Its value", rows[0]); + Assert.Contains("Its Date", rows[0]); + Assert.Contains("Column4", rows[0]); Assert.Contains("Name of something", rows[1]); Assert.Contains("Its value", rows[1]); + Assert.Contains("Its Date", rows[1]); + Assert.Contains("Column4", rows[1]); Assert.Equal("MiniExcel", rows[0]["Name of something"]); Assert.Equal(1D, rows[0]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[0]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[0]["Column4"]); Assert.Equal("Github", rows[1]["Name of something"]); Assert.Equal(2D, rows[1]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[1]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[1]["Column4"]); } } @@ -1269,12 +1289,16 @@ public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingIDataReader() public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingDataTable() { var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); + var dateTime = DateTime.Now; + var onlyDate = DateOnly.FromDateTime(dateTime); var table = new DataTable(); { table.Columns.Add("Column1", typeof(string)); table.Columns.Add("Column2", typeof(int)); - table.Rows.Add("MiniExcel", 1); - table.Rows.Add("Github", 2); + table.Columns.Add("Column3", typeof(DateTime)); + table.Columns.Add("Column4", typeof(DateOnly)); + table.Rows.Add("MiniExcel", 1, dateTime, onlyDate); + table.Rows.Add("Github", 2, dateTime, onlyDate); } var configuration = new OpenXmlConfiguration @@ -1292,6 +1316,13 @@ public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingDataTable() Name = "Its value", Index = 1, Width = 150 + }, + new DynamicExcelColumn("Column3") + { + Name = "Its Date", + Index = 2, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss" } } }; @@ -1307,14 +1338,22 @@ public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingDataTable() Assert.Contains("Name of something", rows[0]); Assert.Contains("Its value", rows[0]); + Assert.Contains("Its Date", rows[0]); + Assert.Contains("Column4", rows[0]); Assert.Contains("Name of something", rows[1]); Assert.Contains("Its value", rows[1]); + Assert.Contains("Its Date", rows[1]); + Assert.Contains("Column4", rows[1]); Assert.Equal("MiniExcel", rows[0]["Name of something"]); Assert.Equal(1D, rows[0]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[0]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[0]["Column4"]); Assert.Equal("Github", rows[1]["Name of something"]); Assert.Equal(2D, rows[1]["Its value"]); + Assert.Equal(dateTime, (DateTime)rows[1]["Its Date"], TimeSpan.FromMilliseconds(10d)); + Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[1]["Column4"]); } } }