Skip to content

Commit

Permalink
support for mapping datetime fields
Browse files Browse the repository at this point in the history
  • Loading branch information
EliotJones committed Apr 8, 2022
1 parent 89b445f commit 8514830
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 3 deletions.
100 changes: 99 additions & 1 deletion src/CsvSwan.Tests/MapTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace CsvSwan.Tests
using System;
using System.Collections.Generic;

namespace CsvSwan.Tests
{
using System.Linq;
using Xunit;
Expand Down Expand Up @@ -168,6 +171,83 @@ public void CanMapWithNullableIntFromColumnHeaders()
}
}

[Fact]
public void CanMapWithNonNullableIntFromColumnHeadersUsingDefaultOption()
{
const string input = @"name,price,time,valid
Jim,5,100,true
Jane,,12,false
Bob,3,7,";

using (var csv = Csv.FromString(input, hasHeaderRow: true))
{
var values = csv.Map<MyClassUnmappedNonNullable>().ToList();

Assert.Equal(3, values.Count);

Assert.Equal("Jim", values[0].Name);
Assert.Equal(5, values[0].Price);
Assert.Equal(100, values[0].Time);
Assert.True(values[0].Valid);

Assert.Equal("Jane", values[1].Name);
Assert.Equal(0, values[1].Price);
Assert.Equal(12, values[1].Time);
Assert.False(values[1].Valid);

Assert.Equal("Bob", values[2].Name);
Assert.Equal(3, values[2].Price);
Assert.Equal(7, values[2].Time);
Assert.False(values[2].Valid);
}
}

[Fact]
public void CanMapWithNonNullableIntFromColumnHeadersUsingThrowOption()
{
const string input = @"name,price,time,valid
Jim,5,100,true
Jane,,12,false
Bob,3,7,";

using (var csv = Csv.FromString(input, new CsvOptions
{
DefaultNullValues = false,
HasHeaderRow = true
}))
{
// ReSharper disable once AccessToDisposedClosure
Func<List<MyClassUnmappedNonNullable>> func = () => csv.Map<MyClassUnmappedNonNullable>().ToList();

Assert.Throws<InvalidOperationException>(func);
}
}

[Fact]
public void CanMapWithDateTime()
{
const string input = @"key,created,identifier
any-key,2022-05-16,3873764
other-key,2020-12-05,457473
my-key,,473737";

using (var csv = Csv.FromString(input, hasHeaderRow: true))
{
var values = csv.Map<MyClassUnmappedDateTime>().ToList();

Assert.Equal(3, values.Count);

Assert.Equal("any-key", values[0].Key);
Assert.Equal(new DateTime(2022, 5, 16), values[0].Created);

Assert.Equal("other-key", values[1].Key);
Assert.Equal(new DateTime(2020, 12, 5), values[1].Created);

Assert.Equal("my-key", values[2].Key);
Assert.Equal(DateTime.MinValue, values[2].Created);
}
}

public class MyClassAllMapped
{
[CsvColumnOrder(0)]
Expand Down Expand Up @@ -210,5 +290,23 @@ public class MyClassUnmappedNullable

public bool? Valid { get; set; }
}

public class MyClassUnmappedNonNullable
{
public string Name { get; set; }

public int Price { get; set; }

public int Time { get; set; }

public bool Valid { get; set; }
}

public class MyClassUnmappedDateTime
{
public string Key { get; set; }

public DateTime Created { get; set; }
}
}
}
44 changes: 42 additions & 2 deletions src/CsvSwan/Csv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,6 @@ public float GetFloat(int index, IFormatProvider formatProvider = null)
/// </summary>
public bool GetBool(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);

var nullable = GetNullableBool(index, formatProvider);

if (nullable.HasValue)
Expand Down Expand Up @@ -496,6 +494,48 @@ public bool GetBool(int index, IFormatProvider formatProvider = null)
return null;
}

/// <summary>
/// Gets the value from the column at the given index as a <see cref="DateTime"/>.
/// </summary>
public DateTime GetDateTime(int index, IFormatProvider formatProvider = null)
{
var value = GetNullableDateTime(index, formatProvider);

if (value.HasValue)
{
return value.Value;
}

if (options.DefaultNullValues)
{
return default;
}

throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
/// Gets the value from the column at the given index as a <see cref="DateTime"/>?.
/// </summary>
public DateTime? GetNullableDateTime(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);

var value = csv.currentValues[index];

if (string.IsNullOrEmpty(value))
{
return null;
}

if (!DateTime.TryParse(value, formatProvider, DateTimeStyles.None, out var dateTime))
{
return null;
}

return dateTime;
}

/// <summary>
/// Gets the <see langword="string" /> value from the column at the given index.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/CsvSwan/TypeMapFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ public object Map(Csv.RowAccessor row, IFormatProvider formatProvider)
{
value = row.GetNullableBool(setter.column, formatProvider);
}
else if (propertyType == typeof(DateTime))
{
value = row.GetDateTime(setter.column, formatProvider);
}
else if (propertyType == typeof(DateTime?))
{
value = row.GetNullableDateTime(setter.column, formatProvider);
}
else
{
throw new InvalidOperationException($"Cannot map to type {propertyType.FullName} on type {type.FullName}.");
Expand Down

0 comments on commit 8514830

Please sign in to comment.