0

Consider the following input to a program:

  • time = 18:00
  • timezone = Europe/London (or any sensible representation of a timezone, possible examples include Europe__London, Europe.London, etc ... see note)
  • the date, which can be obtained by stripping out the date fields from chrono::Utc::now().

Is it possible to combine these data, or something similar, into a chrono::Datetime object?

Note: that the particular form of the string is not particularly important, and can be changed. This data just comes from a text file.

I think there may be two possible approaches.

  • Construct a string representation in rfc3339 format using format!, then parse this using the datetime parse_from_rfc3339 function. This seems like the wrong approach - creating bigger strings and then parsing them feels like an inefficient or "clumsy" approach. But it seems relatively easy to do.
  • The second option I am less confident on. I would guess there is a way to operate on some chrono types directly to create a datetime from each of these 3 pieces. But as for this option, I am somewhat lost and have no real idea how to approach the problem this way.

My attempts so far:


Option 1: Process strings

A chrono::DateTime object can be constructed using a string in rfc3339 format. Here is how that might be done:

let datetime_rfc3339_format_string =
    format!("{}T{}-{}",
        date_string, time_string, timezone_string);

let datetime_remote = chrono::DateTime::parse_from_rfc3339(datetime_remote_rfc3339_format);

The question then becomes how to build those three components in String format.

Timezone String

I previously managed to come up with a way of constructing a chrono_tz::Tz object from a string. See the following linked question.

let chrono_timezone = Tz::from_str(timezone.as_str())
    .expect("failed to convert string to chrono tz");

This works, assuming a string in the format of something like:

Europe/London

But in order to use something in rfc3339 format, we need to convert this "timezone name" into an "offset in number of hours". That depends both on the global datelines as well as local rules for things such as daylight savings.

So here I become stuck. I don't know how to convert a Tz object into an offset in numbers of hours, which in principle would depend on the current date.

Date String

This part is relatively easy. We can build a DateTime object from Utc::now() and extract the date using date_naive(). This can then be formatted as a String.

let datetime = chrono::Utc::now();
let date = datetime.date_naive();
let string_date = format!("{}", date);

Time String

I already have these in the required (String) format. For example, I can read these from a text file, and they are in the format of, for example:

08:00

Option 2:

Since I already have a timezone object, my thoughts were that it might be possible to use this to construct a DateTime object directly. It seems like the FixedOffset type has a with_ymd_and_hms function.

This suggests that somehow DateTime objects can be constructed from Timezone objects with this additional information, which I have access to.

But, this is where I get stuck. I do not know how to proceed here, or even if FixedOffset is the type I want to be working with. I don't know how it is related to Tz, if at all.

let chrono_timezone = Tz::from_str(timezone.as_str())
    .expect("failed to convert string to chrono tz");
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225

1 Answers1

0

This answer demonstrates a solution for Option 1:

Using the information presented in this question, we can solve the problem of how to convert a timezone into an offset, which can then be converted to the required String format.

fn get_todays_offset(tz: chrono_tz::Tz) -> chrono::Duration {

    let now = chrono::Utc::now().naive_utc();
    let offset = tz.offset_from_utc_datetime(&now);
    offset.base_utc_offset() + offset.dst_offset()
}

fn get_todays_offset_as_hh_mm(tz: chrono_tz::Tz) -> String {

    let todays_offset = get_todays_offset(tz);
    
    let todays_offset_sign = todays_offset.num_nanoseconds().unwrap().signum();
    let todays_offset_hours = todays_offset.num_hours();
    let todays_offset_minutes = todays_offset.num_minutes() % 60;

    let mut todays_offset_hh_mm_string = format!("{:02}:{:02}", todays_offset_hours, todays_offset_minutes);
    if todays_offset_sign >= 0 {
        todays_offset_hh_mm_string = format!("+{}", todays_offset_hh_mm_string);
    }
    else {
        todays_offset_hh_mm_string = format!("-{}", todays_offset_hh_mm_string);
    }
    
    return todays_offset_hh_mm_string;
}

From this, we can then construct the rfc3339 String.

let timezone_string = get_todays_offset_as_hh_mm(tz);

let datetime_rfc3339_format =
    format!("{}T{}-{}",
        date_string, time_string, timezone_string);

let datetime =
    chrono::DateTime::parse_from_rfc3339(
        datetime_remote_rfc3339_format.as_str())
            .expect("rfc3339 parse failed");
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225