mike chambers | about

Finding a specific time in the previous week using Rust

Saturday, November 28, 2020

I am working on a new Rust based Destiny 2 project (dcli) and needed to be able to calculate the last date / time that Destiny 2 had its weekly reset (every Tuesday at 1700 UTC). Finding the previous Tuesday wasn’t too difficult, but adding the specific time on Tuesday added some complexities and I wanted to share my solution in Rust using the Chrono library.

First, lets look at a quick solution to get the last previous day of week (in this case Tuesday):

use chrono::{DateTime, Utc, Duration};
use chrono::prelude::*;

pub fn get_last_tuesday() -> DateTime<Utc> {
    let now: DateTime<Utc> = Utc::now();
    let c:i64 = ((now.weekday().num_days_from_sunday() + 4) % 7 + 1) as i64;
    now - Duration::days(c)
}

Where the day of the week is the +1 (subtract one from the day you want from here). This uses modular arithmatic and the repeating nature of days of the week to figure out the previous specific day of week.

However, in my case, I needed to know the previous day AND a specific time. So if you are checking on a Tuesday and it is after Tuesday 1700 UTC, it would return the current Tuesday, but if it was before Tuesday 1700 UTC, it would return the previous Tuesday.

My first solution used a brute force approach:

pub fn get_last_reset() -> DateTime<Utc> {

    let now: DateTime<Utc> = Utc::now();
    //use this to test specific dates / times
    //let now : DateTime<Utc> = Utc.ymd(2020, 11, 24).and_hms(17, 0, 1);

    let n_date = now.date();

    //today's date with reset time
    let dt = Utc.ymd(n_date.year(), n_date.month(), n_date.day()).and_hms(17, 0, 0);

    let w_day = n_date.weekday();

    //see if we are on reset day (Tue)
    let target_dt = if w_day == Weekday::Tue {

        if now > dt {
            //after reset, so use today's reset time
            dt
        } else {

            //before reset, go back to previous tuesday, reset
            dt - Duration::days(7)
        }
    } else {

        //figure out how many days we are away from the previous tuesday
        let c:i64 = ((w_day.num_days_from_sunday() + 4) % 7 + 1) as i64;
        dt - Duration::days(c)
    };

    target_dt
}

This worked, but seemed more complicated than it should be.

I was chatting with some other developers on the Destiny 2 API Discord, and the idea of using a known reset date as a reference came up.

Basically, using a reference date, you could divide by a week, and use the remainder time as an offset. This led to what I think is a much more elegant solution:

use chrono::{DateTime, Utc, Duration};
use chrono::prelude::*;

pub const WEEK_IN_SECONDS: i64 = 604800;

pub fn get_last_reset() -> DateTime<Utc> {
    //get a hardcoded past reset date / time (17:00 UTC every tuesday)
    let past_reset : DateTime<Utc> = Utc.ymd(2020, 11, 10).and_hms(17, 0, 0);
    let now: DateTime<Utc> = Utc::now();

    //get total seconds between now and the past reset
    //take the mod of that divided by a week in seconds
    //subtract that amount from current date / time to find previous reset
    now - Duration::seconds((now - past_reset).num_seconds() % WEEK_IN_SECONDS)
}

Here is a more verbose version that steps through each step:

pub const WEEK_IN_SECONDS: i64 = 604800;

pub fn get_last_reset() -> DateTime<Utc> {

    //reference reset date / time
    let past_reset : DateTime<Utc> = Utc.ymd(2020, 11, 10).and_hms(17, 0, 0);

    //current time
    let now: DateTime<Utc> = Utc::now();

    //get Duration since reference reset
    let d = now - past_reset;

    // divide by a week, and get the remainder
    let rem = d.num_seconds() % WEEK_IN_SECONDS;

    //subtract the remainder from current time
    let target = now - Duration::seconds(rem);

    //last reset / datetime
    target
}

I haven’t tested this against leap year / seconds yet, but assuming the Chrono library handles them, this should work.

If you see any issues or improvements, ping me at @mesh on Twitter.

twitter github flickr behance