chrono icon indicating copy to clipboard operation
chrono copied to clipboard

Support parsing dates with missing fields

Open Diggsey opened this issue 8 years ago • 7 comments
trafficstars

This example is documented as failing when parsing a NaiveDate:

assert!(parse_from_str("2015/9", "%Y/%m").is_err());

However, it would be more useful if unspecified fields defaulted to the lowest valid value. This is how the python date/time parsing works.

Alternatively, the API could provide a mechanism to supply "missing fields" separately from the string being parsed. It would be sufficient for this to work only for "prefixes", eg. "%Y", "%Y-%m", "%Y-%m-%d", and similarly for times.

Diggsey avatar Oct 18 '17 13:10 Diggsey

I think that I prefer the idea of requiring missing fields to be supplied in some way to get around accidentally typoing the format string. We could make it a bit easier if it's expected by supplying a "default" default-supplier.

Have you seen an API that looks good for a supplier? The first thing that comes to mind is some sort of builder like, for your example:

// okay, ends up on the fifth:
Date::parse_defaults().day(5).parse_from_str("2015/9", "%Y/%m");
// error, day is what's missing:
Date::parse_defaults().month(1).parse_from_str("2015/9", "%Y/%m");
// okay, same result as the first example:
Date::parse_defaults().month(1).day(5).parse_from_str("2015/9", "%Y/%m");
// okay, uses first day of month:
Date::parse_defaults().first().parse_from_str("2015/9", "%Y/%m");

Is this the kind of thing you're imagining? Have you seen something better?

quodlibetor avatar Oct 20 '17 03:10 quodlibetor

One possibility that comes to mind would be to add a new "DateTimeItems" type which just stores the parsed items but doesn't attempt to convert to a valid date (yet). Usage would look like:

let result: ParseResult<Date> = DateTimeItems::parse_from_str("2015/9", "%Y/%m").with_day(1).into();

Diggsey avatar Oct 20 '17 09:10 Diggsey

Hmm actually yeah that would result in probably a much nicer API all around if we stuff DT Items in the Err variant (or something), then we don't need any new functions and can provide better error messages, and I think that the current code would support that without the kinds of crazy refactor I was scared of for my idea.

That would mean maybe some sort of new trait on ParseResults allowing just something like parse_from_str("..", "%Y/%m").with_day(1).

quodlibetor avatar Oct 25 '17 13:10 quodlibetor

Hey, @quodlibetor. I see there hasn't been much progress on this issue since it was created. Is there any way I can help? This is a feature I need in a personal project and would be willing to help implement it :smiley:

bernardobelchior avatar Apr 06 '19 23:04 bernardobelchior

@bernardobelchior PRs around this would be very welcome!

If you would like to take a look at the parsing code and come up with a bit of a proposal I could definitely guide you. You can feel free to ping me on gitter if you want to talk about it. My overall feeling about this is that we should implement a new trait for better dt parsing that we can incorporate in the 0.4.x series and replace the existing methods with in 0.5.x.

quodlibetor avatar Apr 07 '19 21:04 quodlibetor

Hi. Is there any way to parse partial dates in the current version? Or maybe there is external crate for this? I need to parse dates like "1956-01" which are in the csv files. The problem is that different csv uses different format for datetime. That's why I would like to pass format string to the parsing function. Any tips are appreciated :-)

klangner avatar Oct 13 '20 14:10 klangner

I have a workaround way to handle it.

use chrono::format::{parse, Parsed, StrftimeItems};

let datetime = "2015/9";
let fmt = "%Y/%m";

let mut parsed = Parsed::new();
parse(&mut parsed, datetime, StrftimeItems::new(fmt)).unwrap();

// set default values
parsed.year = parsed.year.or(Some(0));
parsed.month = parsed.month.or(Some(1));
parsed.day = parsed.day.or(Some(1));
if parsed.hour_div_12.is_none() {
    parsed.set_hour(0).unwrap();
}
parsed.minute = parsed.minute.or(Some(0));
parsed.second = parsed.second.or(Some(0));
parsed.nanosecond = parsed.nanosecond.or(Some(0));
parsed.offset = parsed.offset.or(Some(0));

let datetime = parsed.to_datetime().unwrap()
println!("{datetime}");   //  2015-09-01 00:00:00 +00:00

zen-xu avatar Mar 27 '22 07:03 zen-xu