Custom DateTime format (#616)

* - goto/jumpmark

* + generate numberformats

* + `FormatId`

* ~ first working shot

* ~ assign responsibilities correctly

* ~ fix last test issues

* + extend tests

* ~ clean up

* ~ simplify `DateOnly` handling

* + `DateOnly` to Tests

* Update ExcelOpenXmlSheetWriter.cs

Update GenerateSheetByIDataReader

* + datetime format for async part

---------

Co-authored-by: Gary Jia <35099424+jiaguangli@users.noreply.github.com>
This commit is contained in:
DancePanda42 2024-06-13 15:26:26 +02:00 committed by GitHub
parent 12bb1c0028
commit 00a445c6bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 340 additions and 99 deletions

View File

@ -9,6 +9,8 @@ namespace MiniExcelLibs.Attributes
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 @@ namespace MiniExcelLibs.Attributes
public class DynamicExcelColumn : ExcelColumnAttribute
{
public string Key { get; set; }
public DynamicExcelColumn(string key)
{
Key = key;

View File

@ -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 @@ namespace MiniExcelLibs.OpenXml.Constants
</x:cellXfs>
</x:styleSheet>";
internal static readonly string DefaultStylesXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
#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 = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<x:styleSheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">
<x:numFmts count=""1"">
<x:numFmts count=""{NumFmtsCountToken}"">
<x:numFmt numFmtId=""0"" formatCode="""" />
{NumFmtsToken}
</x:numFmts>
<x:fonts count=""2"">
<x:font>
@ -133,7 +149,7 @@ namespace MiniExcelLibs.OpenXml.Constants
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
</x:cellStyleXfs>
<x:cellXfs count=""4"">
<x:cellXfs count=""{cellXfsCountToken}"">
<x:xf></x:xf>
<x:xf numFmtId=""0"" fontId=""1"" fillId=""2"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""0"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""left"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
@ -150,12 +166,15 @@ namespace MiniExcelLibs.OpenXml.Constants
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyBorder=""1"" applyAlignment=""1"">
<x:alignment horizontal=""fill""/>
</x:xf>
{cellXfsToken}
</x:cellXfs>
<x:cellStyles count=""1"">
<x:cellStyle name=""Normal"" xfId=""0"" builtinId=""0"" />
</x:cellStyles>
</x:styleSheet>";
#endregion
internal static readonly string DefaultWorkbookXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
<x:workbook xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships""
xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">
@ -231,5 +250,52 @@ namespace MiniExcelLibs.OpenXml.Constants
internal static string Sheet(SheetDto sheetDto, int sheetId)
=> $@"<x:sheet name=""{ExcelOpenXmlUtils.EncodeXML(sheetDto.Name)}"" sheetId=""{sheetId}""{(string.IsNullOrWhiteSpace(sheetDto.State) ? string.Empty : $" state=\"{sheetDto.State}\"")} r:id=""{sheetDto.ID}"" />";
internal static string SetupStyleXml(string styleXml, ICollection<ExcelColumnAttribute> columns)
{
const int numFmtIndex = 166;
var sb = new StringBuilder(styleXml);
var columnsToApply = GenerateStyleIds(columns);
var numFmts = columnsToApply.Select((x, i) =>
{
return new
{
numFmt =
$@"<x:numFmt numFmtId=""{numFmtIndex + i}"" formatCode=""{x.Format}"" />",
cellXfs =
$@"<x:xf numFmtId=""{numFmtIndex + i}"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""general"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>"
};
}).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<ExcelColumnAttribute> GenerateStyleIds(ICollection<ExcelColumnAttribute> 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++;
}
}
}
}

View File

@ -36,6 +36,7 @@ namespace MiniExcelLibs.OpenXml
{
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 @@ namespace MiniExcelLibs.OpenXml
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 @@ namespace MiniExcelLibs.OpenXml
{
await AddFilesToZipAsync(cancellationToken);
await GenerateStylesXmlAsync(cancellationToken);
await GenerateDrawinRelXmlAsync(cancellationToken);
await GenerateDrawingXmlAsync(cancellationToken);
@ -479,7 +478,7 @@ namespace MiniExcelLibs.OpenXml
/// </summary>
private async Task GenerateStylesXmlAsync(CancellationToken cancellationToken)
{
var styleXml = GetStylesXml();
var styleXml = GetStylesXml(_configuration.DynamicColumns);
await CreateZipEntryAsync(
ExcelFileNames.Styles,

View File

@ -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 @@ namespace MiniExcelLibs.OpenXml
if (dynamicColumn.Format != null)
{
prop.ExcelFormat = dynamicColumn.Format;
prop.ExcelFormatId = dynamicColumn.FormatId;
}
if (dynamicColumn.Aliases != null)
@ -158,14 +160,26 @@ namespace MiniExcelLibs.OpenXml
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 @@ namespace MiniExcelLibs.OpenXml
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<string, string, string>("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 @@ namespace MiniExcelLibs.OpenXml
return base64;
}
private Tuple<string, string, string> GetDateTimeValue(object value, ExcelColumnInfo columnInfo)
private Tuple<string, string, string> 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<string, string, string>("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 @@ namespace MiniExcelLibs.OpenXml
return dimensionRef;
}
private string GetStylesXml()
private string GetStylesXml(ICollection<ExcelColumnAttribute> 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;
}

View File

@ -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 @@ namespace MiniExcelLibs.OpenXml
{
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 @@ namespace MiniExcelLibs.OpenXml
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 @@ namespace MiniExcelLibs.OpenXml
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 @@ namespace MiniExcelLibs.OpenXml
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 @@ namespace MiniExcelLibs.OpenXml
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 @@ namespace MiniExcelLibs.OpenXml
{
AddFilesToZip();
GenerateStylesXml();
GenerateDrawinRelXml();
GenerateDrawingXml();
@ -508,7 +508,7 @@ namespace MiniExcelLibs.OpenXml
/// </summary>
private void GenerateStylesXml()
{
var styleXml = GetStylesXml();
var styleXml = GetStylesXml(_configuration.DynamicColumns);
CreateZipEntry(ExcelFileNames.Styles, ExcelContentTypes.Styles, styleXml);
}
@ -600,4 +600,4 @@ namespace MiniExcelLibs.OpenXml
}
}
}
}
}

View File

@ -23,6 +23,7 @@
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 @@
ExcelIndexName = p.GetAttribute<ExcelColumnIndexAttribute>()?.ExcelXName ?? excelColumn?.IndexName,
ExcelColumnWidth = p.GetAttribute<ExcelColumnWidthAttribute>()?.ExcelColumnWidth ?? excelColumn?.Width,
ExcelFormat = excelFormat ?? excelColumn?.Format,
ExcelFormatId = excelColumn?.FormatId ?? -1
};
}).Where(_ => _ != null);
}
@ -292,7 +294,10 @@
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)

View File

@ -309,7 +309,7 @@ namespace MiniExcelLibs.Tests
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 @@ namespace MiniExcelLibs.Tests
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);

View File

@ -1281,5 +1281,150 @@ namespace MiniExcelLibs.Tests
}
});
}
[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<string, object>)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<string, object>)x)
.Select(x => (IDictionary<string, object>)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"]);
}
}
}
}

View File

@ -1217,12 +1217,16 @@ namespace MiniExcelLibs.Tests
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 @@ namespace MiniExcelLibs.Tests
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 @@ namespace MiniExcelLibs.Tests
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 @@ namespace MiniExcelLibs.Tests
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 @@ namespace MiniExcelLibs.Tests
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 @@ namespace MiniExcelLibs.Tests
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"]);
}
}
}