rustrust-chrono

Converting from NaiveDateTime to DateTime<Local>


Rust's chrono has been very frustrating to work with as it makes converting from timezones very difficult.

For example: my user inputs a string. I parse this into a naive date time using NaiveDateTime::parse_from_str. Now I would like to convert this into a DateTime<Local>.

Unfortunately I can't seem to find out how to do so. Using Local::From doesn't work. Using DateTime<Local>::from() doesn't work either. Neither structs have methods to convert from a NaiveDateTime, and NaiveDateTime doesn't have methods to convert into Local.

Yet, we can do things like this: someLocalDateTime.date().and_time(some_naive_time). So why can't we just do Local::new(some_naive_date_time)?

Additionally, why can't we skip fields in the parsing? I don't need seconds and I don't need year. In order to assume the current year and 0 seconds, I have to manually write parsing code and construct the datetime from ymd hms.


Solution

  • This functionality is provided by the chrono::offset::TimeZone trait. Specifically, the method TimeZone::from_local_datetime is almost precisely what you're looking for.

    use chrono::{offset::TimeZone, DateTime, Local, NaiveDateTime};
    
    fn main() {
        let naive = NaiveDateTime::parse_from_str("2020-11-12T5:52:46", "%Y-%m-%dT%H:%M:%S").unwrap();
        let date_time: DateTime<Local> = Local.from_local_datetime(&naive).unwrap();
        println!("{:?}", date_time);
    }
    

    (playground)


    As for the other question about parsing with assumptions, I'm not sure if the tools exist yet. It would be cool if ParseResult allowed you to manually set specific values before unwrapping (or what have you) the result.

    One idea that lets you still use chrono's parser is to manually add the extra fields to the parse string.

    For example:

    use chrono::{offset::TimeZone, DateTime, Datelike, Local, NaiveDateTime};
    
    fn main() {
        let time_string = "11-12T5:52"; // no year or seconds
        let current_year = Local::now().year();
        let modified_time_string = format!("{}&{}:{}", time_string, current_year, 0);
    
        let naive = NaiveDateTime::parse_from_str(&modified_time_string, "%m-%dT%H:%M&%Y:%S").unwrap();
        let date_time: DateTime<Local> = Local.from_local_datetime(&naive).unwrap();
        println!("{:?}", date_time); // prints (as of 2020) 2020-11-12T05:52:00+00:00
    }
    

    (playground)