Dates and times
Dates stay text until you ask for them, because 03/04/2024 means two different days depending on where the file came from.
Install#
dart pub add csv_plusimport 'package:csv_plus/csv_plus.dart';Turning it on#
final codec = CsvCodec(const CsvConfig(parseDates: true));
codec.decode('when,who\n2024-01-31,Alice');
// [[when, who], [DateTime(2024, 1, 31), Alice]]It applies everywhere inference already applies: decode, decodeToTable, decodeToMaps, the streaming CsvDecoder, and bindBytes.
What is accepted#
| Text | Result |
|---|---|
2024-01-31 | local midnight |
2024-01-31T09:30:00 | local date and time |
2024-01-31 09:30:00 | a space works as the separator |
2024-01-31T09:30:00.123 | fractional seconds kept |
2024-01-31T09:30:00Z | UTC |
2024-01-31T09:30:00+05:30 | offset applied, UTC returned |
A value has to start with YYYY-MM-DD. A value with no offset reads as local time, one with an offset reads as UTC.
What stays text#
codec.decode('a,b,c,d\n03/04/2024,2024-13-45,20240131,"2024-01-31"');
// ['03/04/2024', '2024-13-45', 20240131, '2024-01-31']Ambiguous locale formats are left alone. So are unpunctuated runs such as 20240131, which are far more likely to be identifiers than dates, and quoted fields, which always opt out of inference.
An impossible date stays a string#
2024-13-45 is the case worth knowing about. DateTime.parse accepts it and quietly rolls it over to 14 February 2025, so a typo in a source file becomes a real date that is simply wrong.
DateTime.parse('2024-13-45'); // 2025-02-14 (!)
FastDecoder.tryParseIsoDateTime('2024-13-45'); // nullcsv_plus range-checks the year, month, day, hour, minute and second before parsing, and honours leap years. 2024-02-29 parses; 2023-02-29 does not.
Other date formats#
For anything that is not ISO 8601, convert the column yourself with a decoderTransform. It runs on every data cell and receives the column header, so you can target one column.
final codec = CsvCodec(CsvConfig(
hasHeader: true,
decoderTransform: (value, index, header) {
if (header != 'when' || value is! String) return value;
final parts = value.split('/'); // 03/04/2024, day first
if (parts.length != 3) return value;
return DateTime(
int.parse(parts[2]),
int.parse(parts[1]),
int.parse(parts[0]),
);
},
));Writing dates back#
A DateTime encodes to a form that decodes to the same value, in both local and UTC, so a decode and encode round trip is lossless.