How to subtract two Chrono DateTime objects to obtain the Duration difference between them, when the DateTime types differ?

426 Views Asked by At

I have two DateTime objects of different type which I would like to be able to subtract, or otherwise find the Duration difference between.

Here is some example code:

// First step: Want to be able to construct DateTimes for a set of different
// timezones.
//
// To do this, we first create a Utc now object, and convert it to different
// timezones, as required. For example here, we convert to London.
let utc_now = Utc::now();
let naive_utc_datetime = utc_now.naive_local();
    // Since this time is in Utc, `naive_local` should return the same as
    // `naive_utc` ?

// Create London time from a chrono_tz Timezone
let london_now = chrono_tz::Europe::London.from_utc_datetime(&naive_utc_datetime);
println!("london_now: {}", london_now);
    // london_now: 2023-08-31 10:07:43.421198168 BST

let difference_from_utc = london_now - utc_now;
println!("difference_from_utc: {}", difference_from_utc);
    // does not compile

The final line does not compile, this is why:

cannot subtract `DateTime<Utc>` from `DateTime<Tz>`
the trait `Sub<DateTime<Utc>>` is not implemented for `DateTime<Tz>`
the following other types implement trait `Sub<Rhs>`:
  <DateTime<Tz> as Sub<Days>>
  <DateTime<Tz> as Sub<FixedOffset>>
  <DateTime<Tz> as Sub<Months>>
  <DateTime<Tz> as Sub<chrono::Duration>>
  <DateTime<Tz> as Sub>

The question is, how can we obtain the Duration (or otherwise) difference between the time in London and the Utc time. Since it is currently British Summer Time, I would expect to see a difference of exactly 1 hour on todays date. (2023-08-31)


Related: The following question explains how to subtract two DateTime::<T> objects assuming they have the same type T.

This will work if both datetimes are DateTime::<Utc> but not otherwise.

2

There are 2 best solutions below

0
FreelanceConsultant On

One way is to convert the current London Datetime, which is some kind of DateTime<FixedOffset> type into a DateTime<Utc> object.

println!("london_now.naive_local(): {}", london_now.naive_local());
    // 2023-08-31 10:07:43.421198168

// force it into a type which can be subtracted
// this is perhaps a misnomer, because this time IS NOT a utc time
let london_now_utc = Utc.from_local_datetime(&london_now.naive_local()).unwrap();

let difference_from_utc = london_now_utc - utc_now;
println!("difference_from_utc: {}", difference_from_utc); // PT3600S

However, this example only works for subtracting two Utc times. If we wanted to subtract two DateTime objects for a London and Chicago time, this method would suggest both have to be converted to UTC first, which seems inefficient.


I thought it may be possible to subtract two DateTime<FixedOffset> types, but this does not produce the expected result.

For example:

let london_now = chrono_tz::Europe::London.from_utc_datetime(&naive_utc_datetime);
println!("london_now: {}", london_now);
    // london_now: 2023-08-31 10:21:36.642965010 BST

let chicago_now = chrono_tz::America::Chicago.from_utc_datetime(&naive_utc_datetime);
println!("chicago_now: {}", chicago_now);
    // chicago_now: 2023-08-31 04:21:36.642965010 CDT

println!("difference Chicago - London: {}", chicago_now - london_now);

I can sort of imagine why the difference is zero. Both "times" are in different "timezones". When we compute the difference between them we find that although the displayed time is very different, the difference in time is still zero.

6
Chayim Friedman On

You can convert the datetimes to NaiveDateTime:

let difference_from_utc = london_now.naive_local() - utc_now.naive_local();

For UTC, it is faster to use naive_utc():

let difference_from_utc = london_now.naive_local() - utc_now.naive_utc();