0

I need to read a CSV file using CsvReader, however I have a custom date format (yyyyMMdd) which by default can't be converted to DateOnly. How can I specify this custom date format? Is that may possible using some annotation?

This is the code I currently have, it fails with

FormatException: String '20230817' was not recognized as a valid DateOnly.

class Foo
{
    public string Title { get; set; } = null!;
    public DateOnly Date { get; set; }
}

var sb = new StringBuilder();
sb.Append(@"Title,Date");
sb.AppendLine();
sb.Append(@"hello world,20230817");
sb.AppendLine();


using var stringReader = new StringReader(sb.ToString());
using var csvReader = new CsvReader(stringReader, CultureInfo.InvariantCulture);

int lineIndex = 0;

while(csvReader.Read())
{
    if(lineIndex == 0)
    {
        csvReader.ReadHeader();
    } else
    {
        var foo = csvReader.GetRecord<Foo>();
    }

    lineIndex++;
}
stefan.at.kotlin
  • 15,347
  • 38
  • 147
  • 270
  • I doubt it would be different than the classic `DateTime`, does this help? https://stackoverflow.com/questions/39564585 – Rand Random Aug 17 '23 at 13:11
  • btw - I hope that `yyymmdd` is a typo, as the small `m` is minutes and not month, and only 3 `y` instead of 4 – Rand Random Aug 17 '23 at 13:14
  • 3
    seems to work: https://dotnetfiddle.net/gqg1iS – Rand Random Aug 17 '23 at 13:16
  • thank you and all the others, perfect answers :-) now I don't know which to select besides you only having commented, would also have been worth an answer :-) And yeah, should have been `yyyyMMdd` – stefan.at.kotlin Aug 17 '23 at 15:04
  • IMHO no one should have answered and it should be flagged as duplicate, as I said in the beginning there seems to be no reason to believe it wouldn’t work exactly like DateTime – Rand Random Aug 17 '23 at 15:12

2 Answers2

2

As Rand Random commented, you have two ways to add the formatting.

You can simply add the Format annotation

class Foo
{
    public string Title { get; set; } = null!;
    
    [Format("yyyyMMdd")]
    public DateOnly Date { get; set; }
}

Or you can set all DateOnly values to use the format with TypeConverterOptionsCache

var sb = new StringBuilder();
sb.AppendLine(@"Title,Date");
sb.AppendLine(@"hello world,20230817");

using var stringReader = new StringReader(sb.ToString());
using var csvReader = new CsvReader(stringReader, CultureInfo.InvariantCulture);

var options = new TypeConverterOptions { Formats = new[] { "yyyyMMdd" } };
csvReader.Context.TypeConverterOptionsCache.AddOptions<DateOnly>(options);

if (csvReader.Read())
{
    csvReader.ReadHeader();

    while (csvReader.Read())
    {
        var foo = csvReader.GetRecord<Foo>();
    }
}
David Specht
  • 7,784
  • 1
  • 22
  • 30
0

Based on your example you can try something like this, since you have to specify the exact format you provide

StringBuilder sb = new StringBuilder();
sb.Append(@"Title,Date");
sb.AppendLine();
sb.Append(@"hello world,20230817");
sb.AppendLine();
using var stringReader = new StringReader(sb.ToString());

using var csvReader = new CsvReader(stringReader, CultureInfo.InvariantCulture);

int lineIndex = 0;
while (csvReader.Read())
{
    if (lineIndex == 0)
    {
        csvReader.ReadHeader();
    }
    else
    {
        var foo = new Foo() { Title = csvReader.GetField<string>(0), Date = DateOnly.ParseExact(csvReader.GetField<string>(1), "yyyyMMdd") };
    }
    lineIndex++;
}
Izanagi
  • 199
  • 1
  • 2
  • 10